diff --git a/libs/pigment/KoColorConversionTransformation.h b/libs/pigment/KoColorConversionTransformation.h index d89503d053..36cef9b3bc 100644 --- a/libs/pigment/KoColorConversionTransformation.h +++ b/libs/pigment/KoColorConversionTransformation.h @@ -1,147 +1,148 @@ /* * Copyright (c) 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 _KO_COLOR_CONVERSION_TRANSFORMATION_H_ #define _KO_COLOR_CONVERSION_TRANSFORMATION_H_ #include "KoColorTransformation.h" #include "kritapigment_export.h" class KoColorSpace; class KoColorConversionCache; /** * This is the base class of all color transform that convert the color of a pixel */ class KRITAPIGMENT_EXPORT KoColorConversionTransformation : public KoColorTransformation { friend class KoColorConversionCache; struct Private; public: /** * Possible value for the intent of a color conversion (useful only for ICC * transformations) */ enum Intent { IntentPerceptual = 0, IntentRelativeColorimetric = 1, IntentSaturation = 2, IntentAbsoluteColorimetric = 3 }; /** * Flags for the color conversion, see lcms2 documentation for more information */ enum ConversionFlag { Empty = 0x0, NoOptimization = 0x0100, GamutCheck = 0x1000, // Out of Gamut alarm SoftProofing = 0x4000, // Do softproofing BlackpointCompensation = 0x2000, NoWhiteOnWhiteFixup = 0x0004, // Don't fix scum dot HighQuality = 0x0400, // Use more memory to give better accuracy - LowQuality = 0x0800 // Use less memory to minimize resources + LowQuality = 0x0800, // Use less memory to minimize resources + CopyAlpha = 0x04000000 //Let LCMS handle the alpha. Should always be on. }; Q_DECLARE_FLAGS(ConversionFlags, ConversionFlag) /** * We have numerous places where we need to convert color spaces. * * In several cases the user asks us about the conversion * explicitly, e.g. when changing the image type or converting * pixel data to the monitor profile. Doing this explicitly the * user can choose what rendering intent and conversion flags to * use. * * But there are also cases when we have to do a conversion * internally (transparently for the user), for example, when * merging heterogeneous images, creating thumbnails, converting * data to/from QImage or while doing some adjustments. We cannot * ask the user about parameters for every single * conversion. That's why in all these non-critical cases the * following default values should be used. */ static Intent internalRenderingIntent() { return IntentPerceptual; } static ConversionFlags internalConversionFlags() { return BlackpointCompensation; } static Intent adjustmentRenderingIntent() { return IntentPerceptual; } static ConversionFlags adjustmentConversionFlags() { return ConversionFlags(BlackpointCompensation | NoWhiteOnWhiteFixup); } public: KoColorConversionTransformation(const KoColorSpace* srcCs, const KoColorSpace* dstCs, Intent renderingIntent, ConversionFlags conversionFlags); ~KoColorConversionTransformation() override; public: /** * @return the source color space for this transformation. */ const KoColorSpace* srcColorSpace() const; /** * @return the destination color space for this transformation. */ const KoColorSpace* dstColorSpace() const; /** * @return the rendering intent of this transformation (this is only useful * for ICC transformations) */ Intent renderingIntent() const; /** * @return the conversion flags */ ConversionFlags conversionFlags() const; /** * perform the color conversion between two buffers. Make sure that * \p src is not the same as \p dst! * @param nPixels the number of pixels in the buffers. */ void transform(const quint8 *src, quint8 *dst, qint32 nPixels) const override = 0; /** * perform the color conversion between two or one buffer. This is a convenience * function that allows doing the conversion in-place * @param nPixels the number of pixels in the buffers. */ void transformInPlace(const quint8 *src, quint8 *dst, qint32 nPixels) const; /** * @return false if the transformation is not valid */ bool isValid() const override { return true; } private: void setSrcColorSpace(const KoColorSpace*) const; void setDstColorSpace(const KoColorSpace*) const; Private * const d; }; Q_DECLARE_OPERATORS_FOR_FLAGS(KoColorConversionTransformation::ConversionFlags) #endif diff --git a/plugins/color/lcms2engine/IccColorSpaceEngine.cpp b/plugins/color/lcms2engine/IccColorSpaceEngine.cpp index a731057554..250ef94989 100644 --- a/plugins/color/lcms2engine/IccColorSpaceEngine.cpp +++ b/plugins/color/lcms2engine/IccColorSpaceEngine.cpp @@ -1,342 +1,318 @@ /* * Copyright (c) 2007-2008 Cyrille Berger * Copyright (c) 2011 Srikanth Tiyyagura * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "IccColorSpaceEngine.h" #include "KoColorModelStandardIds.h" #include #include "LcmsColorSpace.h" // -- KoLcmsColorConversionTransformation -- class KoLcmsColorConversionTransformation : public KoColorConversionTransformation { public: KoLcmsColorConversionTransformation(const KoColorSpace *srcCs, quint32 srcColorSpaceType, LcmsColorProfileContainer *srcProfile, const KoColorSpace *dstCs, quint32 dstColorSpaceType, LcmsColorProfileContainer *dstProfile, Intent renderingIntent, ConversionFlags conversionFlags) : KoColorConversionTransformation(srcCs, dstCs, renderingIntent, conversionFlags) , m_transform(0) { Q_ASSERT(srcCs); Q_ASSERT(dstCs); Q_ASSERT(renderingIntent < 4); if (srcCs->colorDepthId() == Integer8BitsColorDepthID || srcCs->colorDepthId() == Integer16BitsColorDepthID) { if ((srcProfile->name().contains(QLatin1String("linear"), Qt::CaseInsensitive) || dstProfile->name().contains(QLatin1String("linear"), Qt::CaseInsensitive)) && !conversionFlags.testFlag(KoColorConversionTransformation::NoOptimization)) { conversionFlags |= KoColorConversionTransformation::NoOptimization; } } + conversionFlags |= KoColorConversionTransformation::CopyAlpha; m_transform = cmsCreateTransform(srcProfile->lcmsProfile(), srcColorSpaceType, dstProfile->lcmsProfile(), dstColorSpaceType, renderingIntent, conversionFlags); Q_ASSERT(m_transform); } ~KoLcmsColorConversionTransformation() override { cmsDeleteTransform(m_transform); } public: void transform(const quint8 *src, quint8 *dst, qint32 numPixels) const override { Q_ASSERT(m_transform); - qint32 srcPixelSize = srcColorSpace()->pixelSize(); - qint32 dstPixelSize = dstColorSpace()->pixelSize(); - cmsDoTransform(m_transform, const_cast(src), dst, numPixels); - // Lcms does nothing to the destination alpha channel so we must convert that manually. - while (numPixels > 0) { - qreal alpha = srcColorSpace()->opacityF(src); - dstColorSpace()->setOpacity(dst, alpha, 1); - - src += srcPixelSize; - dst += dstPixelSize; - numPixels--; - } } private: mutable cmsHTRANSFORM m_transform; }; class KoLcmsColorProofingConversionTransformation : public KoColorProofingConversionTransformation { public: KoLcmsColorProofingConversionTransformation(const KoColorSpace *srcCs, quint32 srcColorSpaceType, LcmsColorProfileContainer *srcProfile, const KoColorSpace *dstCs, quint32 dstColorSpaceType, LcmsColorProfileContainer *dstProfile, const KoColorSpace *proofingSpace, Intent renderingIntent, Intent proofingIntent, ConversionFlags conversionFlags, quint8 *gamutWarning, double adaptationState ) : KoColorProofingConversionTransformation(srcCs, dstCs, proofingSpace, renderingIntent, proofingIntent, conversionFlags, gamutWarning, adaptationState) , m_transform(0) { Q_ASSERT(srcCs); Q_ASSERT(dstCs); Q_ASSERT(renderingIntent < 4); if (srcCs->colorDepthId() == Integer8BitsColorDepthID || srcCs->colorDepthId() == Integer16BitsColorDepthID) { if ((srcProfile->name().contains(QLatin1String("linear"), Qt::CaseInsensitive) || dstProfile->name().contains(QLatin1String("linear"), Qt::CaseInsensitive)) && !conversionFlags.testFlag(KoColorConversionTransformation::NoOptimization)) { conversionFlags |= KoColorConversionTransformation::NoOptimization; } } + conversionFlags |= KoColorConversionTransformation::CopyAlpha; quint16 alarm[cmsMAXCHANNELS];//this seems to be bgr??? alarm[0] = (cmsUInt16Number)gamutWarning[2]*256; alarm[1] = (cmsUInt16Number)gamutWarning[1]*256; alarm[2] = (cmsUInt16Number)gamutWarning[0]*256; cmsSetAlarmCodes(alarm); cmsSetAdaptationState(adaptationState); m_transform = cmsCreateProofingTransform(srcProfile->lcmsProfile(), srcColorSpaceType, dstProfile->lcmsProfile(), dstColorSpaceType, dynamic_cast(proofingSpace->profile())->asLcms()->lcmsProfile(), renderingIntent, proofingIntent, conversionFlags); cmsSetAdaptationState(1); Q_ASSERT(m_transform); } ~KoLcmsColorProofingConversionTransformation() override { cmsDeleteTransform(m_transform); } public: void transform(const quint8 *src, quint8 *dst, qint32 numPixels) const override { Q_ASSERT(m_transform); - qint32 srcPixelSize = srcColorSpace()->pixelSize(); - qint32 dstPixelSize = dstColorSpace()->pixelSize(); - //cmsSetAdaptationState(0); - cmsDoTransform(m_transform, const_cast(src), dst, numPixels); - // Lcms does nothing to the destination alpha channel so we must convert that manually. - while (numPixels > 0) { - qreal alpha = srcColorSpace()->opacityF(src); - dstColorSpace()->setOpacity(dst, alpha, 1); - - src += srcPixelSize; - dst += dstPixelSize; - numPixels--; - } - //cmsSetAdaptationState(1); } private: mutable cmsHTRANSFORM m_transform; }; struct IccColorSpaceEngine::Private { }; IccColorSpaceEngine::IccColorSpaceEngine() : KoColorSpaceEngine("icc", i18n("ICC Engine")), d(new Private) { } IccColorSpaceEngine::~IccColorSpaceEngine() { delete d; } const KoColorProfile* IccColorSpaceEngine::addProfile(const QString &filename) { KoColorSpaceRegistry *registry = KoColorSpaceRegistry::instance(); KoColorProfile *profile = new IccColorProfile(filename); Q_CHECK_PTR(profile); // this our own loading code; sometimes it fails because of an lcms error profile->load(); // and then lcms can read the profile from file itself without problems, // quite often, and we can initialize it if (!profile->valid()) { cmsHPROFILE cmsp = cmsOpenProfileFromFile(filename.toLatin1(), "r"); profile = LcmsColorProfileContainer::createFromLcmsProfile(cmsp); } if (profile->valid()) { dbgPigment << "Valid profile : " << profile->fileName() << profile->name(); registry->addProfile(profile); } else { dbgPigment << "Invalid profile : " << profile->fileName() << profile->name(); delete profile; profile = 0; } return profile; } const KoColorProfile* IccColorSpaceEngine::addProfile(const QByteArray &data) { KoColorSpaceRegistry *registry = KoColorSpaceRegistry::instance(); KoColorProfile *profile = new IccColorProfile(data); Q_CHECK_PTR(profile); if (profile->valid()) { dbgPigment << "Valid profile : " << profile->fileName() << profile->name(); registry->addProfile(profile); } else { dbgPigment << "Invalid profile : " << profile->fileName() << profile->name(); delete profile; profile = 0; } return profile; } void IccColorSpaceEngine::removeProfile(const QString &filename) { KoColorSpaceRegistry *registry = KoColorSpaceRegistry::instance(); KoColorProfile *profile = new IccColorProfile(filename); Q_CHECK_PTR(profile); profile->load(); if (profile->valid() && registry->profileByName(profile->name())) { registry->removeProfile(profile); } } KoColorConversionTransformation *IccColorSpaceEngine::createColorTransformation(const KoColorSpace *srcColorSpace, const KoColorSpace *dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const { Q_ASSERT(srcColorSpace); Q_ASSERT(dstColorSpace); return new KoLcmsColorConversionTransformation( srcColorSpace, computeColorSpaceType(srcColorSpace), dynamic_cast(srcColorSpace->profile())->asLcms(), dstColorSpace, computeColorSpaceType(dstColorSpace), dynamic_cast(dstColorSpace->profile())->asLcms(), renderingIntent, conversionFlags); } KoColorProofingConversionTransformation *IccColorSpaceEngine::createColorProofingTransformation(const KoColorSpace *srcColorSpace, const KoColorSpace *dstColorSpace, const KoColorSpace *proofingSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::Intent proofingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags, quint8 *gamutWarning, double adaptationState) const { Q_ASSERT(srcColorSpace); Q_ASSERT(dstColorSpace); return new KoLcmsColorProofingConversionTransformation( srcColorSpace, computeColorSpaceType(srcColorSpace), dynamic_cast(srcColorSpace->profile())->asLcms(), dstColorSpace, computeColorSpaceType(dstColorSpace), dynamic_cast(dstColorSpace->profile())->asLcms(), proofingSpace, renderingIntent, proofingIntent, conversionFlags, gamutWarning, adaptationState ); } quint32 IccColorSpaceEngine::computeColorSpaceType(const KoColorSpace *cs) const { Q_ASSERT(cs); if (const KoLcmsInfo *lcmsInfo = dynamic_cast(cs)) { return lcmsInfo->colorSpaceType(); } else { QString modelId = cs->colorModelId().id(); QString depthId = cs->colorDepthId().id(); // Compute the depth part of the type quint32 depthType; if (depthId == Integer8BitsColorDepthID.id()) { depthType = BYTES_SH(1); } else if (depthId == Integer16BitsColorDepthID.id()) { depthType = BYTES_SH(2); } else if (depthId == Float16BitsColorDepthID.id()) { depthType = BYTES_SH(2); } else if (depthId == Float32BitsColorDepthID.id()) { depthType = BYTES_SH(4); } else if (depthId == Float64BitsColorDepthID.id()) { depthType = BYTES_SH(0); } else { qWarning() << "Unknown bit depth"; return 0; } // Compute the model part of the type quint32 modelType = 0; if (modelId == RGBAColorModelID.id()) { if (depthId.startsWith(QLatin1Char('U'))) { modelType = (COLORSPACE_SH(PT_RGB) | EXTRA_SH(1) | CHANNELS_SH(3) | DOSWAP_SH(1) | SWAPFIRST_SH(1)); } else if (depthId.startsWith(QLatin1Char('F'))) { modelType = (COLORSPACE_SH(PT_RGB) | EXTRA_SH(1) | CHANNELS_SH(3)); } } else if (modelId == XYZAColorModelID.id()) { modelType = (COLORSPACE_SH(PT_XYZ) | EXTRA_SH(1) | CHANNELS_SH(3)); } else if (modelId == LABAColorModelID.id()) { modelType = (COLORSPACE_SH(PT_Lab) | EXTRA_SH(1) | CHANNELS_SH(3)); } else if (modelId == CMYKAColorModelID.id()) { modelType = (COLORSPACE_SH(PT_CMYK) | EXTRA_SH(1) | CHANNELS_SH(4)); } else if (modelId == GrayAColorModelID.id()) { modelType = (COLORSPACE_SH(PT_GRAY) | EXTRA_SH(1) | CHANNELS_SH(1)); } else if (modelId == GrayColorModelID.id()) { modelType = (COLORSPACE_SH(PT_GRAY) | CHANNELS_SH(1)); } else if (modelId == YCbCrAColorModelID.id()) { modelType = (COLORSPACE_SH(PT_YCbCr) | EXTRA_SH(1) | CHANNELS_SH(3)); } else { qWarning() << "Cannot convert colorspace to lcms modeltype"; return 0; } return depthType | modelType; } } bool IccColorSpaceEngine::supportsColorSpace(const QString &colorModelId, const QString &colorDepthId, const KoColorProfile *profile) const { Q_UNUSED(colorDepthId); return colorModelId != RGBAColorModelID.id() || !profile || profile->name() != "High Dynamic Range UHDTV Wide Color Gamut Display (Rec. 2020) - SMPTE ST 2084 PQ EOTF"; }