diff --git a/libs/pigment/KoColorSpace.cpp b/libs/pigment/KoColorSpace.cpp index 5198378459..23fd346d1c 100644 --- a/libs/pigment/KoColorSpace.cpp +++ b/libs/pigment/KoColorSpace.cpp @@ -1,831 +1,836 @@ /* * 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 #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. const KoColorSpace* xyzColorSpace = KoColorSpaceRegistry::instance()->colorSpace("XYZA", "F32"); 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(); } const KoColorSpace* xyzColorSpace = KoColorSpaceRegistry::instance()->colorSpace("XYZA", "F32"); quint8 *data = new quint8[pixelSize()]; quint8 *data2 = new quint8[xyzColorSpace->pixelSize()]; // This is fixed to 5 since the maximum number of channels are 5 for CMYKA QVector channelValuesF(5);//for getting the coordinates. d->colorants.resize(3*colorChannelCount()); const int segments = 10; for (quint32 i=0; idisplayPosition()] = ((max/segments)*(segments-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]; d->colorants[3*i] = channelValuesF[0]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]); d->colorants[3*i+1] = channelValuesF[1]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]); d->colorants[3*i+2] = channelValuesF[1]; } d->TRCXYY << QPointF(channelValuesF[1]/colorantY, ((1.0/segments)*(segments-j))); } } else { for (int j = 0; j <= segments; j++) { channelValuesF.fill(0.0); channelValuesF[i] = ((max/segments)*(j)); fromNormalisedChannelsValue(data, channelValuesF); convertPixelsTo(data, data2, xyzColorSpace, 1, KoColorConversionTransformation::IntentAbsoluteColorimetric, KoColorConversionTransformation::adjustmentConversionFlags()); xyzColorSpace->normalisedChannelsValue(data2,channelValuesF); if (j==0) { colorantY = channelValuesF[1]; d->colorants[3*i] = channelValuesF[0]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]); d->colorants[3*i+1] = channelValuesF[1]/(channelValuesF[0]+channelValuesF[1]+channelValuesF[2]); d->colorants[3*i+2] = channelValuesF[1]; } d->TRCXYY << QPointF(channelValuesF[1]/colorantY, ((1.0/segments)*(j))); } } } delete[] data; delete[] data2; return d->TRCXYY; } else { return d->TRCXYY; } } 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 { if (d->colorants.size() <= 0) { if (profile() && profile()->hasColorants()) { d->colorants.resize(3 * colorChannelCount()); d->colorants = profile()->getColorantsxyY(); } else { QPolygonF p = estimatedTRCXYY(); Q_UNUSED(p); } } 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 { // luma coefficients need to add up to 1.0 qreal sum = d->colorants[2] + d->colorants[5] + d->colorants[8]; d->lumaCoefficients[0] = d->colorants[2] / sum; d->lumaCoefficients[1] = d->colorants[5] / sum; d->lumaCoefficients[2] = d->colorants[8] / sum; } } 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()->createColorConverter(this, KoColorSpaceRegistry::instance()->lab16(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()) ; } return d->transfoToLABA16; } const KoColorConversionTransformation* KoColorSpace::fromLabA16Converter() const { if (!d->transfoFromLABA16) { d->transfoFromLABA16 = KoColorSpaceRegistry::instance()->createColorConverter(KoColorSpaceRegistry::instance()->lab16(), this, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()) ; } return d->transfoFromLABA16; } const KoColorConversionTransformation* KoColorSpace::toRgbA16Converter() const { if (!d->transfoToRGBA16) { d->transfoToRGBA16 = KoColorSpaceRegistry::instance()->createColorConverter(this, KoColorSpaceRegistry::instance()->rgb16(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()) ; } return d->transfoToRGBA16; } const KoColorConversionTransformation* KoColorSpace::fromRgbA16Converter() const { if (!d->transfoFromRGBA16) { d->transfoFromRGBA16 = KoColorSpaceRegistry::instance()->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()->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; } KoColorConversionTransformation * KoColorSpace::createProofingTransform(const KoColorSpace *dstColorSpace, const KoColorSpace *proofingSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::Intent proofingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags, quint8 *gamutWarning, double adaptationState) const { if (!d->iccEngine) { d->iccEngine = KoColorSpaceEngineRegistry::instance()->get("icc"); } if (!d->iccEngine) return 0; return d->iccEngine->createColorProofingTransformation(this, dstColorSpace, proofingSpace, renderingIntent, proofingIntent, conversionFlags, gamutWarning, adaptationState); } bool KoColorSpace::proofPixelsTo(const quint8 *src, quint8 *dst, quint32 numPixels, KoColorConversionTransformation *proofingTransform) const { proofingTransform->transform(src, dst, numPixels); //the transform is deleted in the destructor. 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()->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; } void KoColorSpace::fillGrayBrushWithColorAndLightnessOverlay(quint8 *dst, const QRgb *brush, quint8 *brushColor, qint32 nPixels) const +{ + fillGrayBrushWithColorAndLightnessWithStrength(dst, brush, brushColor, 1.0, nPixels); +} + +void KoColorSpace::fillGrayBrushWithColorAndLightnessWithStrength(quint8* dst, const QRgb* brush, quint8* brushColor, qreal strength, qint32 nPixels) const { /// Fallback implementation. All RGB color spaces have their own /// implementation without any conversions. const int rgbPixelSize = sizeof(KoBgrU16Traits::Pixel); QScopedArrayPointer rgbBuffer(new quint8[(nPixels + 1) * rgbPixelSize]); - quint8 *rgbBrushColorBuffer = rgbBuffer.data() + nPixels * rgbPixelSize; + quint8* rgbBrushColorBuffer = rgbBuffer.data() + nPixels * rgbPixelSize; this->toRgbA16(dst, rgbBuffer.data(), nPixels); this->toRgbA16(brushColor, rgbBrushColorBuffer, 1); - fillGrayBrushWithColorPreserveLightnessRGB(rgbBuffer.data(), brush, rgbBrushColorBuffer, nPixels); + fillGrayBrushWithColorPreserveLightnessRGB(rgbBuffer.data(), brush, rgbBrushColorBuffer, strength, nPixels); this->fromRgbA16(rgbBuffer.data(), dst, nPixels); } diff --git a/libs/pigment/KoColorSpace.h b/libs/pigment/KoColorSpace.h index b83fcb4a09..8a9b176611 100644 --- a/libs/pigment/KoColorSpace.h +++ b/libs/pigment/KoColorSpace.h @@ -1,715 +1,717 @@ /* * Copyright (c) 2005 Boudewijn Rempt * Copyright (c) 2006-2007 Cyrille Berger * * 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. */ #ifndef KOCOLORSPACE_H #define KOCOLORSPACE_H #include #include #include #include #include #include #include "KoColorSpaceConstants.h" #include "KoColorConversionTransformation.h" #include "KoColorProofingConversionTransformation.h" #include "KoCompositeOp.h" #include #include "kritapigment_export.h" class QDomDocument; class QDomElement; class KoChannelInfo; class KoColorProfile; class KoColorTransformation; class QBitArray; enum Deletability { OwnedByRegistryDoNotDelete, OwnedByRegistryRegistryDeletes, NotOwnedByRegistry }; enum ColorSpaceIndependence { FULLY_INDEPENDENT, TO_LAB16, TO_RGBA8, TO_RGBA16 }; class KoMixColorsOp; class KoConvolutionOp; /** * A KoColorSpace is the definition of a certain color space. * * A color model and a color space are two related concepts. A color * model is more general in that it describes the channels involved and * how they in broad terms combine to describe a color. Examples are * RGB, HSV, CMYK. * * A color space is more specific in that it also describes exactly how * the channels are combined. So for each color model there can be a * number of specific color spaces. So RGB is the model and sRGB, * adobeRGB, etc are colorspaces. * * In Pigment KoColorSpace acts as both a color model and a color space. * You can think of the class definition as the color model, but the * instance of the class as representing a colorspace. * * A third concept is the profile represented by KoColorProfile. It * represents the info needed to specialize a color model into a color * space. * * KoColorSpace is an abstract class serving as an interface. * * Subclasses implement actual color spaces * Some subclasses implement only some parts and are named Traits * */ class KRITAPIGMENT_EXPORT KoColorSpace : public boost::equality_comparable { friend class KoColorSpaceRegistry; friend class KoColorSpaceFactory; protected: /// Only for use by classes that serve as baseclass for real color spaces KoColorSpace(); public: /// Should be called by real color spaces KoColorSpace(const QString &id, const QString &name, KoMixColorsOp* mixColorsOp, KoConvolutionOp* convolutionOp); virtual bool operator==(const KoColorSpace& rhs) const; protected: virtual ~KoColorSpace(); public: //========== Gamut and other basic info ===================================// /* * @returns QPolygonF with 5*channel samples converted to xyY. * maybe convert to 3d space in future? */ QPolygonF gamutXYY() const; /* * @returns a polygon with 5 samples per channel converted to xyY, but unlike * gamutxyY it focuses on the luminance. This then can be used to visualise * the approximate trc of a given colorspace. */ QPolygonF estimatedTRCXYY() const; QVector lumaCoefficients() const; //========== Channels =====================================================// /// Return a list describing all the channels this color model has. The order /// of the channels in the list is the order of channels in the pixel. To find /// out the preferred display position, use KoChannelInfo::displayPosition. QList channels() const; /** * The total number of channels for a single pixel in this color model */ virtual quint32 channelCount() const = 0; /** * Position of the alpha channel in a pixel */ virtual quint32 alphaPos() const = 0; /** * The total number of color channels (excludes alpha) for a single * pixel in this color model. */ virtual quint32 colorChannelCount() const = 0; /** * returns a QBitArray that contains true for the specified * channel types: * * @param color if true, set all color channels to true * @param alpha if true, set all alpha channels to true * * The order of channels is the colorspace descriptive order, * not the pixel order. */ QBitArray channelFlags(bool color = true, bool alpha = false) const; /** * The size in bytes of a single pixel in this color model */ virtual quint32 pixelSize() const = 0; /** * Return a string with the channel's value suitable for display in the gui. */ virtual QString channelValueText(const quint8 *pixel, quint32 channelIndex) const = 0; /** * Return a string with the channel's value with integer * channels normalised to the floating point range 0 to 1, if * appropriate. */ virtual QString normalisedChannelValueText(const quint8 *pixel, quint32 channelIndex) const = 0; /** * Return a QVector of floats with channels' values normalized * to floating point range 0 to 1. */ virtual void normalisedChannelsValue(const quint8 *pixel, QVector &channels) const = 0; /** * Write in the pixel the value from the normalized vector. */ virtual void fromNormalisedChannelsValue(quint8 *pixel, const QVector &values) const = 0; /** * Convert the value of the channel at the specified position into * an 8-bit value. The position is not the number of bytes, but * the position of the channel as defined in the channel info list. */ virtual quint8 scaleToU8(const quint8 * srcPixel, qint32 channelPos) const = 0; /** * Set dstPixel to the pixel containing only the given channel of srcPixel. The remaining channels * should be set to whatever makes sense for 'empty' channels of this color space, * with the intent being that the pixel should look like it only has the given channel. */ virtual void singleChannelPixel(quint8 *dstPixel, const quint8 *srcPixel, quint32 channelIndex) const = 0; //========== Identification ===============================================// /** * ID for use in files and internally: unchanging name. As the id must be unique * it is usually the concatenation of the id of the color model and of the color * depth, for instance "RGBA8" or "CMYKA16" or "XYZA32f". */ QString id() const; /** * User visible name which contains the name of the color model and of the color depth. * For instance "RGBA (8-bits)" or "CMYKA (16-bits)". */ QString name() const; /** * @return a string that identify the color model (for instance "RGB" or "CMYK" ...) * @see KoColorModelStandardIds.h */ virtual KoID colorModelId() const = 0; /** * @return a string that identify the bit depth (for instance "U8" or "F16" ...) * @see KoColorModelStandardIds.h */ virtual KoID colorDepthId() const = 0; /** * @return true if the profile given in argument can be used by this color space */ virtual bool profileIsCompatible(const KoColorProfile* profile) const = 0; /** * If false, images in this colorspace will degrade considerably by * functions, tools and filters that have the given measure of colorspace * independence. * * @param independence the measure to which this colorspace will suffer * from the manipulations of the tool or filter asking * @return false if no degradation will take place, true if degradation will * take place */ virtual bool willDegrade(ColorSpaceIndependence independence) const = 0; //========== Capabilities =================================================// /** * Tests if the colorspace offers the specific composite op. */ virtual bool hasCompositeOp(const QString & id) const; /** * Returns the list of user-visible composite ops supported by this colorspace. */ virtual QList compositeOps() const; /** * Retrieve a single composite op from the ones this colorspace offers. * If the requeste composite op does not exist, COMPOSITE_OVER is returned. */ const KoCompositeOp * compositeOp(const QString & id) const; /** * add a composite op to this colorspace. */ virtual void addCompositeOp(const KoCompositeOp * op); /** * Returns true if the colorspace supports channel values outside the * (normalised) range 0 to 1. */ virtual bool hasHighDynamicRange() const = 0; //========== Display profiles =============================================// /** * Return the profile of this color space. */ virtual const KoColorProfile * profile() const = 0; //================= Conversion functions ==================================// /** * The fromQColor methods take a given color defined as an RGB QColor * and fills a byte array with the corresponding color in the * the colorspace managed by this strategy. * * @param color the QColor that will be used to fill dst * @param dst a pointer to a pixel * @param profile the optional profile that describes the color values of QColor */ virtual void fromQColor(const QColor& color, quint8 *dst, const KoColorProfile * profile = 0) const = 0; /** * The toQColor methods take a byte array that is at least pixelSize() long * and converts the contents to a QColor, using the given profile as a source * profile and the optional profile as a destination profile. * * @param src a pointer to the source pixel * @param c the QColor that will be filled with the color at src * @param profile the optional profile that describes the color in c, for instance the monitor profile */ virtual void toQColor(const quint8 *src, QColor *c, const KoColorProfile * profile = 0) const = 0; /** * Convert the pixels in data to (8-bit BGRA) QImage using the specified profiles. * * @param data A pointer to a contiguous memory region containing width * height pixels * @param width in pixels * @param height in pixels * @param dstProfile destination profile * @param renderingIntent the rendering intent * @param conversionFlags conversion flags */ virtual QImage convertToQImage(const quint8 *data, qint32 width, qint32 height, const KoColorProfile * dstProfile, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const; /** * Convert the specified data to Lab (D50). All colorspaces are guaranteed to support this * * @param src the source data * @param dst the destination data * @param nPixels the number of source pixels */ virtual void toLabA16(const quint8 * src, quint8 * dst, quint32 nPixels) const; /** * Convert the specified data from Lab (D50). to this colorspace. All colorspaces are * guaranteed to support this. * * @param src the pixels in 16 bit lab format * @param dst the destination data * @param nPixels the number of pixels in the array */ virtual void fromLabA16(const quint8 * src, quint8 * dst, quint32 nPixels) const; /** * Convert the specified data to sRGB 16 bits. All colorspaces are guaranteed to support this * * @param src the source data * @param dst the destination data * @param nPixels the number of source pixels */ virtual void toRgbA16(const quint8 * src, quint8 * dst, quint32 nPixels) const; /** * Convert the specified data from sRGB 16 bits. to this colorspace. All colorspaces are * guaranteed to support this. * * @param src the pixels in 16 bit rgb format * @param dst the destination data * @param nPixels the number of pixels in the array */ virtual void fromRgbA16(const quint8 * src, quint8 * dst, quint32 nPixels) const; /** * Create a color conversion transformation. */ virtual KoColorConversionTransformation* createColorConverter(const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const; /** * Convert a byte array of srcLen pixels *src to the specified color space * and put the converted bytes into the prepared byte array *dst. * * Returns false if the conversion failed, true if it succeeded * * This function is not thread-safe. If you want to apply multiple conversion * in different threads at the same time, you need to create one color converter * per-thread using createColorConverter. */ virtual bool convertPixelsTo(const quint8 * src, quint8 * dst, const KoColorSpace * dstColorSpace, quint32 numPixels, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const; virtual KoColorConversionTransformation *createProofingTransform(const KoColorSpace * dstColorSpace, const KoColorSpace * proofingSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::Intent proofingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags, quint8 *gamutWarning, double adaptationState) const; /** * @brief proofPixelsTo * @param src source * @param dst destination * @param numPixels the amount of pixels. * @param proofingTransform the intent used for proofing. * @return */ virtual bool proofPixelsTo(const quint8 * src, quint8 * dst, quint32 numPixels, KoColorConversionTransformation *proofingTransform) const; /** * Convert @p nPixels pixels in @p src into their human-visible * visual representation. The channel is shown as grayscale. * * Both buffers are in the same color space. * * @param src source buffer in (*this) color space * @param dst destination buffer in the same color space as @p src * @param nPixels length of the buffers in number of pixels * @param pixelSize stride of each pixel in the destination buffer * @param selectedChannelIndex Index of the selected channel. */ virtual void convertChannelToVisualRepresentation(const quint8 *src, quint8 *dst, quint32 nPixels, const qint32 selectedChannelIndex) const = 0; /** * Convert @p nPixels pixels in @p src into their human-visible * visual representation. The channels are shown as if other channels were null (or, if Lab, L = 1.0, *a = *b = 0.0). * * Both buffers are in the same color space. * * @param src source buffer in (*this) color space * @param dst destination buffer in the same color space as @p src * @param nPixels length of the buffers in number of pixels * @param pixelSize stride of each pixel in the destination buffer * @param selectedChannels Bitmap of selected channels */ virtual void convertChannelToVisualRepresentation(const quint8 *src, quint8 *dst, quint32 nPixels, const QBitArray selectedChannels) const = 0; //============================== Manipulation functions ==========================// // // The manipulation functions have default implementations that _convert_ the pixel // to a QColor and back. Reimplement these methods in your color strategy! // /** * Get the alpha value of the given pixel, downscaled to an 8-bit value. */ virtual quint8 opacityU8(const quint8 * pixel) const = 0; virtual qreal opacityF(const quint8 * pixel) const = 0; /** * Set the alpha channel of the given run of pixels to the given value. * * pixels -- a pointer to the pixels that will have their alpha set to this value * alpha -- a downscaled 8-bit value for opacity * nPixels -- the number of pixels * */ virtual void setOpacity(quint8 * pixels, quint8 alpha, qint32 nPixels) const = 0; virtual void setOpacity(quint8 * pixels, qreal alpha, qint32 nPixels) const = 0; /** * Multiply the alpha channel of the given run of pixels by the given value. * * pixels -- a pointer to the pixels that will have their alpha set to this value * alpha -- a downscaled 8-bit value for opacity * nPixels -- the number of pixels * */ virtual void multiplyAlpha(quint8 * pixels, quint8 alpha, qint32 nPixels) const = 0; /** * Applies the specified 8-bit alpha mask to the pixels. We assume that there are just * as many alpha values as pixels but we do not check this; the alpha values * are assumed to be 8-bits. */ virtual void applyAlphaU8Mask(quint8 * pixels, const quint8 * alpha, qint32 nPixels) const = 0; /** * Applies the inverted 8-bit alpha mask to the pixels. We assume that there are just * as many alpha values as pixels but we do not check this; the alpha values * are assumed to be 8-bits. */ virtual void applyInverseAlphaU8Mask(quint8 * pixels, const quint8 * alpha, qint32 nPixels) const = 0; /** * Applies the specified float alpha mask to the pixels. We assume that there are just * as many alpha values as pixels but we do not check this; alpha values have to be between 0.0 and 1.0 */ virtual void applyAlphaNormedFloatMask(quint8 * pixels, const float * alpha, qint32 nPixels) const = 0; /** * Applies the inverted specified float alpha mask to the pixels. We assume that there are just * as many alpha values as pixels but we do not check this; alpha values have to be between 0.0 and 1.0 */ virtual void applyInverseNormedFloatMask(quint8 * pixels, const float * alpha, qint32 nPixels) const = 0; /** * Fills \p pixels with specified \p brushColor and then applies inverted brush * mask specified in \p alpha. */ virtual void fillInverseAlphaNormedFloatMaskWithColor(quint8 * pixels, const float * alpha, const quint8 *brushColor, qint32 nPixels) const = 0; /** * Fills \p dst with specified \p brushColor and then applies inverted brush * mask specified in \p brush. Premultiplied red channel of the brush is * used as an alpha channel for destination pixels. * * The equation is: * * dstC = colorC; * dstA = qAlpha(brush) * (255 - qRed(brush)) / 255; */ virtual void fillGrayBrushWithColor(quint8 *dst, const QRgb *brush, quint8 *brushColor, qint32 nPixels) const = 0; /** * Fills \p dst with specified \p brushColor and then applies inverted brush * mask specified in \p brush. Inverted red channel of the brush is used * as lightness of the destination. Alpha channel of the brush is used as * alpha of the destination. * * The equation is: * * dstL_hsl = preserveLightness(colorL_hsl, lightFactor); * dstA = qAlpha(brush); * * For details on preserveLightness() formula, * see KoColorSpacePreserveLightnessUtils.h */ virtual void fillGrayBrushWithColorAndLightnessOverlay(quint8 *dst, const QRgb *brush, quint8 *brushColor, qint32 nPixels) const; + //Same as above, but with contrast adjusted by strength. Strength == 1 -> full contrast. Allows softer lightness adjustments. + virtual void fillGrayBrushWithColorAndLightnessWithStrength(quint8* dst, const QRgb* brush, quint8* brushColor, qreal strength, qint32 nPixels) const; /** * Create an adjustment object for adjusting the brightness and contrast * transferValues is a 256 bins array with values from 0 to 0xFFFF * This function is thread-safe, but you need to create one KoColorTransformation per thread. */ virtual KoColorTransformation *createBrightnessContrastAdjustment(const quint16 *transferValues) const = 0; /** * Create an adjustment object for adjusting individual channels * transferValues is an array of colorChannelCount number of 256 bins array with values from 0 to 0xFFFF * This function is thread-safe, but you need to create one KoColorTransformation per thread. * * The layout of the channels must be the following: * * 0..N-2 - color channels of the pixel; * N-1 - alpha channel of the pixel (if exists) */ virtual KoColorTransformation *createPerChannelAdjustment(const quint16 * const* transferValues) const = 0; /** * Darken all color channels with the given amount. If compensate is true, * the compensation factor will be used to limit the darkening. * */ virtual KoColorTransformation *createDarkenAdjustment(qint32 shade, bool compensate, qreal compensation) const = 0; /** * Invert color channels of the given pixels * This function is thread-safe, but you need to create one KoColorTransformation per thread. */ virtual KoColorTransformation *createInvertTransformation() const = 0; /** * Get the difference between 2 colors, normalized in the range (0,255). Only completely * opaque and completely transparent are taken into account when computing the difference; * other transparency levels are not regarded when finding the difference. * * Completely transparent pixels are treated as if they are completely * different from any non-transparent pixels. */ virtual quint8 difference(const quint8* src1, const quint8* src2) const = 0; /** * Get the difference between 2 colors, normalized in the range (0,255). This function * takes the Alpha channel of the pixel into account. Alpha channel has the same * weight as Lightness channel. * * Completely transparent pixels are treated as if their color channels are * the same as ones of the other pixel. In other words, only alpha channel * difference is compared. */ virtual quint8 differenceA(const quint8* src1, const quint8* src2) const = 0; /** * @return the mix color operation of this colorspace (do not delete it locally, it's deleted by the colorspace). */ virtual KoMixColorsOp* mixColorsOp() const; /** * @return the convolution operation of this colorspace (do not delete it locally, it's deleted by the colorspace). */ virtual KoConvolutionOp* convolutionOp() const; /** * Calculate the intensity of the given pixel, scaled down to the range 0-255. XXX: Maybe this should be more flexible */ virtual quint8 intensity8(const quint8 * src) const = 0; /* *increase luminosity by step */ virtual void increaseLuminosity(quint8 * pixel, qreal step) const; virtual void decreaseLuminosity(quint8 * pixel, qreal step) const; virtual void increaseSaturation(quint8 * pixel, qreal step) const; virtual void decreaseSaturation(quint8 * pixel, qreal step) const; virtual void increaseHue(quint8 * pixel, qreal step) const; virtual void decreaseHue(quint8 * pixel, qreal step) const; virtual void increaseRed(quint8 * pixel, qreal step) const; virtual void increaseGreen(quint8 * pixel, qreal step) const; virtual void increaseBlue(quint8 * pixel, qreal step) const; virtual void increaseYellow(quint8 * pixel, qreal step) const; virtual void toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const = 0; virtual QVector fromHSY(qreal *hue, qreal *sat, qreal *luma) const = 0; virtual void toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const = 0; virtual QVector fromYUV(qreal *y, qreal *u, qreal *v) const = 0; /** * Compose two arrays of pixels together. If source and target * are not the same color model, the source pixels will be * converted to the target model. We're "dst" -- "dst" pixels are always in _this_ * colorspace. * * @param srcSpace the colorspace of the source pixels that will be composited onto "us" * @param params the information needed for blitting e.g. the source and destination pixel data, * the opacity and flow, ... * @param op the composition operator to use, e.g. COPY_OVER * @param renderingIntent the rendering intent * @param conversionFlags the conversion flags. * */ virtual void bitBlt(const KoColorSpace* srcSpace, const KoCompositeOp::ParameterInfo& params, const KoCompositeOp* op, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const; /** * Serialize this color following Create's swatch color specification available * at https://web.archive.org/web/20110826002520/http://create.freedesktop.org/wiki/Swatches_-_colour_file_format/Draft * * This function doesn't create the \ element but rather the \, * \, \ ... elements. It is assumed that colorElt is the \ * element. * * @param pixel buffer to serialized * @param colorElt root element for the serialization, it is assumed that this * element is \ * @param doc is the document containing colorElt */ virtual void colorToXML(const quint8* pixel, QDomDocument& doc, QDomElement& colorElt) const = 0; /** * Unserialize a color following Create's swatch color specification available * at https://web.archive.org/web/20110826002520/http://create.freedesktop.org/wiki/Swatches_-_colour_file_format/Draft * * @param pixel buffer where the color will be unserialized * @param elt the element to unserialize (\, \, \) * @return the unserialize color, or an empty color object if the function failed * to unserialize the color */ virtual void colorFromXML(quint8* pixel, const QDomElement& elt) const = 0; KoColorTransformation* createColorTransformation(const QString & id, const QHash & parameters) const; protected: /** * Use this function in the constructor of your colorspace to add the information about a channel. * @param ci a pointer to the information about a channel */ virtual void addChannel(KoChannelInfo * ci); const KoColorConversionTransformation* toLabA16Converter() const; const KoColorConversionTransformation* fromLabA16Converter() const; const KoColorConversionTransformation* toRgbA16Converter() const; const KoColorConversionTransformation* fromRgbA16Converter() const; /** * Returns the thread-local conversion cache. If it doesn't exist * yet, it is created. If it is currently too small, it is resized. */ QVector * threadLocalConversionCache(quint32 size) const; /** * This function defines the behavior of the bitBlt function * when the composition of pixels in different colorspaces is * requested, that is in case: * * srcCS == any * dstCS == this * * 1) preferCompositionInSourceColorSpace() == false, * * the source pixels are first converted to *this color space * and then composition is performed. * * 2) preferCompositionInSourceColorSpace() == true, * * the destination pixels are first converted into *srcCS color * space, then the composition is done, and the result is finally * converted into *this colorspace. * * This is used by alpha8() color space mostly, because it has * weaker representation of the color, so the composition * should be done in CS with richer functionality. */ virtual bool preferCompositionInSourceColorSpace() const; struct Private; Private * const d; }; inline QDebug operator<<(QDebug dbg, const KoColorSpace *cs) { if (cs) { dbg.nospace() << cs->name() << " (" << cs->colorModelId().id() << "," << cs->colorDepthId().id() << " )"; } else { dbg.nospace() << "0x0"; } return dbg.space(); } #endif // KOCOLORSPACE_H diff --git a/libs/pigment/KoColorSpacePreserveLightnessUtils.h b/libs/pigment/KoColorSpacePreserveLightnessUtils.h index 4739d9a361..ce4dbf1388 100644 --- a/libs/pigment/KoColorSpacePreserveLightnessUtils.h +++ b/libs/pigment/KoColorSpacePreserveLightnessUtils.h @@ -1,79 +1,82 @@ /* * Copyright (c) 2020 Peter Schatz * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KOCOLORSPACEPRESERVELIGHTNESSUTILS_H #define KOCOLORSPACEPRESERVELIGHTNESSUTILS_H #include #include "kis_global.h" template -inline static void fillGrayBrushWithColorPreserveLightnessRGB(quint8 *pixels, const QRgb *brush, quint8 *brushColor, qint32 nPixels) { +inline static void fillGrayBrushWithColorPreserveLightnessRGB(quint8 *pixels, const QRgb *brush, quint8 *brushColor, qreal strength, qint32 nPixels) { using RGBPixel = typename CSTraits::Pixel; using channels_type = typename CSTraits::channels_type; static const quint32 pixelSize = CSTraits::pixelSize; const RGBPixel *brushColorRGB = reinterpret_cast(brushColor); const float brushColorR = KoColorSpaceMaths::scaleToA(brushColorRGB->red); const float brushColorG = KoColorSpaceMaths::scaleToA(brushColorRGB->green); const float brushColorB = KoColorSpaceMaths::scaleToA(brushColorRGB->blue); + const float brushColorA = KoColorSpaceMaths::scaleToA(brushColorRGB->alpha); /** * Lightness mixing algorithm is developed by Peter Schatz * * We use a formula f(x) where f(0) = 0, f(1) = 1, and f(.5) = z, * where z is the lightness of the brush color. This can’t be linear unless * the color chosen is also .5. So we use a quadratic equation: * * f(x) = ax^2 + b^x +c * 0,0 -> 0 = a0^2 + b0 + c -> c = 0 * 1,1 -> 1 = a1^2 +b1 + c -> 1 = a + b + 0 -> a = 1 - b * .5,z -> z = a*.5^2 + b*.5 + c -> z = * = a/4 + b/2 + 0 -> z = * = 1/4 - b/4 + b/2 -> z = 1/4 + b/4 -> b = 4z - 1 * * f(x) = (1 - (4z - 1)) * x^2 + (4z - 1) * x */ const float brushColorL = getLightness(brushColorR, brushColorG, brushColorB); const float lightnessB = 4 * brushColorL - 1; const float lightnessA = 1 - lightnessB; for (; nPixels > 0; --nPixels, pixels += pixelSize, ++brush) { RGBPixel *pixelRGB = reinterpret_cast(pixels); - const float brushMaskL = qRed(*brush) / 255.0f; + float brushMaskL = qRed(*brush) / 255.0f; + brushMaskL = (brushMaskL - 0.5) * strength + 0.5; const float finalLightness = lightnessA * pow2(brushMaskL) + lightnessB * brushMaskL; + const float finalAlpha = qMin(qAlpha(*brush) / 255.0f, brushColorA); float pixelR = brushColorR; float pixelG = brushColorG; float pixelB = brushColorB; setLightness(pixelR, pixelG, pixelB, finalLightness); pixelRGB->red = KoColorSpaceMaths::scaleToA(pixelR); pixelRGB->green = KoColorSpaceMaths::scaleToA(pixelG); pixelRGB->blue = KoColorSpaceMaths::scaleToA(pixelB); - pixelRGB->alpha = KoColorSpaceMaths::scaleToA(quint8(qAlpha(*brush))); + pixelRGB->alpha = KoColorSpaceMaths::scaleToA(quint8(finalAlpha * 255)); } } #endif // KOCOLORSPACEPRESERVELIGHTNESSUTILS_H diff --git a/libs/pigment/colorspaces/KoRgbU16ColorSpace.cpp b/libs/pigment/colorspaces/KoRgbU16ColorSpace.cpp index 68ab387f20..6a2ff2406e 100644 --- a/libs/pigment/colorspaces/KoRgbU16ColorSpace.cpp +++ b/libs/pigment/colorspaces/KoRgbU16ColorSpace.cpp @@ -1,104 +1,109 @@ /* * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2006 Cyrille Berger * * 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 program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KoRgbU16ColorSpace.h" #include #include #include #include #include #include "KoChannelInfo.h" #include "KoID.h" #include "KoIntegerMaths.h" #include "KoColorConversions.h" #include KoRgbU16ColorSpace::KoRgbU16ColorSpace() : KoSimpleColorSpace(colorSpaceId(), i18n("RGB (16-bit integer/channel, unmanaged)"), RGBAColorModelID, Integer16BitsColorDepthID) { } KoRgbU16ColorSpace::~KoRgbU16ColorSpace() { } QString KoRgbU16ColorSpace::colorSpaceId() { return QString("RGBA16"); } KoColorSpace* KoRgbU16ColorSpace::clone() const { return new KoRgbU16ColorSpace(); } void KoRgbU16ColorSpace::fromQColor(const QColor& c, quint8 *dst, const KoColorProfile * /*profile*/) const { QVector channelValues; channelValues << c.blueF() << c.greenF() << c.redF() << c.alphaF(); fromNormalisedChannelsValue(dst, channelValues); } void KoRgbU16ColorSpace::toQColor(const quint8 * src, QColor *c, const KoColorProfile * /*profile*/) const { QVector channelValues(4); normalisedChannelsValue(src, channelValues); c->setRgbF(channelValues[2], channelValues[1], channelValues[0], channelValues[3]); } void KoRgbU16ColorSpace::toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const { RGBToHSY(channelValues[0],channelValues[1],channelValues[2], hue, sat, luma); } QVector KoRgbU16ColorSpace::fromHSY(qreal *hue, qreal *sat, qreal *luma) const { QVector channelValues(4); HSYToRGB(*hue, *sat, *luma, &channelValues[0],&channelValues[1],&channelValues[2]); channelValues[3]=1.0; return channelValues; } void KoRgbU16ColorSpace::toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const { RGBToYUV(channelValues[0],channelValues[1],channelValues[2], y, u, v); } QVector KoRgbU16ColorSpace::fromYUV(qreal *y, qreal *u, qreal *v) const { QVector channelValues(4); YUVToRGB(*y, *u, *v, &channelValues[0],&channelValues[1],&channelValues[2]); channelValues[3]=1.0; return channelValues; } void KoRgbU16ColorSpace::fillGrayBrushWithColorAndLightnessOverlay(quint8 *dst, const QRgb *brush, quint8 *brushColor, qint32 nPixels) const { - fillGrayBrushWithColorPreserveLightnessRGB(dst, brush, brushColor, nPixels); + fillGrayBrushWithColorPreserveLightnessRGB(dst, brush, brushColor, 1.0, nPixels); } + +void KoRgbU16ColorSpace::fillGrayBrushWithColorAndLightnessWithStrength(quint8* dst, const QRgb* brush, quint8* brushColor, qreal strength, qint32 nPixels) const +{ + fillGrayBrushWithColorPreserveLightnessRGB(dst, brush, brushColor, strength, nPixels); +} \ No newline at end of file diff --git a/libs/pigment/colorspaces/KoRgbU16ColorSpace.h b/libs/pigment/colorspaces/KoRgbU16ColorSpace.h index 0a0b3ecbc6..537339b11c 100644 --- a/libs/pigment/colorspaces/KoRgbU16ColorSpace.h +++ b/libs/pigment/colorspaces/KoRgbU16ColorSpace.h @@ -1,77 +1,78 @@ /* * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2006 Cyrille Berger * * 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 program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KORGBU16COLORSPACE_H #define KORGBU16COLORSPACE_H #include #include "KoSimpleColorSpace.h" #include "KoSimpleColorSpaceFactory.h" #include "KoColorModelStandardIds.h" struct KoBgrU16Traits; /** * The alpha mask is a special color strategy that treats all pixels as * alpha value with a color common to the mask. The default color is white. */ class KoRgbU16ColorSpace : public KoSimpleColorSpace { public: KoRgbU16ColorSpace(); ~KoRgbU16ColorSpace() override; static QString colorSpaceId(); virtual KoColorSpace* clone() const; void fromQColor(const QColor& color, quint8 *dst, const KoColorProfile * profile = 0) const override; void toQColor(const quint8 *src, QColor *c, const KoColorProfile * profile = 0) const override; void toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const override; QVector fromHSY(qreal *hue, qreal *sat, qreal *luma) const override; void toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const override; QVector fromYUV(qreal *y, qreal *u, qreal *v) const override; void fillGrayBrushWithColorAndLightnessOverlay(quint8 *dst, const QRgb *brush, quint8 *brushColor, qint32 nPixels) const override; + void fillGrayBrushWithColorAndLightnessWithStrength(quint8* dst, const QRgb* brush, quint8* brushColor, qreal strength, qint32 nPixels) const override; }; class KoRgbU16ColorSpaceFactory : public KoSimpleColorSpaceFactory { public: KoRgbU16ColorSpaceFactory() : KoSimpleColorSpaceFactory(KoRgbU16ColorSpace::colorSpaceId(), i18n("RGB (16-bit integer/channel, unmanaged)"), true, RGBAColorModelID, Integer16BitsColorDepthID) { } KoColorSpace *createColorSpace(const KoColorProfile *) const override { return new KoRgbU16ColorSpace(); } }; #endif diff --git a/libs/pigment/colorspaces/KoRgbU8ColorSpace.cpp b/libs/pigment/colorspaces/KoRgbU8ColorSpace.cpp index 4b92725c07..b8ba5dbeb9 100644 --- a/libs/pigment/colorspaces/KoRgbU8ColorSpace.cpp +++ b/libs/pigment/colorspaces/KoRgbU8ColorSpace.cpp @@ -1,117 +1,122 @@ /* * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2006 Cyrille Berger * * 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 program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KoRgbU8ColorSpace.h" #include #include #include #include #include #include "KoChannelInfo.h" #include "KoID.h" #include "KoIntegerMaths.h" #include "compositeops/KoCompositeOps.h" #include "KoColorConversions.h" #include KoRgbU8ColorSpace::KoRgbU8ColorSpace() : KoSimpleColorSpace(colorSpaceId(), i18n("RGB (8-bit integer/channel, unmanaged)"), RGBAColorModelID, Integer8BitsColorDepthID) { addChannel(new KoChannelInfo(i18n("Blue"), 0, 2, KoChannelInfo::COLOR, KoChannelInfo::UINT8, 1, QColor(0, 0, 255))); addChannel(new KoChannelInfo(i18n("Green"), 1, 1, KoChannelInfo::COLOR, KoChannelInfo::UINT8, 1, QColor(0, 255, 0))); addChannel(new KoChannelInfo(i18n("Red"), 2, 0, KoChannelInfo::COLOR, KoChannelInfo::UINT8, 1, QColor(255, 0, 0))); addChannel(new KoChannelInfo(i18n("Alpha"), 3, 3, KoChannelInfo::ALPHA, KoChannelInfo::UINT8)); // ADD, ALPHA_DARKEN, BURN, DIVIDE, DODGE, ERASE, MULTIPLY, OVER, OVERLAY, SCREEN, SUBTRACT addStandardCompositeOps(this); } KoRgbU8ColorSpace::~KoRgbU8ColorSpace() { } QString KoRgbU8ColorSpace::colorSpaceId() { return QString("RGBA"); } KoColorSpace* KoRgbU8ColorSpace::clone() const { return new KoRgbU8ColorSpace(); } void KoRgbU8ColorSpace::fromQColor(const QColor& c, quint8 *dst, const KoColorProfile * /*profile*/) const { QVector channelValues; channelValues << c.blueF() << c.greenF() << c.redF() << c.alphaF(); fromNormalisedChannelsValue(dst, channelValues); } void KoRgbU8ColorSpace::toQColor(const quint8 * src, QColor *c, const KoColorProfile * /*profile*/) const { QVector channelValues(4); normalisedChannelsValue(src, channelValues); c->setRgbF(channelValues[2], channelValues[1], channelValues[0], channelValues[3]); } void KoRgbU8ColorSpace::toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const { RGBToHSY(channelValues[0],channelValues[1],channelValues[2], hue, sat, luma); } QVector KoRgbU8ColorSpace::fromHSY(qreal *hue, qreal *sat, qreal *luma) const { QVector channelValues(4); HSYToRGB(*hue, *sat, *luma, &channelValues[0],&channelValues[1],&channelValues[2]); channelValues[3]=1.0; return channelValues; } void KoRgbU8ColorSpace::toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const { RGBToYUV(channelValues[0],channelValues[1],channelValues[2], y, u, v); } QVector KoRgbU8ColorSpace::fromYUV(qreal *y, qreal *u, qreal *v) const { QVector channelValues(4); YUVToRGB(*y, *u, *v, &channelValues[0],&channelValues[1],&channelValues[2]); channelValues[3]=1.0; return channelValues; } void KoRgbU8ColorSpace::fillGrayBrushWithColorAndLightnessOverlay(quint8 *dst, const QRgb *brush, quint8 *brushColor, qint32 nPixels) const { - fillGrayBrushWithColorPreserveLightnessRGB(dst, brush, brushColor, nPixels); + fillGrayBrushWithColorPreserveLightnessRGB(dst, brush, brushColor, 1.0, nPixels); +} + +void KoRgbU8ColorSpace::fillGrayBrushWithColorAndLightnessWithStrength(quint8* dst, const QRgb* brush, quint8* brushColor, qreal strength, qint32 nPixels) const +{ + fillGrayBrushWithColorPreserveLightnessRGB(dst, brush, brushColor, strength, nPixels); } diff --git a/libs/pigment/colorspaces/KoRgbU8ColorSpace.h b/libs/pigment/colorspaces/KoRgbU8ColorSpace.h index 70071219b1..4a6e2a156d 100644 --- a/libs/pigment/colorspaces/KoRgbU8ColorSpace.h +++ b/libs/pigment/colorspaces/KoRgbU8ColorSpace.h @@ -1,77 +1,78 @@ /* * Copyright (c) 2004 Boudewijn Rempt * Copyright (c) 2006 Cyrille Berger * * 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 program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KORGBU8COLORSPACE_H #define KORGBU8COLORSPACE_H #include #include "KoSimpleColorSpace.h" #include "KoSimpleColorSpaceFactory.h" #include "KoColorModelStandardIds.h" struct KoBgrU8Traits; /** * The alpha mask is a special color strategy that treats all pixels as * alpha value with a color common to the mask. The default color is white. */ class KoRgbU8ColorSpace : public KoSimpleColorSpace { public: KoRgbU8ColorSpace(); ~KoRgbU8ColorSpace() override; static QString colorSpaceId(); virtual KoColorSpace* clone() const; void fromQColor(const QColor& color, quint8 *dst, const KoColorProfile * profile = 0) const override; void toQColor(const quint8 *src, QColor *c, const KoColorProfile * profile = 0) const override; void toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const override; QVector fromHSY(qreal *hue, qreal *sat, qreal *luma) const override; void toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const override; QVector fromYUV(qreal *y, qreal *u, qreal *v) const override; void fillGrayBrushWithColorAndLightnessOverlay(quint8 *dst, const QRgb *brush, quint8 *brushColor, qint32 nPixels) const override; + void fillGrayBrushWithColorAndLightnessWithStrength(quint8* dst, const QRgb* brush, quint8* brushColor, qreal strength, qint32 nPixels) const override; }; class KoRgbU8ColorSpaceFactory : public KoSimpleColorSpaceFactory { public: KoRgbU8ColorSpaceFactory() : KoSimpleColorSpaceFactory("RGBA", i18n("RGB (8-bit integer/channel, unmanaged)"), true, RGBAColorModelID, Integer8BitsColorDepthID) { } KoColorSpace *createColorSpace(const KoColorProfile *) const override { return new KoRgbU8ColorSpace(); } }; #endif diff --git a/plugins/color/lcms2engine/colorspaces/rgb_f16/RgbF16ColorSpace.cpp b/plugins/color/lcms2engine/colorspaces/rgb_f16/RgbF16ColorSpace.cpp index 2918507f00..07ae34a9a9 100644 --- a/plugins/color/lcms2engine/colorspaces/rgb_f16/RgbF16ColorSpace.cpp +++ b/plugins/color/lcms2engine/colorspaces/rgb_f16/RgbF16ColorSpace.cpp @@ -1,117 +1,122 @@ /* * Copyright (c) 2006 Cyrille Berger * * 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 * Library 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 "RgbF16ColorSpace.h" #include #include #include #include "compositeops/KoCompositeOps.h" #include "compositeops/RgbCompositeOpIn.h" #include "compositeops/RgbCompositeOpOut.h" #include "compositeops/RgbCompositeOpBumpmap.h" #include #include RgbF16ColorSpace::RgbF16ColorSpace(const QString &name, KoColorProfile *p) : LcmsColorSpace(colorSpaceId(), name, TYPE_RGBA_HALF_FLT, cmsSigRgbData, p) { addChannel(new KoChannelInfo(i18n("Red"), 0 * sizeof(half), 0, KoChannelInfo::COLOR, KoChannelInfo::FLOAT16, 2, QColor(255, 0, 0))); addChannel(new KoChannelInfo(i18n("Green"), 1 * sizeof(half), 1, KoChannelInfo::COLOR, KoChannelInfo::FLOAT16, 2, QColor(0, 255, 0))); addChannel(new KoChannelInfo(i18n("Blue"), 2 * sizeof(half), 2, KoChannelInfo::COLOR, KoChannelInfo::FLOAT16, 2, QColor(0, 0, 255))); addChannel(new KoChannelInfo(i18n("Alpha"), 3 * sizeof(half), 3, KoChannelInfo::ALPHA, KoChannelInfo::FLOAT16, 2)); init(); addStandardCompositeOps(this); addCompositeOp(new RgbCompositeOpIn(this)); addCompositeOp(new RgbCompositeOpOut(this)); addCompositeOp(new RgbCompositeOpBumpmap(this)); } bool RgbF16ColorSpace::willDegrade(ColorSpaceIndependence independence) const { if (independence == TO_RGBA16) { return true; } else { return false; } } KoColorSpace *RgbF16ColorSpace::clone() const { return new RgbF16ColorSpace(name(), profile()->clone()); } void RgbF16ColorSpace::colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const { const KoRgbF16Traits::Pixel *p = reinterpret_cast(pixel); QDomElement labElt = doc.createElement("RGB"); labElt.setAttribute("r", KisDomUtils::toString(KoColorSpaceMaths< KoRgbF16Traits::channels_type, qreal>::scaleToA(p->red))); labElt.setAttribute("g", KisDomUtils::toString(KoColorSpaceMaths< KoRgbF16Traits::channels_type, qreal>::scaleToA(p->green))); labElt.setAttribute("b", KisDomUtils::toString(KoColorSpaceMaths< KoRgbF16Traits::channels_type, qreal>::scaleToA(p->blue))); labElt.setAttribute("space", profile()->name()); colorElt.appendChild(labElt); } void RgbF16ColorSpace::colorFromXML(quint8 *pixel, const QDomElement &elt) const { KoRgbF16Traits::Pixel *p = reinterpret_cast(pixel); p->red = KoColorSpaceMaths< qreal, KoRgbF16Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("r"))); p->green = KoColorSpaceMaths< qreal, KoRgbF16Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("g"))); p->blue = KoColorSpaceMaths< qreal, KoRgbF16Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("b"))); p->alpha = 1.0; } void RgbF16ColorSpace::toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const { RGBToHSY(channelValues[0],channelValues[1],channelValues[2], hue, sat, luma, lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); } QVector RgbF16ColorSpace::fromHSY(qreal *hue, qreal *sat, qreal *luma) const { QVector channelValues(4); HSYToRGB(*hue, *sat, *luma, &channelValues[0],&channelValues[1],&channelValues[2], lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); channelValues[3]=1.0; return channelValues; } void RgbF16ColorSpace::toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const { RGBToYUV(channelValues[0],channelValues[1],channelValues[2], y, u, v, lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); } QVector RgbF16ColorSpace::fromYUV(qreal *y, qreal *u, qreal *v) const { QVector channelValues(4); YUVToRGB(*y, *u, *v, &channelValues[0],&channelValues[1],&channelValues[2], lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); channelValues[3]=1.0; return channelValues; } void RgbF16ColorSpace::fillGrayBrushWithColorAndLightnessOverlay(quint8 *dst, const QRgb *brush, quint8 *brushColor, qint32 nPixels) const { - fillGrayBrushWithColorPreserveLightnessRGB(dst, brush, brushColor, nPixels); + fillGrayBrushWithColorPreserveLightnessRGB(dst, brush, brushColor, 1.0, nPixels); +} + +void RgbF16ColorSpace::fillGrayBrushWithColorAndLightnessWithStrength(quint8* dst, const QRgb* brush, quint8* brushColor, qreal strength, qint32 nPixels) const +{ + fillGrayBrushWithColorPreserveLightnessRGB(dst, brush, brushColor, strength, nPixels); } diff --git a/plugins/color/lcms2engine/colorspaces/rgb_f16/RgbF16ColorSpace.h b/plugins/color/lcms2engine/colorspaces/rgb_f16/RgbF16ColorSpace.h index 249a5f6c12..966b79bbcb 100644 --- a/plugins/color/lcms2engine/colorspaces/rgb_f16/RgbF16ColorSpace.h +++ b/plugins/color/lcms2engine/colorspaces/rgb_f16/RgbF16ColorSpace.h @@ -1,123 +1,124 @@ /* * Copyright (c) 2006 Cyrille Berger * * 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 * Library 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. */ #ifndef KORGBF16COLORSPACE_H_ #define KORGBF16COLORSPACE_H_ #include "LcmsColorSpace.h" #include "KoColorModelStandardIds.h" struct KoRgbF16Traits; class RgbF16ColorSpace : public LcmsColorSpace { public: RgbF16ColorSpace(const QString &name, KoColorProfile *p); bool willDegrade(ColorSpaceIndependence independence) const override; KoID colorModelId() const override { return RGBAColorModelID; } KoID colorDepthId() const override { return Float16BitsColorDepthID; } virtual KoColorSpace *clone() const; void colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const override; void colorFromXML(quint8* pixel, const QDomElement& elt) const override; void toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const override; QVector fromHSY(qreal *hue, qreal *sat, qreal *luma) const override; void toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const override; QVector fromYUV(qreal *y, qreal *u, qreal *v) const override; static QString colorSpaceId() { return QString("RGBAF16"); } bool hasHighDynamicRange() const override { return true; } void fillGrayBrushWithColorAndLightnessOverlay(quint8 *dst, const QRgb *brush, quint8 *brushColor, qint32 nPixels) const override; + void fillGrayBrushWithColorAndLightnessWithStrength(quint8* dst, const QRgb* brush, quint8* brushColor, qreal strength, qint32 nPixels) const override; }; class RgbF16ColorSpaceFactory : public LcmsColorSpaceFactory { public: RgbF16ColorSpaceFactory() : LcmsColorSpaceFactory(TYPE_RGBA_HALF_FLT, cmsSigRgbData) { } QString id() const override { return RgbF16ColorSpace::colorSpaceId(); } QString name() const override { return QString("%1 (%2)").arg(RGBAColorModelID.name()).arg(Float16BitsColorDepthID.name()); } bool userVisible() const override { return true; } KoID colorModelId() const override { return RGBAColorModelID; } KoID colorDepthId() const override { return Float16BitsColorDepthID; } int referenceDepth() const override { return 16; } KoColorSpace *createColorSpace(const KoColorProfile *p) const override { return new RgbF16ColorSpace(name(), p->clone()); } QString defaultProfile() const override { return "sRGB-elle-V2-g10.icc"; } bool isHdr() const override { return true; } }; #endif diff --git a/plugins/color/lcms2engine/colorspaces/rgb_f32/RgbF32ColorSpace.cpp b/plugins/color/lcms2engine/colorspaces/rgb_f32/RgbF32ColorSpace.cpp index d42aee9260..0cf2e8cadc 100644 --- a/plugins/color/lcms2engine/colorspaces/rgb_f32/RgbF32ColorSpace.cpp +++ b/plugins/color/lcms2engine/colorspaces/rgb_f32/RgbF32ColorSpace.cpp @@ -1,122 +1,127 @@ /* * Copyright (c) 2006 Cyrille Berger * * 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 * Library 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 "RgbF32ColorSpace.h" #include #include #include "compositeops/KoCompositeOps.h" #include "compositeops/RgbCompositeOps.h" #include #include #include #include RgbF32ColorSpace::RgbF32ColorSpace(const QString &name, KoColorProfile *p) : LcmsColorSpace(colorSpaceId(), name, TYPE_RGBA_FLT, cmsSigRgbData, p) { const IccColorProfile *icc_p = dynamic_cast(p); Q_ASSERT(icc_p); QVector uiRanges(icc_p->getFloatUIMinMax()); Q_ASSERT(uiRanges.size() == 3); addChannel(new KoChannelInfo(i18n("Red"), 0 * sizeof(float), 0, KoChannelInfo::COLOR, KoChannelInfo::FLOAT32, 4, QColor(255, 0, 0), uiRanges[0])); addChannel(new KoChannelInfo(i18n("Green"), 1 * sizeof(float), 1, KoChannelInfo::COLOR, KoChannelInfo::FLOAT32, 4, QColor(0, 255, 0), uiRanges[1])); addChannel(new KoChannelInfo(i18n("Blue"), 2 * sizeof(float), 2, KoChannelInfo::COLOR, KoChannelInfo::FLOAT32, 4, QColor(0, 0, 255), uiRanges[2])); addChannel(new KoChannelInfo(i18n("Alpha"), 3 * sizeof(float), 3, KoChannelInfo::ALPHA, KoChannelInfo::FLOAT32, 4)); init(); addStandardCompositeOps(this); addCompositeOp(new RgbCompositeOpIn(this)); addCompositeOp(new RgbCompositeOpOut(this)); addCompositeOp(new RgbCompositeOpBumpmap(this)); } bool RgbF32ColorSpace::willDegrade(ColorSpaceIndependence independence) const { if (independence == TO_RGBA16) { return true; } else { return false; } } KoColorSpace *RgbF32ColorSpace::clone() const { return new RgbF32ColorSpace(name(), profile()->clone()); } void RgbF32ColorSpace::colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const { const KoRgbF32Traits::Pixel *p = reinterpret_cast(pixel); QDomElement labElt = doc.createElement("RGB"); labElt.setAttribute("r", KisDomUtils::toString(KoColorSpaceMaths< KoRgbF32Traits::channels_type, qreal>::scaleToA(p->red))); labElt.setAttribute("g", KisDomUtils::toString(KoColorSpaceMaths< KoRgbF32Traits::channels_type, qreal>::scaleToA(p->green))); labElt.setAttribute("b", KisDomUtils::toString(KoColorSpaceMaths< KoRgbF32Traits::channels_type, qreal>::scaleToA(p->blue))); labElt.setAttribute("space", profile()->name()); colorElt.appendChild(labElt); } void RgbF32ColorSpace::colorFromXML(quint8 *pixel, const QDomElement &elt) const { KoRgbF32Traits::Pixel *p = reinterpret_cast(pixel); p->red = KoColorSpaceMaths< qreal, KoRgbF32Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("r"))); p->green = KoColorSpaceMaths< qreal, KoRgbF32Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("g"))); p->blue = KoColorSpaceMaths< qreal, KoRgbF32Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("b"))); p->alpha = 1.0; } void RgbF32ColorSpace::toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const { RGBToHSY(channelValues[0],channelValues[1],channelValues[2], hue, sat, luma, lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); } QVector RgbF32ColorSpace::fromHSY(qreal *hue, qreal *sat, qreal *luma) const { QVector channelValues(4); HSYToRGB(*hue, *sat, *luma, &channelValues[0],&channelValues[1],&channelValues[2], lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); channelValues[3]=1.0; return channelValues; } void RgbF32ColorSpace::toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const { RGBToYUV(channelValues[0],channelValues[1],channelValues[2], y, u, v, lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); } QVector RgbF32ColorSpace::fromYUV(qreal *y, qreal *u, qreal *v) const { QVector channelValues(4); YUVToRGB(*y, *u, *v, &channelValues[0],&channelValues[1],&channelValues[2], lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); channelValues[3]=1.0; return channelValues; } void RgbF32ColorSpace::fillGrayBrushWithColorAndLightnessOverlay(quint8 *dst, const QRgb *brush, quint8 *brushColor, qint32 nPixels) const { - fillGrayBrushWithColorPreserveLightnessRGB(dst, brush, brushColor, nPixels); + fillGrayBrushWithColorPreserveLightnessRGB(dst, brush, brushColor, 1.0, nPixels); +} + +void RgbF32ColorSpace::fillGrayBrushWithColorAndLightnessWithStrength(quint8* dst, const QRgb* brush, quint8* brushColor, qreal strength, qint32 nPixels) const +{ + fillGrayBrushWithColorPreserveLightnessRGB(dst, brush, brushColor, strength, nPixels); } diff --git a/plugins/color/lcms2engine/colorspaces/rgb_f32/RgbF32ColorSpace.h b/plugins/color/lcms2engine/colorspaces/rgb_f32/RgbF32ColorSpace.h index 89580dc3be..ce3e713a03 100644 --- a/plugins/color/lcms2engine/colorspaces/rgb_f32/RgbF32ColorSpace.h +++ b/plugins/color/lcms2engine/colorspaces/rgb_f32/RgbF32ColorSpace.h @@ -1,125 +1,126 @@ /* * Copyright (c) 2006 Cyrille Berger * * 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 * Library 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. */ #ifndef KORGBF32COLORSPACE_H_ #define KORGBF32COLORSPACE_H_ #include "LcmsColorSpace.h" #include "KoColorModelStandardIds.h" struct KoRgbF32Traits; class RgbF32ColorSpace : public LcmsColorSpace { public: RgbF32ColorSpace(const QString &name, KoColorProfile *p); bool willDegrade(ColorSpaceIndependence independence) const override; KoID colorModelId() const override { return RGBAColorModelID; } KoID colorDepthId() const override { return Float32BitsColorDepthID; } virtual KoColorSpace *clone() const; void colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const override; void colorFromXML(quint8* pixel, const QDomElement& elt) const override; void toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const override; QVector fromHSY(qreal *hue, qreal *sat, qreal *luma) const override; void toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const override; QVector fromYUV(qreal *y, qreal *u, qreal *v) const override; static QString colorSpaceId() { return QString("RGBAF32"); } bool hasHighDynamicRange() const override { return true; } void fillGrayBrushWithColorAndLightnessOverlay(quint8 *dst, const QRgb *brush, quint8 *brushColor, qint32 nPixels) const override; + void fillGrayBrushWithColorAndLightnessWithStrength(quint8* dst, const QRgb* brush, quint8* brushColor, qreal strength, qint32 nPixels) const override; }; class RgbF32ColorSpaceFactory : public LcmsColorSpaceFactory { public: RgbF32ColorSpaceFactory() : LcmsColorSpaceFactory(TYPE_RGBA_FLT, cmsSigRgbData) { } QString id() const override { return RgbF32ColorSpace::colorSpaceId(); } QString name() const override { return QString("%1 (%2)").arg(RGBAColorModelID.name()).arg(Float32BitsColorDepthID.name()); } bool userVisible() const override { return true; } KoID colorModelId() const override { return RGBAColorModelID; } KoID colorDepthId() const override { return Float32BitsColorDepthID; } int referenceDepth() const override { return 32; } KoColorSpace *createColorSpace(const KoColorProfile *p) const override { return new RgbF32ColorSpace(name(), p->clone()); } QString defaultProfile() const override { return "sRGB-elle-V2-g10.icc"; } bool isHdr() const override { return true; } }; #endif diff --git a/plugins/color/lcms2engine/colorspaces/rgb_u16/RgbU16ColorSpace.cpp b/plugins/color/lcms2engine/colorspaces/rgb_u16/RgbU16ColorSpace.cpp index 1656b77d00..40f1ed1dc3 100644 --- a/plugins/color/lcms2engine/colorspaces/rgb_u16/RgbU16ColorSpace.cpp +++ b/plugins/color/lcms2engine/colorspaces/rgb_u16/RgbU16ColorSpace.cpp @@ -1,114 +1,119 @@ /* * Copyright (c) 2006 Cyrille Berger * * 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 * Library 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 "RgbU16ColorSpace.h" #include #include #include "compositeops/KoCompositeOps.h" #include "compositeops/RgbCompositeOps.h" #include "kis_dom_utils.h" #include #include RgbU16ColorSpace::RgbU16ColorSpace(const QString &name, KoColorProfile *p) : LcmsColorSpace(colorSpaceId(), name, TYPE_BGRA_16, cmsSigRgbData, p) { addChannel(new KoChannelInfo(i18n("Blue"), 0 * sizeof(quint16), 2, KoChannelInfo::COLOR, KoChannelInfo::UINT16, 2, QColor(0, 0, 255))); addChannel(new KoChannelInfo(i18n("Green"), 1 * sizeof(quint16), 1, KoChannelInfo::COLOR, KoChannelInfo::UINT16, 2, QColor(0, 255, 0))); addChannel(new KoChannelInfo(i18n("Red"), 2 * sizeof(quint16), 0, KoChannelInfo::COLOR, KoChannelInfo::UINT16, 2, QColor(255, 0, 0))); addChannel(new KoChannelInfo(i18n("Alpha"), 3 * sizeof(quint16), 3, KoChannelInfo::ALPHA, KoChannelInfo::UINT16, 2)); init(); addStandardCompositeOps(this); addCompositeOp(new RgbCompositeOpIn(this)); addCompositeOp(new RgbCompositeOpOut(this)); addCompositeOp(new RgbCompositeOpBumpmap(this)); } bool RgbU16ColorSpace::willDegrade(ColorSpaceIndependence independence) const { if (independence == TO_RGBA8) { return true; } else { return false; } } KoColorSpace *RgbU16ColorSpace::clone() const { return new RgbU16ColorSpace(name(), profile()->clone()); } void RgbU16ColorSpace::colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const { const KoBgrU16Traits::Pixel *p = reinterpret_cast(pixel); QDomElement labElt = doc.createElement("RGB"); labElt.setAttribute("r", KisDomUtils::toString(KoColorSpaceMaths< KoBgrU16Traits::channels_type, qreal>::scaleToA(p->red))); labElt.setAttribute("g", KisDomUtils::toString(KoColorSpaceMaths< KoBgrU16Traits::channels_type, qreal>::scaleToA(p->green))); labElt.setAttribute("b", KisDomUtils::toString(KoColorSpaceMaths< KoBgrU16Traits::channels_type, qreal>::scaleToA(p->blue))); labElt.setAttribute("space", profile()->name()); colorElt.appendChild(labElt); } void RgbU16ColorSpace::colorFromXML(quint8 *pixel, const QDomElement &elt) const { KoBgrU16Traits::Pixel *p = reinterpret_cast(pixel); p->red = KoColorSpaceMaths< qreal, KoBgrU16Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("r"))); p->green = KoColorSpaceMaths< qreal, KoBgrU16Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("g"))); p->blue = KoColorSpaceMaths< qreal, KoBgrU16Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("b"))); p->alpha = KoColorSpaceMathsTraits::max; } void RgbU16ColorSpace::toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const { RGBToHSY(channelValues[0],channelValues[1],channelValues[2], hue, sat, luma, lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); } QVector RgbU16ColorSpace::fromHSY(qreal *hue, qreal *sat, qreal *luma) const { QVector channelValues(4); HSYToRGB(*hue, *sat, *luma, &channelValues[0],&channelValues[1],&channelValues[2], lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); channelValues[3]=1.0; return channelValues; } void RgbU16ColorSpace::toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const { RGBToYUV(channelValues[0],channelValues[1],channelValues[2], y, u, v, lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); } QVector RgbU16ColorSpace::fromYUV(qreal *y, qreal *u, qreal *v) const { QVector channelValues(4); YUVToRGB(*y, *u, *v, &channelValues[0],&channelValues[1],&channelValues[2], lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); channelValues[3]=1.0; return channelValues; } -void RgbU16ColorSpace::fillGrayBrushWithColorAndLightnessOverlay(quint8 *dst, const QRgb *brush, quint8 *brushColor, qint32 nPixels) const +void RgbU16ColorSpace::fillGrayBrushWithColorAndLightnessOverlay(quint8* dst, const QRgb* brush, quint8* brushColor, qint32 nPixels) const { - fillGrayBrushWithColorPreserveLightnessRGB(dst, brush, brushColor, nPixels); + fillGrayBrushWithColorPreserveLightnessRGB(dst, brush, brushColor, 1.0, nPixels); +} + +void RgbU16ColorSpace::fillGrayBrushWithColorAndLightnessWithStrength(quint8* dst, const QRgb* brush, quint8* brushColor, qreal strength, qint32 nPixels) const +{ + fillGrayBrushWithColorPreserveLightnessRGB(dst, brush, brushColor, strength, nPixels); } diff --git a/plugins/color/lcms2engine/colorspaces/rgb_u16/RgbU16ColorSpace.h b/plugins/color/lcms2engine/colorspaces/rgb_u16/RgbU16ColorSpace.h index c94559c4a2..7c37565c96 100644 --- a/plugins/color/lcms2engine/colorspaces/rgb_u16/RgbU16ColorSpace.h +++ b/plugins/color/lcms2engine/colorspaces/rgb_u16/RgbU16ColorSpace.h @@ -1,106 +1,107 @@ /* * Copyright (c) 2006 Cyrille Berger * * 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 * Library 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. */ #ifndef KORGBU16COLORSPACE_H_ #define KORGBU16COLORSPACE_H_ #include "LcmsColorSpace.h" #include "KoColorModelStandardIds.h" struct KoBgrU16Traits; class RgbU16ColorSpace : public LcmsColorSpace { public: RgbU16ColorSpace(const QString &name, KoColorProfile *p); bool willDegrade(ColorSpaceIndependence independence) const override; KoID colorModelId() const override { return RGBAColorModelID; } KoID colorDepthId() const override { return Integer16BitsColorDepthID; } virtual KoColorSpace *clone() const; void colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const override; void colorFromXML(quint8* pixel, const QDomElement& elt) const override; void toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const override; QVector fromHSY(qreal *hue, qreal *sat, qreal *luma) const override; void toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const override; QVector fromYUV(qreal *y, qreal *u, qreal *v) const override; static QString colorSpaceId() { return QString("RGBA16"); } void fillGrayBrushWithColorAndLightnessOverlay(quint8 *dst, const QRgb *brush, quint8 *brushColor, qint32 nPixels) const override; + void fillGrayBrushWithColorAndLightnessWithStrength(quint8* dst, const QRgb* brush, quint8* brushColor, qreal strength, qint32 nPixels) const override; }; class RgbU16ColorSpaceFactory : public LcmsColorSpaceFactory { public: RgbU16ColorSpaceFactory() : LcmsColorSpaceFactory(TYPE_BGRA_16, cmsSigRgbData) { } QString id() const override { return RgbU16ColorSpace::colorSpaceId(); } QString name() const override { return QString("%1 (%2)").arg(RGBAColorModelID.name()).arg(Integer16BitsColorDepthID.name()); } bool userVisible() const override { return true; } KoID colorModelId() const override { return RGBAColorModelID; } KoID colorDepthId() const override { return Integer16BitsColorDepthID; } int referenceDepth() const override { return 16; } KoColorSpace *createColorSpace(const KoColorProfile *p) const override { return new RgbU16ColorSpace(name(), p->clone()); } QString defaultProfile() const override { return "sRGB-elle-V2-g10.icc";//this is a linear space, because 16bit is enough to only enjoy advantages of linear space } }; #endif diff --git a/plugins/color/lcms2engine/colorspaces/rgb_u8/RgbU8ColorSpace.cpp b/plugins/color/lcms2engine/colorspaces/rgb_u8/RgbU8ColorSpace.cpp index ed1834fc11..cb6797e4f3 100644 --- a/plugins/color/lcms2engine/colorspaces/rgb_u8/RgbU8ColorSpace.cpp +++ b/plugins/color/lcms2engine/colorspaces/rgb_u8/RgbU8ColorSpace.cpp @@ -1,118 +1,123 @@ /* * Copyright (c) 2002 Patrick Julien * Copyright (c) 2004 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 program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "RgbU8ColorSpace.h" #include #include #include #include #include #include #include #include "compositeops/KoCompositeOps.h" #include "compositeops/RgbCompositeOps.h" #include #include #define downscale(quantum) (quantum) //((unsigned char) ((quantum)/257UL)) #define upscale(value) (value) // ((quint8) (257UL*(value))) RgbU8ColorSpace::RgbU8ColorSpace(const QString &name, KoColorProfile *p) : LcmsColorSpace(colorSpaceId(), name, TYPE_BGRA_8, cmsSigRgbData, p) { addChannel(new KoChannelInfo(i18n("Blue"), 0, 2, KoChannelInfo::COLOR, KoChannelInfo::UINT8, 1, QColor(0, 0, 255))); addChannel(new KoChannelInfo(i18n("Green"), 1, 1, KoChannelInfo::COLOR, KoChannelInfo::UINT8, 1, QColor(0, 255, 0))); addChannel(new KoChannelInfo(i18n("Red"), 2, 0, KoChannelInfo::COLOR, KoChannelInfo::UINT8, 1, QColor(255, 0, 0))); addChannel(new KoChannelInfo(i18n("Alpha"), 3, 3, KoChannelInfo::ALPHA, KoChannelInfo::UINT8)); init(); addStandardCompositeOps(this); addCompositeOp(new RgbCompositeOpIn(this)); addCompositeOp(new RgbCompositeOpOut(this)); addCompositeOp(new RgbCompositeOpBumpmap(this)); } KoColorSpace *RgbU8ColorSpace::clone() const { return new RgbU8ColorSpace(name(), profile()->clone()); } void RgbU8ColorSpace::colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const { const KoBgrU8Traits::Pixel *p = reinterpret_cast(pixel); QDomElement labElt = doc.createElement("RGB"); labElt.setAttribute("r", KisDomUtils::toString(KoColorSpaceMaths< KoBgrU8Traits::channels_type, qreal>::scaleToA(p->red))); labElt.setAttribute("g", KisDomUtils::toString(KoColorSpaceMaths< KoBgrU8Traits::channels_type, qreal>::scaleToA(p->green))); labElt.setAttribute("b", KisDomUtils::toString(KoColorSpaceMaths< KoBgrU8Traits::channels_type, qreal>::scaleToA(p->blue))); labElt.setAttribute("space", profile()->name()); colorElt.appendChild(labElt); } void RgbU8ColorSpace::colorFromXML(quint8 *pixel, const QDomElement &elt) const { KoBgrU8Traits::Pixel *p = reinterpret_cast(pixel); p->red = KoColorSpaceMaths< qreal, KoBgrU8Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("r"))); p->green = KoColorSpaceMaths< qreal, KoBgrU8Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("g"))); p->blue = KoColorSpaceMaths< qreal, KoBgrU8Traits::channels_type >::scaleToA(KisDomUtils::toDouble(elt.attribute("b"))); p->alpha = KoColorSpaceMathsTraits::max; } quint8 RgbU8ColorSpace::intensity8(const quint8 *src) const { const KoBgrU8Traits::Pixel *p = reinterpret_cast(src); return (quint8)(p->red * 0.30 + p->green * 0.59 + p->blue * 0.11); } void RgbU8ColorSpace::toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const { RGBToHSY(channelValues[0],channelValues[1],channelValues[2], hue, sat, luma, lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); } QVector RgbU8ColorSpace::fromHSY(qreal *hue, qreal *sat, qreal *luma) const { QVector channelValues(4); HSYToRGB(*hue, *sat, *luma, &channelValues[0],&channelValues[1],&channelValues[2], lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); channelValues[3]=1.0; return channelValues; } void RgbU8ColorSpace::toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const { RGBToYUV(channelValues[0],channelValues[1],channelValues[2], y, u, v, lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); } QVector RgbU8ColorSpace::fromYUV(qreal *y, qreal *u, qreal *v) const { QVector channelValues(4); YUVToRGB(*y, *u, *v, &channelValues[0],&channelValues[1],&channelValues[2], lumaCoefficients()[0], lumaCoefficients()[1], lumaCoefficients()[2]); channelValues[3]=1.0; return channelValues; } -void RgbU8ColorSpace::fillGrayBrushWithColorAndLightnessOverlay(quint8 *dst, const QRgb *brush, quint8 *brushColor, qint32 nPixels) const +void RgbU8ColorSpace::fillGrayBrushWithColorAndLightnessOverlay(quint8* dst, const QRgb* brush, quint8* brushColor, qint32 nPixels) const { - fillGrayBrushWithColorPreserveLightnessRGB(dst, brush, brushColor, nPixels); + fillGrayBrushWithColorPreserveLightnessRGB(dst, brush, brushColor, 1.0, nPixels); +} + +void RgbU8ColorSpace::fillGrayBrushWithColorAndLightnessWithStrength(quint8* dst, const QRgb* brush, quint8* brushColor, qreal strength, qint32 nPixels) const +{ + fillGrayBrushWithColorPreserveLightnessRGB(dst, brush, brushColor, strength, nPixels); } diff --git a/plugins/color/lcms2engine/colorspaces/rgb_u8/RgbU8ColorSpace.h b/plugins/color/lcms2engine/colorspaces/rgb_u8/RgbU8ColorSpace.h index 91c1208991..2fc124bcdc 100644 --- a/plugins/color/lcms2engine/colorspaces/rgb_u8/RgbU8ColorSpace.h +++ b/plugins/color/lcms2engine/colorspaces/rgb_u8/RgbU8ColorSpace.h @@ -1,118 +1,119 @@ /* * Copyright (c) 2002 Patrick Julien * * 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 program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KO_STRATEGY_COLORSPACE_RGB_H_ #define KO_STRATEGY_COLORSPACE_RGB_H_ #include #include #include "KoColorModelStandardIds.h" struct KoBgrU8Traits; class RgbU8ColorSpace : public LcmsColorSpace { public: RgbU8ColorSpace(const QString &name, KoColorProfile *p); bool willDegrade(ColorSpaceIndependence) const override { return false; } KoID colorModelId() const override { return RGBAColorModelID; } KoID colorDepthId() const override { return Integer8BitsColorDepthID; } virtual KoColorSpace *clone() const; void colorToXML(const quint8 *pixel, QDomDocument &doc, QDomElement &colorElt) const override; void colorFromXML(quint8 *pixel, const QDomElement &elt) const override; quint8 intensity8(const quint8 * src) const override; void toHSY(const QVector &channelValues, qreal *hue, qreal *sat, qreal *luma) const override; QVector fromHSY(qreal *hue, qreal *sat, qreal *luma) const override; void toYUV(const QVector &channelValues, qreal *y, qreal *u, qreal *v) const override; QVector fromYUV(qreal *y, qreal *u, qreal *v) const override; static QString colorSpaceId() { return QString("RGBA"); } void fillGrayBrushWithColorAndLightnessOverlay(quint8 *dst, const QRgb *brush, quint8 *brushColor, qint32 nPixels) const override; + void fillGrayBrushWithColorAndLightnessWithStrength(quint8* dst, const QRgb* brush, quint8* brushColor, qreal strength, qint32 nPixels) const override; }; class RgbU8ColorSpaceFactory : public LcmsColorSpaceFactory { public: RgbU8ColorSpaceFactory() : LcmsColorSpaceFactory(TYPE_BGRA_8, cmsSigRgbData) {} bool userVisible() const override { return true; } QString id() const override { return RgbU8ColorSpace::colorSpaceId(); } QString name() const override { return QString("%1 (%2)").arg(RGBAColorModelID.name()).arg(Integer8BitsColorDepthID.name()); } KoID colorModelId() const override { return RGBAColorModelID; } KoID colorDepthId() const override { return Integer8BitsColorDepthID; } int referenceDepth() const override { return 8; } KoColorSpace *createColorSpace(const KoColorProfile *p) const override { return new RgbU8ColorSpace(name(), p->clone()); } QString defaultProfile() const override { return "sRGB-elle-V2-srgbtrc.icc"; } }; #endif // KO_STRATEGY_COLORSPACE_RGB_H_ diff --git a/plugins/paintops/defaultpaintops/brush/KisBrushOpResources.cpp b/plugins/paintops/defaultpaintops/brush/KisBrushOpResources.cpp index 5da1c8ea48..b86d81250f 100644 --- a/plugins/paintops/defaultpaintops/brush/KisBrushOpResources.cpp +++ b/plugins/paintops/defaultpaintops/brush/KisBrushOpResources.cpp @@ -1,95 +1,96 @@ /* * Copyright (c) 2017 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KisBrushOpResources.h" #include #include #include "kis_color_source.h" #include "kis_pressure_mix_option.h" #include "kis_pressure_darken_option.h" #include "kis_pressure_hsv_option.h" #include "kis_color_source_option.h" #include "kis_pressure_sharpness_option.h" #include "kis_texture_option.h" #include "kis_painter.h" #include "kis_paintop_settings.h" struct KisBrushOpResources::Private { QList hsvOptions; KoColorTransformation *hsvTransformation = 0; KisPressureMixOption mixOption; KisPressureDarkenOption darkenOption; }; KisBrushOpResources::KisBrushOpResources(const KisPaintOpSettingsSP settings, KisPainter *painter) : m_d(new Private) { KisColorSourceOption colorSourceOption; colorSourceOption.readOptionSetting(settings); colorSource.reset(colorSourceOption.createColorSource(painter)); sharpnessOption.reset(new KisPressureSharpnessOption()); sharpnessOption->readOptionSetting(settings); sharpnessOption->resetAllSensors(); textureOption.reset(new KisTextureProperties(painter->device()->defaultBounds()->currentLevelOfDetail())); textureOption->fillProperties(settings); + textureOption->setTextureGradient(painter->gradient()); m_d->hsvOptions.append(KisPressureHSVOption::createHueOption()); m_d->hsvOptions.append(KisPressureHSVOption::createSaturationOption()); m_d->hsvOptions.append(KisPressureHSVOption::createValueOption()); Q_FOREACH (KisPressureHSVOption * option, m_d->hsvOptions) { option->readOptionSetting(settings); option->resetAllSensors(); if (option->isChecked() && !m_d->hsvTransformation) { m_d->hsvTransformation = painter->backgroundColor().colorSpace()->createColorTransformation("hsv_adjustment", QHash()); } } m_d->darkenOption.readOptionSetting(settings); m_d->mixOption.readOptionSetting(settings); m_d->darkenOption.resetAllSensors(); m_d->mixOption.resetAllSensors(); } KisBrushOpResources::~KisBrushOpResources() { qDeleteAll(m_d->hsvOptions); delete m_d->hsvTransformation; } void KisBrushOpResources::syncResourcesToSeqNo(int seqNo, const KisPaintInformation &info) { colorSource->selectColor(m_d->mixOption.apply(info), info); m_d->darkenOption.apply(colorSource.data(), info); if (m_d->hsvTransformation) { Q_FOREACH (KisPressureHSVOption * option, m_d->hsvOptions) { option->apply(m_d->hsvTransformation, info); } colorSource->applyColorTransformation(m_d->hsvTransformation); } KisDabCacheUtils::DabRenderingResources::syncResourcesToSeqNo(seqNo, info); } diff --git a/plugins/paintops/libpaintop/KisTextureMaskInfo.cpp b/plugins/paintops/libpaintop/KisTextureMaskInfo.cpp index 74ac1d9b1f..e3ff96875f 100644 --- a/plugins/paintops/libpaintop/KisTextureMaskInfo.cpp +++ b/plugins/paintops/libpaintop/KisTextureMaskInfo.cpp @@ -1,229 +1,247 @@ /* * Copyright (c) 2017 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "KisTextureMaskInfo.h" #include #include #include "kis_embedded_pattern_manager.h" #include #include #include #include #include #include /**********************************************************************/ /* KisTextureMaskInfo */ /**********************************************************************/ KisTextureMaskInfo::KisTextureMaskInfo(int levelOfDetail) : m_levelOfDetail(levelOfDetail) { } KisTextureMaskInfo::KisTextureMaskInfo(const KisTextureMaskInfo &rhs) : m_levelOfDetail(rhs.m_levelOfDetail), m_pattern(rhs.m_pattern), m_scale(rhs.m_scale), m_brightness(rhs.m_brightness), m_contrast(rhs.m_contrast), + m_neutralPoint(rhs.m_neutralPoint), m_invert(rhs.m_invert), m_cutoffLeft(rhs.m_cutoffLeft), m_cutoffRight(rhs.m_cutoffRight), m_cutoffPolicy(rhs.m_cutoffPolicy) { } KisTextureMaskInfo::~KisTextureMaskInfo() { } bool operator==(const KisTextureMaskInfo &lhs, const KisTextureMaskInfo &rhs) { return lhs.m_levelOfDetail == rhs.m_levelOfDetail && (lhs.m_pattern == rhs.m_pattern || (lhs.m_pattern && rhs.m_pattern && lhs.m_pattern->md5() == rhs.m_pattern->md5())) && qFuzzyCompare(lhs.m_scale, rhs.m_scale) && qFuzzyCompare(lhs.m_brightness, rhs.m_brightness) && qFuzzyCompare(lhs.m_contrast, rhs.m_contrast) && + qFuzzyCompare(lhs.m_neutralPoint, rhs.m_neutralPoint) && lhs.m_invert == rhs.m_invert && lhs.m_cutoffLeft == rhs.m_cutoffLeft && lhs.m_cutoffRight == rhs.m_cutoffRight && lhs.m_cutoffPolicy == rhs.m_cutoffPolicy; } KisTextureMaskInfo &KisTextureMaskInfo::operator=(const KisTextureMaskInfo &rhs) { m_levelOfDetail = rhs.m_levelOfDetail; m_pattern = rhs.m_pattern; m_scale = rhs.m_scale; m_brightness = rhs.m_brightness; m_contrast = rhs.m_contrast; + m_neutralPoint = rhs.m_neutralPoint; m_invert = rhs.m_invert; m_cutoffLeft = rhs.m_cutoffLeft; m_cutoffRight = rhs.m_cutoffRight; m_cutoffPolicy = rhs.m_cutoffPolicy; return *this; } int KisTextureMaskInfo::levelOfDetail() const { return m_levelOfDetail; } bool KisTextureMaskInfo::hasMask() const { return m_mask; } KisPaintDeviceSP KisTextureMaskInfo::mask() { return m_mask; } QRect KisTextureMaskInfo::maskBounds() const { return m_maskBounds; } bool KisTextureMaskInfo::fillProperties(const KisPropertiesConfigurationSP setting) { if (!setting->hasProperty("Texture/Pattern/PatternMD5")) { return false; } m_pattern = KisEmbeddedPatternManager::loadEmbeddedPattern(setting); if (!m_pattern) { warnKrita << "WARNING: Couldn't load the pattern for a stroke"; return false; } m_scale = setting->getDouble("Texture/Pattern/Scale", 1.0); m_brightness = setting->getDouble("Texture/Pattern/Brightness"); m_contrast = setting->getDouble("Texture/Pattern/Contrast", 1.0); + m_neutralPoint = setting->getDouble("Texture/Pattern/NeutralPoint", 0.5); m_invert = setting->getBool("Texture/Pattern/Invert"); m_cutoffLeft = setting->getInt("Texture/Pattern/CutoffLeft", 0); m_cutoffRight = setting->getInt("Texture/Pattern/CutoffRight", 255); m_cutoffPolicy = setting->getInt("Texture/Pattern/CutoffPolicy", 0); return true; } void KisTextureMaskInfo::recalculateMask() { if (!m_pattern) return; - const KoColorSpace *cs = KoColorSpaceRegistry::instance()->alpha8(); + const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8(); if (!m_mask) { m_mask = new KisPaintDevice(cs); } QImage mask = m_pattern->pattern(); if ((mask.format() != QImage::Format_RGB32) | (mask.format() != QImage::Format_ARGB32)) { mask = mask.convertToFormat(QImage::Format_ARGB32); } qreal scale = m_scale * KisLodTransform::lodToScale(m_levelOfDetail); if (!qFuzzyCompare(scale, 0.0)) { QTransform tf; tf.scale(scale, scale); QRect rc = KisAlgebra2D::ensureRectNotSmaller(tf.mapRect(mask.rect()), QSize(2,2)); mask = mask.scaled(rc.size(), Qt::KeepAspectRatio, Qt::SmoothTransformation); } - const QRgb* pixel = reinterpret_cast(mask.constBits()); + QRgb* pixel = reinterpret_cast(mask.bits()); const int width = mask.width(); const int height = mask.height(); - KisHLineIteratorSP iter = m_mask->createHLineIteratorNG(0, 0, width); + //KisHLineIteratorSP iter = m_mask->createHLineIteratorNG(0, 0, width); for (int row = 0; row < height; ++row) { for (int col = 0; col < width; ++col) { const QRgb currentPixel = pixel[row * width + col]; const int red = qRed(currentPixel); const int green = qGreen(currentPixel); const int blue = qBlue(currentPixel); float alpha = qAlpha(currentPixel) / 255.0; const int grayValue = (red * 11 + green * 16 + blue * 5) / 32; float maskValue = (grayValue / 255.0) * alpha + (1 - alpha); maskValue = maskValue - m_brightness; maskValue = ((maskValue - 0.5)*m_contrast)+0.5; if (maskValue > 1.0) {maskValue = 1;} else if (maskValue < 0) {maskValue = 0;} if (m_invert) { maskValue = 1 - maskValue; } if (m_cutoffPolicy == 1 && (maskValue < (m_cutoffLeft / 255.0) || maskValue > (m_cutoffRight / 255.0))) { // mask out the dab if it's outside the pattern's cuttoff points - maskValue = OPACITY_TRANSPARENT_F; + alpha = OPACITY_TRANSPARENT_F; } else if (m_cutoffPolicy == 2 && (maskValue < (m_cutoffLeft / 255.0) || maskValue > (m_cutoffRight / 255.0))) { - maskValue = OPACITY_OPAQUE_F; + alpha = OPACITY_OPAQUE_F; } - cs->setOpacity(iter->rawData(), maskValue, 1); - iter->nextPixel(); + maskValue = qBound(0.0f, maskValue, 1.0f); + + float neutralAdjustedValue; + + //Adjust neutral point in linear fashion. Uses separate linear equations from 0 to neutralPoint, and neutralPoint to 1, + //to prevent loss of detail (clipping). + if (m_neutralPoint == 1 || (m_neutralPoint !=0 && maskValue <= m_neutralPoint)) { + neutralAdjustedValue = maskValue / (2 * m_neutralPoint); + } + else { + neutralAdjustedValue = 0.5 + (maskValue-m_neutralPoint) / (2 - 2 * m_neutralPoint); + } + + int finalValue = neutralAdjustedValue * 255; + pixel[row * width + col] = QColor(finalValue, finalValue, finalValue, alpha * 255).rgba(); + } - iter->nextRow(); } + m_mask->convertFromQImage(mask, 0); m_maskBounds = QRect(0, 0, width, height); } /**********************************************************************/ /* KisTextureMaskInfoCache */ /**********************************************************************/ Q_GLOBAL_STATIC(KisTextureMaskInfoCache, s_instance) KisTextureMaskInfoCache *KisTextureMaskInfoCache::instance() { return s_instance; } KisTextureMaskInfoSP KisTextureMaskInfoCache::fetchCachedTextureInfo(KisTextureMaskInfoSP info) { QMutexLocker locker(&m_mutex); KisTextureMaskInfoSP &cachedInfo = info->levelOfDetail() > 0 ? m_lodInfo : m_mainInfo; if (!cachedInfo || *cachedInfo != *info) { cachedInfo = info; cachedInfo->recalculateMask(); } return cachedInfo; } diff --git a/plugins/paintops/libpaintop/KisTextureMaskInfo.h b/plugins/paintops/libpaintop/KisTextureMaskInfo.h index 9ac1834388..7ec9d60fae 100644 --- a/plugins/paintops/libpaintop/KisTextureMaskInfo.h +++ b/plugins/paintops/libpaintop/KisTextureMaskInfo.h @@ -1,89 +1,90 @@ /* * Copyright (c) 2017 Dmitry Kazakov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KISTEXTUREMASKINFO_H #define KISTEXTUREMASKINFO_H #include #include #include #include class KoPattern; class KisTextureMaskInfo; class KisTextureMaskInfo : public boost::equality_comparable { public: KisTextureMaskInfo(int levelOfDetail); KisTextureMaskInfo(const KisTextureMaskInfo &rhs); ~KisTextureMaskInfo(); friend bool operator==(const KisTextureMaskInfo &lhs, const KisTextureMaskInfo &rhs); KisTextureMaskInfo& operator=(const KisTextureMaskInfo &rhs); int levelOfDetail() const; bool hasMask() const; KisPaintDeviceSP mask(); QRect maskBounds() const; bool fillProperties(const KisPropertiesConfigurationSP setting); void recalculateMask(); private: int m_levelOfDetail = 0; KoPattern *m_pattern = 0; qreal m_scale = 1.0; qreal m_brightness = 0.0; qreal m_contrast = 1.0; + qreal m_neutralPoint = 0.5; bool m_invert = false; int m_cutoffLeft = 0; int m_cutoffRight = 255; int m_cutoffPolicy = 0; KisPaintDeviceSP m_mask; QRect m_maskBounds; }; typedef QSharedPointer KisTextureMaskInfoSP; struct KisTextureMaskInfoCache { static KisTextureMaskInfoCache *instance(); KisTextureMaskInfoSP fetchCachedTextureInfo(KisTextureMaskInfoSP info); private: QMutex m_mutex; QSharedPointer m_lodInfo; QSharedPointer m_mainInfo; }; #endif // KISTEXTUREMASKINFO_H diff --git a/plugins/paintops/libpaintop/forms/wdgtexturechooser.ui b/plugins/paintops/libpaintop/forms/wdgtexturechooser.ui index 51f417e805..2e617760e2 100644 --- a/plugins/paintops/libpaintop/forms/wdgtexturechooser.ui +++ b/plugins/paintops/libpaintop/forms/wdgtexturechooser.ui @@ -1,341 +1,361 @@ KisWdgTextureChooser 0 0 548 391 0 0 0 0 - 0 + 1 Texture 0 0 QFrame::StyledPanel QFrame::Plain 0 Options - - + + + + + 0 0 - Contrast: + Scale: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - 200 - 0 - + + + + + 0 + 0 + - - + + 0 0 - Cutoff + Contrast: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + 0 + 0 + + + + 0 0 Brightness: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - 200 - 0 - - - - - + 0 0 Cutoff Policy: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 Texturing Mode: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - 0 - 0 - - - - - - - - - 0 - 0 - + + + + + 200 + 0 + - - + + 0 0 - Scale: + Cutoff Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - + PushButton + + + + + 200 + 0 + + + + + + + + Neutral Point + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 200 + 0 + + + + 0 0 Horizontal Offset: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter The border of the brush will be smoothed to avoid aliasing Invert Pattern Random Offset 0 0 Vertical Offset: Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 0 0 Random Offset Qt::Vertical 20 40 KisDoubleSliderSpinBox QWidget -
kis_slider_spin_box.h
+
kis_slider_spin_box.h
1
KisSliderSpinBox QWidget
kis_slider_spin_box.h
1
KisPatternChooser QFrame
kis_pattern_chooser.h
1
KisGradientSlider QWidget
KisGradientSlider.h
1
KisMultipliersDoubleSliderSpinBox QWidget
kis_multipliers_double_slider_spinbox.h
1
diff --git a/plugins/paintops/libpaintop/kis_brush_based_paintop.cpp b/plugins/paintops/libpaintop/kis_brush_based_paintop.cpp index eca8aff271..9062a95669 100644 --- a/plugins/paintops/libpaintop/kis_brush_based_paintop.cpp +++ b/plugins/paintops/libpaintop/kis_brush_based_paintop.cpp @@ -1,185 +1,186 @@ /* * Copyright (c) 2008 Boudewijn Rempt * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_brush_based_paintop.h" #include "kis_properties_configuration.h" #include #include "kis_brush_option.h" #include #include #include "kis_painter.h" #include #include "kis_paintop_utils.h" #include "kis_paintop_plugin_utils.h" #include #include #include #ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND Q_GLOBAL_STATIC(TextBrushInitializationWorkaround, s_instance) TextBrushInitializationWorkaround *TextBrushInitializationWorkaround::instance() { return s_instance; } void TextBrushInitializationWorkaround::preinitialize(KisPropertiesConfigurationSP settings) { if (KisBrushOptionProperties::isTextBrush(settings.data())) { KisBrushOptionProperties brushOption; brushOption.readOptionSetting(settings); m_brush = brushOption.brush(); m_settings = settings; } else { m_brush = 0; m_settings = 0; } } KisBrushSP TextBrushInitializationWorkaround::tryGetBrush(const KisPropertiesConfigurationSP settings) { return (settings && settings == m_settings ? m_brush : 0); } TextBrushInitializationWorkaround::TextBrushInitializationWorkaround() : m_settings(0) {} TextBrushInitializationWorkaround::~TextBrushInitializationWorkaround() {} void KisBrushBasedPaintOp::preinitializeOpStatically(KisPaintOpSettingsSP settings) { TextBrushInitializationWorkaround::instance()->preinitialize(settings); } #endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */ KisBrushBasedPaintOp::KisBrushBasedPaintOp(const KisPropertiesConfigurationSP settings, KisPainter* painter) : KisPaintOp(painter), m_textureProperties(painter->device()->defaultBounds()->currentLevelOfDetail()) { Q_ASSERT(settings); #ifdef HAVE_THREADED_TEXT_RENDERING_WORKAROUND m_brush = TextBrushInitializationWorkaround::instance()->tryGetBrush(settings); #endif /* HAVE_THREADED_TEXT_RENDERING_WORKAROUND */ if (!m_brush) { KisBrushOptionProperties brushOption; brushOption.readOptionSetting(settings); m_brush = brushOption.brush(); } m_brush->notifyStrokeStarted(); m_precisionOption.readOptionSetting(settings); m_dabCache = new KisDabCache(m_brush); m_dabCache->setPrecisionOption(&m_precisionOption); m_mirrorOption.readOptionSetting(settings); m_dabCache->setMirrorPostprocessing(&m_mirrorOption); m_textureProperties.fillProperties(settings); m_dabCache->setTexturePostprocessing(&m_textureProperties); + m_textureProperties.setTextureGradient(painter->gradient()); m_precisionOption.setHasImprecisePositionOptions( m_precisionOption.hasImprecisePositionOptions() | m_mirrorOption.isChecked() | m_textureProperties.m_enabled); } KisBrushBasedPaintOp::~KisBrushBasedPaintOp() { delete m_dabCache; } bool KisBrushBasedPaintOp::checkSizeTooSmall(qreal scale) { scale *= m_brush->scale(); return KisPaintOpUtils::checkSizeTooSmall(scale, m_brush->width(), m_brush->height()); } KisSpacingInformation KisBrushBasedPaintOp::effectiveSpacing(qreal scale) const { // we parse dab rotation separately, so don't count it QSizeF metric = m_brush->characteristicSize(KisDabShape(scale, 1.0, 0)); return effectiveSpacing(metric.width(), metric.height(), 1.0, false, 0.0, false); } KisSpacingInformation KisBrushBasedPaintOp::effectiveSpacing(qreal scale, qreal rotation, const KisPaintInformation &pi) const { return effectiveSpacing(scale, rotation, nullptr, nullptr, pi); } KisSpacingInformation KisBrushBasedPaintOp::effectiveSpacing(qreal scale, qreal rotation, const KisPressureSpacingOption &spacingOption, const KisPaintInformation &pi) const { return effectiveSpacing(scale, rotation, nullptr, &spacingOption, pi); } KisSpacingInformation KisBrushBasedPaintOp::effectiveSpacing(qreal scale, qreal rotation, const KisAirbrushOptionProperties *airbrushOption, const KisPressureSpacingOption *spacingOption, const KisPaintInformation &pi) const { bool isotropicSpacing = spacingOption && spacingOption->isotropicSpacing(); MirrorProperties prop = m_mirrorOption.apply(pi); const bool implicitFlipped = prop.horizontalMirror != prop.verticalMirror; // we parse dab rotation separately, so don't count it QSizeF metric = m_brush->characteristicSize(KisDabShape(scale, 1.0, 0)); return KisPaintOpPluginUtils::effectiveSpacing(metric.width(), metric.height(), isotropicSpacing, rotation, implicitFlipped, m_brush->spacing(), m_brush->autoSpacingActive(), m_brush->autoSpacingCoeff(), KisLodTransform::lodToScale(painter()->device()), airbrushOption, spacingOption, pi); } KisSpacingInformation KisBrushBasedPaintOp::effectiveSpacing(qreal dabWidth, qreal dabHeight, qreal extraScale, bool isotropicSpacing, qreal rotation, bool axesFlipped) const { return KisPaintOpUtils::effectiveSpacing(dabWidth, dabHeight, extraScale, true, isotropicSpacing, rotation, axesFlipped, m_brush->spacing(), m_brush->autoSpacingActive(), m_brush->autoSpacingCoeff(), KisLodTransform::lodToScale(painter()->device())); } bool KisBrushBasedPaintOp::canPaint() const { return m_brush != 0; } diff --git a/plugins/paintops/libpaintop/kis_texture_chooser.cpp b/plugins/paintops/libpaintop/kis_texture_chooser.cpp index e3dbc1562e..652f489bad 100644 --- a/plugins/paintops/libpaintop/kis_texture_chooser.cpp +++ b/plugins/paintops/libpaintop/kis_texture_chooser.cpp @@ -1,69 +1,76 @@ /* * Copyright (c) 2017 Scott Petrovic * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kis_texture_chooser.h" #include "kis_texture_option.h" +#include "widgets/kis_gradient_chooser.h" + KisTextureChooser::KisTextureChooser(QWidget *parent) : QWidget(parent) { setupUi(this); textureSelectorWidget->setGrayscalePreview(true); textureSelectorWidget->setCurrentItem(0, 0); scaleSlider->setRange(0.0, 2.0, 2); scaleSlider->setValue(1.0); scaleSlider->addMultiplier(0.1); scaleSlider->addMultiplier(2); scaleSlider->addMultiplier(10); brightnessSlider->setRange(-1.0, 1.0, 2); brightnessSlider->setValue(0.0); brightnessSlider->setToolTip(i18n("Makes texture lighter or darker")); contrastSlider->setRange(0.0, 2.0, 2); contrastSlider->setValue(1.0); + neutralPointSlider->setRange(0.0, 1.0, 2); + neutralPointSlider->setValue(0.5); + neutralPointSlider->setToolTip(i18n("Set gray value to be considered neutral for lightness mode")); + offsetSliderX->setSuffix(i18n(" px")); offsetSliderY->setSuffix(i18n(" px")); QStringList texturingModes; - texturingModes << i18n("Multiply") << i18n("Subtract"); + texturingModes << i18n("Multiply Alpha") << i18n("Subtract Alpha") << i18n("Lightness Map") << i18n("Gradient Map"); cmbTexturingMode->addItems(texturingModes); cmbTexturingMode->setCurrentIndex(KisTextureProperties::SUBTRACT); QStringList cutOffPolicies; cutOffPolicies << i18n("Cut Off Disabled") << i18n("Cut Off Brush") << i18n("Cut Off Pattern"); cmbCutoffPolicy->addItems(cutOffPolicies); cutoffSlider->setMinimumSize(256, 30); cutoffSlider->enableGamma(false); cutoffSlider->setToolTip(i18n("When pattern texture values are outside the range specified" " by the slider, the cut-off policy will be applied.")); chkInvert->setChecked(false); + } KisTextureChooser::~KisTextureChooser() { } diff --git a/plugins/paintops/libpaintop/kis_texture_option.cpp b/plugins/paintops/libpaintop/kis_texture_option.cpp index 4a851c23a8..5a22346135 100644 --- a/plugins/paintops/libpaintop/kis_texture_option.cpp +++ b/plugins/paintops/libpaintop/kis_texture_option.cpp @@ -1,273 +1,360 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2012 * Copyright (C) Mohit Goyal , (C) 2014 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library 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 "kis_texture_option.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include +#include +#include #include #include #include #include #include #include #include "kis_embedded_pattern_manager.h" #include #include "kis_texture_chooser.h" +#include "KoMixColorsOp.h" #include KisTextureOption::KisTextureOption() : KisPaintOpOption(KisPaintOpOption::TEXTURE, true) , m_textureOptions(new KisTextureChooser()) { setObjectName("KisTextureOption"); setConfigurationPage(m_textureOptions); connect(m_textureOptions->textureSelectorWidget, SIGNAL(resourceSelected(KoResource*)), SLOT(resetGUI(KoResource*))); connect(m_textureOptions->textureSelectorWidget, SIGNAL(resourceSelected(KoResource*)), SLOT(emitSettingChanged())); connect(m_textureOptions->scaleSlider, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_textureOptions->brightnessSlider, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_textureOptions->contrastSlider, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); + connect(m_textureOptions->neutralPointSlider, SIGNAL(valueChanged(qreal)), SLOT(emitSettingChanged())); connect(m_textureOptions->offsetSliderX, SIGNAL(valueChanged(int)), SLOT(emitSettingChanged())); connect(m_textureOptions->randomOffsetX, SIGNAL(toggled(bool)), SLOT(emitSettingChanged())); connect(m_textureOptions->randomOffsetY, SIGNAL(toggled(bool)), SLOT(emitSettingChanged())); connect(m_textureOptions->offsetSliderY, SIGNAL(valueChanged(int)), SLOT(emitSettingChanged())); connect(m_textureOptions->cmbTexturingMode, SIGNAL(currentIndexChanged(int)), SLOT(emitSettingChanged())); connect(m_textureOptions->cmbCutoffPolicy, SIGNAL(currentIndexChanged(int)), SLOT(emitSettingChanged())); connect(m_textureOptions->cutoffSlider, SIGNAL(sigModifiedBlack(int)), SLOT(emitSettingChanged())); connect(m_textureOptions->cutoffSlider, SIGNAL(sigModifiedWhite(int)), SLOT(emitSettingChanged())); connect(m_textureOptions->chkInvert, SIGNAL(toggled(bool)), SLOT(emitSettingChanged())); resetGUI(m_textureOptions->textureSelectorWidget->currentResource()); } KisTextureOption::~KisTextureOption() { delete m_textureOptions; } void KisTextureOption::writeOptionSetting(KisPropertiesConfigurationSP setting) const { m_textureOptions->textureSelectorWidget->blockSignals(true); // Checking if (!m_textureOptions->textureSelectorWidget->currentResource()) return; KoPattern *pattern = static_cast(m_textureOptions->textureSelectorWidget->currentResource()); m_textureOptions->textureSelectorWidget->blockSignals(false); // Checking if (!pattern) return; setting->setProperty("Texture/Pattern/Enabled", isChecked()); if (!isChecked()) { return; } qreal scale = m_textureOptions->scaleSlider->value(); qreal brightness = m_textureOptions->brightnessSlider->value(); qreal contrast = m_textureOptions->contrastSlider->value(); + qreal neutralPoint = m_textureOptions->neutralPointSlider->value(); + int offsetX = m_textureOptions->offsetSliderX->value(); if (m_textureOptions ->randomOffsetX->isChecked()) { m_textureOptions->offsetSliderX ->setEnabled(false); m_textureOptions->offsetSliderX ->blockSignals(true); m_textureOptions->offsetSliderX ->setValue(offsetX); m_textureOptions->offsetSliderX ->blockSignals(false); } else { m_textureOptions->offsetSliderX ->setEnabled(true); } int offsetY = m_textureOptions->offsetSliderY->value(); if (m_textureOptions ->randomOffsetY->isChecked()) { m_textureOptions->offsetSliderY ->setEnabled(false); m_textureOptions->offsetSliderY ->blockSignals(true); m_textureOptions->offsetSliderY ->setValue(offsetY); m_textureOptions->offsetSliderY ->blockSignals(false); } else { m_textureOptions->offsetSliderY ->setEnabled(true); } int texturingMode = m_textureOptions->cmbTexturingMode->currentIndex(); - bool invert = (m_textureOptions->chkInvert->checkState() == Qt::Checked); + bool invert = (m_textureOptions->chkInvert->checkState() == Qt::Checked); setting->setProperty("Texture/Pattern/Scale", scale); setting->setProperty("Texture/Pattern/Brightness", brightness); setting->setProperty("Texture/Pattern/Contrast", contrast); + setting->setProperty("Texture/Pattern/NeutralPoint", neutralPoint); setting->setProperty("Texture/Pattern/OffsetX", offsetX); setting->setProperty("Texture/Pattern/OffsetY", offsetY); setting->setProperty("Texture/Pattern/TexturingMode", texturingMode); setting->setProperty("Texture/Pattern/CutoffLeft", m_textureOptions->cutoffSlider->black()); setting->setProperty("Texture/Pattern/CutoffRight", m_textureOptions->cutoffSlider->white()); setting->setProperty("Texture/Pattern/CutoffPolicy", m_textureOptions->cmbCutoffPolicy->currentIndex()); setting->setProperty("Texture/Pattern/Invert", invert); setting->setProperty("Texture/Pattern/MaximumOffsetX",m_textureOptions->offsetSliderX ->maximum()); setting->setProperty("Texture/Pattern/MaximumOffsetY",m_textureOptions->offsetSliderY ->maximum()); setting->setProperty("Texture/Pattern/isRandomOffsetX",m_textureOptions ->randomOffsetX ->isChecked()); setting->setProperty("Texture/Pattern/isRandomOffsetY",m_textureOptions ->randomOffsetY ->isChecked()); KisEmbeddedPatternManager::saveEmbeddedPattern(setting, pattern); } void KisTextureOption::readOptionSetting(const KisPropertiesConfigurationSP setting) { setChecked(setting->getBool("Texture/Pattern/Enabled")); if (!isChecked()) { return; } KoPattern *pattern = KisEmbeddedPatternManager::loadEmbeddedPattern(setting); if (!pattern) { pattern = static_cast(m_textureOptions->textureSelectorWidget->currentResource()); } m_textureOptions->textureSelectorWidget->setCurrentPattern(pattern); m_textureOptions->scaleSlider->setValue(setting->getDouble("Texture/Pattern/Scale", 1.0)); m_textureOptions->brightnessSlider->setValue(setting->getDouble("Texture/Pattern/Brightness")); m_textureOptions->contrastSlider->setValue(setting->getDouble("Texture/Pattern/Contrast", 1.0)); + m_textureOptions->neutralPointSlider->setValue(setting->getDouble("Texture/Pattern/NeutralPoint", 0.5)); m_textureOptions->offsetSliderX->setValue(setting->getInt("Texture/Pattern/OffsetX")); m_textureOptions->offsetSliderY->setValue(setting->getInt("Texture/Pattern/OffsetY")); m_textureOptions->randomOffsetX->setChecked(setting->getBool("Texture/Pattern/isRandomOffsetX")); m_textureOptions->randomOffsetY->setChecked(setting->getBool("Texture/Pattern/isRandomOffsetY")); m_textureOptions->cmbTexturingMode->setCurrentIndex(setting->getInt("Texture/Pattern/TexturingMode", KisTextureProperties::MULTIPLY)); m_textureOptions->cmbCutoffPolicy->setCurrentIndex(setting->getInt("Texture/Pattern/CutoffPolicy")); m_textureOptions->cutoffSlider->slotModifyBlack(setting->getInt("Texture/Pattern/CutoffLeft", 0)); m_textureOptions->cutoffSlider->slotModifyWhite(setting->getInt("Texture/Pattern/CutoffRight", 255)); - m_textureOptions->chkInvert->setChecked(setting->getBool("Texture/Pattern/Invert")); + m_textureOptions->chkInvert->setChecked(setting->getBool("Texture/Pattern/Invert")); } void KisTextureOption::lodLimitations(KisPaintopLodLimitations *l) const { l->limitations << KoID("texture-pattern", i18nc("PaintOp instant preview limitation", "Texture->Pattern (low quality preview)")); } void KisTextureOption::resetGUI(KoResource* res) { KoPattern *pattern = static_cast(res); if (!pattern) return; m_textureOptions->offsetSliderX->setRange(0, pattern->pattern().width() / 2); m_textureOptions->offsetSliderY->setRange(0, pattern->pattern().height() / 2); } /**********************************************************************/ /* KisTextureProperties */ /**********************************************************************/ KisTextureProperties::KisTextureProperties(int levelOfDetail) : m_levelOfDetail(levelOfDetail) { + QString gradientName = "Foreground to Background"; + KoResourceServer* rserver = KoResourceServerProvider::instance()->gradientServer(); + KoResource* gradient = rserver->resourceByName(gradientName); + m_gradient = dynamic_cast(gradient); } void KisTextureProperties::fillProperties(const KisPropertiesConfigurationSP setting) { if (!setting->hasProperty("Texture/Pattern/PatternMD5")) { m_enabled = false; return; } m_maskInfo = toQShared(new KisTextureMaskInfo(m_levelOfDetail)); if (!m_maskInfo->fillProperties(setting)) { warnKrita << "WARNING: Couldn't load the pattern for a stroke"; m_enabled = false; return; } m_maskInfo = KisTextureMaskInfoCache::instance()->fetchCachedTextureInfo(m_maskInfo); m_enabled = setting->getBool("Texture/Pattern/Enabled", false); m_offsetX = setting->getInt("Texture/Pattern/OffsetX"); m_offsetY = setting->getInt("Texture/Pattern/OffsetY"); m_texturingMode = (TexturingMode) setting->getInt("Texture/Pattern/TexturingMode", MULTIPLY); m_strengthOption.readOptionSetting(setting); m_strengthOption.resetAllSensors(); } +void KisTextureProperties::setTextureGradient(const KoAbstractGradient* gradient) { + if (gradient) { + m_gradient = gradient; + } +} + +//Convert a pixel to a QColor. We need this instead of calling ColorSpace::toQColor() because toQColor() is extremely slow, +//even though it works pretty much exactly the same way... +void KisTextureProperties::createQColorFromPixel(QColor& dest, const quint8* pixel, const KoColorSpace *cs) { + QVector channelValuesF(4); + + //Probably doesn't need to check for RGBA, because the only time we call it is for + //the mask, which we explicitly set as rbg8 colorspace before calling this. It's here from previous testing + //where it was necessary, and left in just in case it's needed in the future. + QString csid = cs->id(); + if (csid.contains("RGBA")) { + cs->normalisedChannelsValue(pixel, channelValuesF); + } + else { + const int rgbPixelSize = sizeof(KoBgrU16Traits::Pixel); + quint8* pixelRGBA = new quint8[rgbPixelSize]; + cs->toRgbA16(pixel, pixelRGBA, 1); + KoColorSpaceTrait::normalisedChannelsValue(pixelRGBA, channelValuesF); + delete pixelRGBA; + } + dest.setRgbF(channelValuesF[2], channelValuesF[1], channelValuesF[0], channelValuesF[3]); +} + void KisTextureProperties::apply(KisFixedPaintDeviceSP dab, const QPoint &offset, const KisPaintInformation & info) { if (!m_enabled) return; - KisPaintDeviceSP fillDevice = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8()); + KisPaintDeviceSP fillAlphaDevice = new KisPaintDevice(KoColorSpaceRegistry::instance()->alpha8()); + KisPaintDeviceSP fillMaskDevice = new KisPaintDevice(KoColorSpaceRegistry::instance()->rgb8()); QRect rect = dab->bounds(); KisPaintDeviceSP mask = m_maskInfo->mask(); const QRect maskBounds = m_maskInfo->maskBounds(); KIS_SAFE_ASSERT_RECOVER_RETURN(mask); int x = offset.x() % maskBounds.width() - m_offsetX; int y = offset.y() % maskBounds.height() - m_offsetY; - KisFillPainter fillPainter(fillDevice); - fillPainter.fillRect(x - 1, y - 1, rect.width() + 2, rect.height() + 2, mask, maskBounds); - fillPainter.end(); + if (m_texturingMode == LIGHTNESS) { + KisFillPainter fillMaskPainter(fillMaskDevice); + fillMaskPainter.fillRect(x - 1, y - 1, rect.width() + 2, rect.height() + 2, mask, maskBounds); + fillMaskPainter.end(); + } + + KisFillPainter fillAlphaPainter(fillAlphaDevice); + fillAlphaPainter.fillRect(x - 1, y - 1, rect.width() + 2, rect.height() + 2, mask, maskBounds); + fillAlphaPainter.end(); qreal pressure = m_strengthOption.apply(info); quint8 *dabData = dab->data(); - KisHLineIteratorSP iter = fillDevice->createHLineIteratorNG(x, y, rect.width()); + //for gradient textures... + KoMixColorsOp *colorMix = dab->colorSpace()->mixColorsOp(); + qint16 colorWeights[2]; + quint8* colors[2]; + + KisHLineIteratorSP iter = fillAlphaDevice->createHLineIteratorNG(x, y, rect.width()); + KisHLineIteratorSP lightIter = fillMaskDevice->createHLineIteratorNG(x, y, rect.width()); for (int row = 0; row < rect.height(); ++row) { for (int col = 0; col < rect.width(); ++col) { if (m_texturingMode == MULTIPLY) { dab->colorSpace()->multiplyAlpha(dabData, quint8(*iter->oldRawData() * pressure), 1); } - else { + else if (m_texturingMode == SUBTRACT) { int pressureOffset = (1.0 - pressure) * 255; qint16 maskA = *iter->oldRawData() + pressureOffset; quint8 dabA = dab->colorSpace()->opacityU8(dabData); dabA = qMax(0, (qint16)dabA - maskA); dab->colorSpace()->setOpacity(dabData, dabA, 1); } - + else if (m_texturingMode == LIGHTNESS) { + //int pressureOffset = (1.0 - pressure) * 255; + const quint8* maskData = lightIter->oldRawData(); // +pressureOffset; + QColor maskColor; + createQColorFromPixel(maskColor, maskData, mask->colorSpace()); + + QRgb maskQRgb = maskColor.rgba(); + dab->colorSpace()->fillGrayBrushWithColorAndLightnessWithStrength(dabData, &maskQRgb, dabData, pressure, 1); + } + else { + if (m_gradient && m_gradient->valid()) { + KoColor paintcolor(m_gradient->colorSpace()); + qreal gradientvalue = qreal(*iter->oldRawData()) / 255.0; + m_gradient->colorAt(paintcolor, gradientvalue); + paintcolor.setOpacity(qMin(paintcolor.opacityF(), dab->colorSpace()->opacityF(dabData))); + paintcolor.convertTo(dab->colorSpace(), KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); + colorWeights[0] = pressure * 255; + colorWeights[1] = (1.0 - pressure) * 255; + colors[0] = paintcolor.data(); + KoColor dabColor(dabData, dab->colorSpace()); + colors[1] = dabColor.data(); + colorMix->mixColors(colors, colorWeights, 2, dabData); + + /*paintcolor.colorSpace()->convertPixelsTo(paintcolor.data(), finalColor->data(), dab->colorSpace(), 1, + KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags());*/ + + + //for (int i = 0; i < dab->pixelSize(); i++) { + // dabData[i] = paintcolor.data()[i]; //should be fine, since they use the same color space... + //} + } + } + lightIter->nextPixel(); iter->nextPixel(); dabData += dab->pixelSize(); } + lightIter->nextRow(); iter->nextRow(); } } diff --git a/plugins/paintops/libpaintop/kis_texture_option.h b/plugins/paintops/libpaintop/kis_texture_option.h index 90a6194708..23ccc0e031 100644 --- a/plugins/paintops/libpaintop/kis_texture_option.h +++ b/plugins/paintops/libpaintop/kis_texture_option.h @@ -1,98 +1,108 @@ /* This file is part of the KDE project * Copyright (C) Boudewijn Rempt , (C) 2012 * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library 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. */ #ifndef KIS_TEXTURE_OPTION_H #define KIS_TEXTURE_OPTION_H #include #include #include #include "kis_paintop_option.h" #include "kis_pressure_texture_strength_option.h" +#include #include "KisTextureMaskInfo.h" #include class KisTextureChooser; class KoPattern; class KoResource; class KisPropertiesConfiguration; class KisPaintopLodLimitations; class PAINTOP_EXPORT KisTextureOption : public KisPaintOpOption { Q_OBJECT public: explicit KisTextureOption(); ~KisTextureOption() override; public Q_SLOTS: void writeOptionSetting(KisPropertiesConfigurationSP setting) const override; void readOptionSetting(const KisPropertiesConfigurationSP setting) override; void lodLimitations(KisPaintopLodLimitations *l) const override; private Q_SLOTS: void resetGUI(KoResource*); /// called when a new pattern is selected + private: /// UI Widget that stores all the texture options KisTextureChooser* m_textureOptions; }; class PAINTOP_EXPORT KisTextureProperties { public: KisTextureProperties(int levelOfDetail); enum TexturingMode { MULTIPLY, - SUBTRACT + SUBTRACT, + LIGHTNESS, + GRADIENT }; bool m_enabled; /** * @brief apply combine the texture map with the dab * @param dab the colored, final representation of the dab, after mirroring and everything. * @param offset the position of the dab on the image. used to calculate the position of the mask pattern * @param info the paint information */ void apply(KisFixedPaintDeviceSP dab, const QPoint& offset, const KisPaintInformation & info); void fillProperties(const KisPropertiesConfigurationSP setting); + void setTextureGradient(const KoAbstractGradient* gradient); + +private: + + void createQColorFromPixel(QColor& dest, const quint8* pixel, const KoColorSpace *cs); private: int m_offsetX; int m_offsetY; TexturingMode m_texturingMode; + const KoAbstractGradient* m_gradient; int m_levelOfDetail; private: KisPressureTextureStrengthOption m_strengthOption; KisTextureMaskInfoSP m_maskInfo; }; #endif // KIS_TEXTURE_OPTION_H