IccColorSpaceEngine.cpp 13.1 KB
Newer Older
1 2
/*
 *  Copyright (c) 2007-2008 Cyrille Berger <cberger@cberger.net>
3
 *  Copyright (c) 2011 Srikanth Tiyyagura <srikanth.tulasiram@gmail.com>
4 5 6 7
 *
 * 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
8
 * version 2.1 of the License, or (at your option) any later version.
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
 *
 * 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"

25
#include <klocalizedstring.h>
26 27 28

#include "LcmsColorSpace.h"

29
#include <QDebug>
30 31 32 33 34 35

// -- KoLcmsColorConversionTransformation --

class KoLcmsColorConversionTransformation : public KoColorConversionTransformation
{
public:
36 37
    KoLcmsColorConversionTransformation(const KoColorSpace *srcCs, quint32 srcColorSpaceType, LcmsColorProfileContainer *srcProfile,
                                        const KoColorSpace *dstCs, quint32 dstColorSpaceType, LcmsColorProfileContainer *dstProfile,
38 39 40
                                        Intent renderingIntent,
                                        ConversionFlags conversionFlags)
        : KoColorConversionTransformation(srcCs, dstCs, renderingIntent, conversionFlags)
41
        , m_transform(0)
42 43 44
    {
        Q_ASSERT(srcCs);
        Q_ASSERT(dstCs);
45
        Q_ASSERT(renderingIntent < 4);
46 47 48 49

        if (srcCs->colorDepthId() == Integer8BitsColorDepthID
                || srcCs->colorDepthId() == Integer16BitsColorDepthID) {

50
            if ((srcProfile->name().contains(QLatin1String("linear"), Qt::CaseInsensitive) ||
51
                 dstProfile->name().contains(QLatin1String("linear"), Qt::CaseInsensitive)) &&
52
                    !conversionFlags.testFlag(KoColorConversionTransformation::NoOptimization)) {
53 54 55 56
                conversionFlags |= KoColorConversionTransformation::NoOptimization;
            }
        }

Boudewijn Rempt's avatar
Boudewijn Rempt committed
57
        m_transform = cmsCreateTransform(srcProfile->lcmsProfile(),
58 59 60 61 62
                                         srcColorSpaceType,
                                         dstProfile->lcmsProfile(),
                                         dstColorSpaceType,
                                         renderingIntent,
                                         conversionFlags);
63

64
        Q_ASSERT(m_transform);
65
    }
66

67 68
    ~KoLcmsColorConversionTransformation()
    {
69 70
        cmsDeleteTransform(m_transform);
    }
71

72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
public:

    virtual void transform(const quint8 *src, quint8 *dst, qint32 numPixels) const
    {
        Q_ASSERT(m_transform);

        qint32 srcPixelSize = srcColorSpace()->pixelSize();
        qint32 dstPixelSize = dstColorSpace()->pixelSize();

        cmsDoTransform(m_transform, const_cast<quint8 *>(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;
};
96

97 98 99 100
class KoLcmsColorProofingConversionTransformation : public KoColorProofingConversionTransformation
{
public:
    KoLcmsColorProofingConversionTransformation(const KoColorSpace *srcCs, quint32 srcColorSpaceType, LcmsColorProfileContainer *srcProfile,
101 102 103
                                                const KoColorSpace *dstCs, quint32 dstColorSpaceType, LcmsColorProfileContainer *dstProfile,
                                                const KoColorSpace *proofingSpace,
                                                Intent renderingIntent,
104 105 106
                                                Intent proofingIntent,
                                                ConversionFlags conversionFlags,
                                                quint8 *gamutWarning
107
                                                )
108
        : KoColorProofingConversionTransformation(srcCs, dstCs, proofingSpace, renderingIntent, proofingIntent, conversionFlags, gamutWarning)
109 110 111 112 113 114 115 116 117 118 119
        , 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)) &&
120 121
                    !conversionFlags.testFlag(KoColorConversionTransformation::NoOptimization)) {
                conversionFlags |= KoColorConversionTransformation::NoOptimization;
122 123 124
            }
        }

125 126 127 128
        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;
129 130 131
        cmsSetAlarmCodes(alarm);

        m_transform = cmsCreateProofingTransform(srcProfile->lcmsProfile(),
132 133 134 135 136
                                                 srcColorSpaceType,
                                                 dstProfile->lcmsProfile(),
                                                 dstColorSpaceType,
                                                 dynamic_cast<const IccColorProfile *>(proofingSpace->profile())->asLcms()->lcmsProfile(),
                                                 renderingIntent,
137
                                                 proofingIntent,
138
                                                 conversionFlags);
139 140 141 142 143 144 145 146 147

        Q_ASSERT(m_transform);
    }

    ~KoLcmsColorProofingConversionTransformation()
    {
        cmsDeleteTransform(m_transform);
    }

148
public:
149 150 151

    virtual void transform(const quint8 *src, quint8 *dst, qint32 numPixels) const
    {
152 153 154 155 156 157 158 159
        Q_ASSERT(m_transform);

        qint32 srcPixelSize = srcColorSpace()->pixelSize();
        qint32 dstPixelSize = dstColorSpace()->pixelSize();

        cmsDoTransform(m_transform, const_cast<quint8 *>(src), dst, numPixels);
        // Lcms does nothing to the destination alpha channel so we must convert that manually.
        while (numPixels > 0) {
160
            qreal alpha = srcColorSpace()->opacityF(src);
161 162 163 164 165 166 167 168 169
            dstColorSpace()->setOpacity(dst, alpha, 1);

            src += srcPixelSize;
            dst += dstPixelSize;
            numPixels--;
        }

    }
private:
170 171
    mutable cmsHTRANSFORM m_transform;
};
172 173 174 175 176 177 178 179 180 181 182 183 184

struct IccColorSpaceEngine::Private {
};

IccColorSpaceEngine::IccColorSpaceEngine() : KoColorSpaceEngine("icc", i18n("ICC Engine")), d(new Private)
{
}

IccColorSpaceEngine::~IccColorSpaceEngine()
{
    delete d;
}

185 186
void IccColorSpaceEngine::addProfile(const QString &filename)
{
187
    KoColorSpaceRegistry *registry = KoColorSpaceRegistry::instance();
188 189 190 191

    KoColorProfile *profile = new IccColorProfile(filename);
    Q_CHECK_PTR(profile);

192
    // this our own loading code; sometimes it fails because of an lcms error
193
    profile->load();
194 195

    // and then lcms can read the profile from file itself without problems,
196
    // quite often, and we can initialize it
197
    if (!profile->valid()) {
198
        cmsHPROFILE cmsp = cmsOpenProfileFromFile(filename.toLatin1(), "r");
199 200 201
        profile = LcmsColorProfileContainer::createFromLcmsProfile(cmsp);
    }

202
    if (profile->valid()) {
203
        qDebug() << "Valid profile : " << profile->fileName() << profile->name();
204
        registry->addProfile(profile);
205
    } else {
206
        qDebug() << "Invalid profile : " << profile->fileName() << profile->name();
207 208 209 210 211
        delete profile;
    }

}

212 213
void IccColorSpaceEngine::removeProfile(const QString &filename)
{
214
    KoColorSpaceRegistry *registry = KoColorSpaceRegistry::instance();
215 216 217 218 219 220 221 222 223 224

    KoColorProfile *profile = new IccColorProfile(filename);
    Q_CHECK_PTR(profile);
    profile->load();

    if (profile->valid() && registry->profileByName(profile->name())) {
        registry->removeProfile(profile);
    }
}

225
KoColorConversionTransformation *IccColorSpaceEngine::createColorTransformation(const KoColorSpace *srcColorSpace,
226 227 228
                                                                                const KoColorSpace *dstColorSpace,
                                                                                KoColorConversionTransformation::Intent renderingIntent,
                                                                                KoColorConversionTransformation::ConversionFlags conversionFlags) const
229
{
230 231 232
    Q_ASSERT(srcColorSpace);
    Q_ASSERT(dstColorSpace);

233
    return new KoLcmsColorConversionTransformation(
234 235 236
                srcColorSpace, computeColorSpaceType(srcColorSpace),
                dynamic_cast<const IccColorProfile *>(srcColorSpace->profile())->asLcms(), dstColorSpace, computeColorSpaceType(dstColorSpace),
                dynamic_cast<const IccColorProfile *>(dstColorSpace->profile())->asLcms(), renderingIntent, conversionFlags);
237 238

}
239
KoColorProofingConversionTransformation *IccColorSpaceEngine::createColorProofingTransformation(const KoColorSpace *srcColorSpace,
240 241 242
                                                                                                const KoColorSpace *dstColorSpace,
                                                                                                const KoColorSpace *proofingSpace,
                                                                                                KoColorConversionTransformation::Intent renderingIntent,
243 244 245
                                                                                                KoColorConversionTransformation::Intent proofingIntent,
                                                                                                KoColorConversionTransformation::ConversionFlags conversionFlags,
                                                                                                quint8 *gamutWarning) const
246 247 248 249 250 251 252
{
    Q_ASSERT(srcColorSpace);
    Q_ASSERT(dstColorSpace);

    return new KoLcmsColorProofingConversionTransformation(
                srcColorSpace, computeColorSpaceType(srcColorSpace),
                dynamic_cast<const IccColorProfile *>(srcColorSpace->profile())->asLcms(), dstColorSpace, computeColorSpaceType(dstColorSpace),
253
                dynamic_cast<const IccColorProfile *>(dstColorSpace->profile())->asLcms(), proofingSpace, renderingIntent, proofingIntent, conversionFlags, gamutWarning
254 255 256
                );
}

257
quint32 IccColorSpaceEngine::computeColorSpaceType(const KoColorSpace *cs) const
258
{
259 260
    Q_ASSERT(cs);

261
    if (const KoLcmsInfo *lcmsInfo = dynamic_cast<const KoLcmsInfo *>(cs)) {
262
        return lcmsInfo->colorSpaceType();
263
    } else {
264 265 266 267 268 269 270
        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);
271
        } else if (depthId == Integer16BitsColorDepthID.id()) {
272
            depthType = BYTES_SH(2);
273
        } else if (depthId == Float16BitsColorDepthID.id()) {
274
            depthType = BYTES_SH(2);
275
        } else if (depthId == Float32BitsColorDepthID.id()) {
276
            depthType = BYTES_SH(4);
277
        } else if (depthId == Float64BitsColorDepthID.id()) {
278
            depthType = BYTES_SH(0);
279
        } else {
280
            qWarning() << "Unknow bit depth";
281 282 283
            return 0;
        }
        // Compute the model part of the type
284
        quint32 modelType = 0;
285 286

        if (modelId == RGBAColorModelID.id()) {
287
            if (depthId.startsWith(QLatin1Char('U'))) {
288
                modelType = (COLORSPACE_SH(PT_RGB) | EXTRA_SH(1) | CHANNELS_SH(3) | DOSWAP_SH(1) | SWAPFIRST_SH(1));
289 290
            } else if (depthId.startsWith(QLatin1Char('F'))) {
                modelType = (COLORSPACE_SH(PT_RGB) | EXTRA_SH(1) | CHANNELS_SH(3));
291 292 293 294 295 296 297 298 299 300 301 302 303 304
            }
        } 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 {
305
            qWarning() << "Cannot convert colorspace to lcms modeltype";
306 307 308
            return 0;
        }
        return depthType | modelType;
309 310
    }
}