KoColorSpace.cpp 13.9 KB
Newer Older
1 2 3
/*
 *  Copyright (c) 2005 Boudewijn Rempt <boud@valdyas.org>
 *
4
 * This library is free software; you can redistribute it and/or
5
 * modify it under the terms of the GNU Lesser General Public
6 7
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
8
 *
9 10 11
 * 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
12
 * Lesser General Public License for more details.
13
 *
14
 * You should have received a copy of the GNU Lesser General Public License
15 16 17 18
 * 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.
*/
19 20 21

#include "KoColorSpace.h"

22 23
#include <QThreadStorage>
#include <QByteArray>
24
#include <QBitArray>
25

26
#include "KoChannelInfo.h"
Boudewijn Rempt's avatar
Boudewijn Rempt committed
27
#include "DebugPigment.h"
28
#include "KoCompositeOp.h"
Cyrille Berger's avatar
Cyrille Berger committed
29
#include "KoColorTransformation.h"
30 31
#include "KoColorTransformationFactory.h"
#include "KoColorTransformationFactoryRegistry.h"
32
#include "KoColorConversionCache.h"
33 34
#include "KoColorConversionSystem.h"
#include "KoColorSpaceRegistry.h"
35
#include "KoColorProfile.h"
36
#include "KoCopyColorConversionTransformation.h"
37
#include "KoFallBackColorTransformation.h"
38
#include "KoUniqueNumberForIdServer.h"
39

40
#include "KoColorSpace_p.h"
41

Stefan Nikolaus's avatar
Stefan Nikolaus committed
42
KoColorSpace::KoColorSpace()
43
        : d(new Private())
Stefan Nikolaus's avatar
Stefan Nikolaus committed
44 45 46
{
}

47 48
KoColorSpace::KoColorSpace(const QString &id, const QString &name, KoMixColorsOp* mixColorsOp, KoConvolutionOp* convolutionOp)
        : d(new Private())
49
{
50
    d->id = id;
51
    d->idNumber = KoUniqueNumberForIdServer::instance()->numberForId(d->id);
52 53 54
    d->name = name;
    d->mixColorsOp = mixColorsOp;
    d->convolutionOp = convolutionOp;
55 56 57 58
    d->transfoToRGBA16 = 0;
    d->transfoFromRGBA16 = 0;
    d->transfoToLABA16 = 0;
    d->transfoFromLABA16 = 0;
59
    d->deletability = NotOwnedByRegistry;
60 61
}

62
KoColorSpace::~KoColorSpace()
63
{
64 65
    Q_ASSERT(d->deletability != OwnedByRegistryDoNotDelete);

66
    qDeleteAll(d->compositeOps);
67
    foreach(KoChannelInfo * channel, d->channels) {
Cyrille Berger's avatar
Cyrille Berger committed
68 69
        delete channel;
    }
70 71 72 73 74
    if (d->deletability == NotOwnedByRegistry) {
        KoColorConversionCache* cache = KoColorSpaceRegistry::instance()->colorConversionCache();
        if (cache) {
            cache->colorSpaceIsDestroyed(this);
        }
75
    }
76 77
    delete d->mixColorsOp;
    delete d->convolutionOp;
Cyrille Berger's avatar
Cyrille Berger committed
78 79 80 81
    delete d->transfoToRGBA16;
    delete d->transfoFromRGBA16;
    delete d->transfoToLABA16;
    delete d->transfoFromLABA16;
82 83 84
    delete d;
}

85 86
bool KoColorSpace::operator==(const KoColorSpace& rhs) const
{
87 88
    const KoColorProfile* p1 = rhs.profile();
    const KoColorProfile* p2 = profile();
89
    return d->idNumber == rhs.d->idNumber && ((p1 == p2) || (*p1 == *p2));
90 91
}

92 93 94 95
QString KoColorSpace::id() const
{
    return d->id;
}
96

97 98 99 100
QString KoColorSpace::name() const
{
    return d->name;
}
101

Adrian Page's avatar
q3--  
Adrian Page committed
102
QList<KoChannelInfo *> KoColorSpace::channels() const
103 104 105 106
{
    return d->channels;
}

Boudewijn Rempt's avatar
Boudewijn Rempt committed
107
QBitArray KoColorSpace::channelFlags(bool color, bool alpha) const
108
{
109
    QBitArray ba(d->channels.size());
Boudewijn Rempt's avatar
Boudewijn Rempt committed
110
    if (!color && !alpha) return ba;
111 112 113 114

    for (int i = 0; i < d->channels.size(); ++i) {
        KoChannelInfo * channel = d->channels.at(i);
        if ((color && channel->channelType() == KoChannelInfo::COLOR) ||
Boudewijn Rempt's avatar
Boudewijn Rempt committed
115
                (alpha && channel->channelType() == KoChannelInfo::ALPHA))
116
            ba.setBit(i, true);
117 118 119 120
    }
    return ba;
}

121 122 123
void KoColorSpace::addChannel(KoChannelInfo * ci)
{
    d->channels.push_back(ci);
124 125
}

126
quint8 *KoColorSpace::allocPixelBuffer(quint32 numPixels, bool clear, quint8 defaultvalue) const
127
{
128 129 130 131 132 133 134 135
    if (numPixels == 0) {
        return 0;
    }
    quint8* buf = new quint8[pixelSize()*numPixels];
    if (clear) {
        memset(buf, defaultvalue, pixelSize() * numPixels);
    }
    return buf;
136 137
}

138 139 140 141 142
bool KoColorSpace::hasCompositeOp(const QString& id) const
{
    return d->compositeOps.contains(id);
}

143
QList<KoCompositeOp*> KoColorSpace::compositeOps() const
144
{
145 146 147 148
    return d->compositeOps.values();
}


149 150
KoMixColorsOp* KoColorSpace::mixColorsOp() const
{
151 152 153 154
    return d->mixColorsOp;
}


155 156
KoConvolutionOp* KoColorSpace::convolutionOp() const
{
157
    return d->convolutionOp;
158 159
}

Cyrille Berger's avatar
Cyrille Berger committed
160
const KoCompositeOp * KoColorSpace::compositeOp(const QString & id) const
161
{
162 163
    if (d->compositeOps.contains(id))
        return d->compositeOps.value(id);
164
    else {
Boudewijn Rempt's avatar
Boudewijn Rempt committed
165
        warnPigment << "Asking for non-existent composite operation " << id << ", returning " << COMPOSITE_OVER;
166
        return d->compositeOps.value(COMPOSITE_OVER);
167
    }
168 169 170 171
}

void KoColorSpace::addCompositeOp(const KoCompositeOp * op)
{
172 173
    if (op->colorSpace()->id() == id()) {
        d->compositeOps.insert(op->id(), const_cast<KoCompositeOp*>(op));
174 175 176
    }
}

177
const KoColorConversionTransformation* KoColorSpace::toLabA16Converter() const
178
{
179 180
    if (!d->transfoToLABA16) {
        d->transfoToLABA16 = KoColorSpaceRegistry::instance()->colorConversionSystem()->createColorConverter(this, KoColorSpaceRegistry::instance()->lab16("")) ;
181
    }
182
    return d->transfoToLABA16;
183
}
184

185
const KoColorConversionTransformation* KoColorSpace::fromLabA16Converter() const
186
{
187 188
    if (!d->transfoFromLABA16) {
        d->transfoFromLABA16 = KoColorSpaceRegistry::instance()->colorConversionSystem()->createColorConverter(KoColorSpaceRegistry::instance()->lab16("") , this) ;
189
    }
190
    return d->transfoFromLABA16;
191
}
192
const KoColorConversionTransformation* KoColorSpace::toRgbA16Converter() const
193
{
194 195
    if (!d->transfoToRGBA16) {
        d->transfoToRGBA16 = KoColorSpaceRegistry::instance()->colorConversionSystem()->createColorConverter(this, KoColorSpaceRegistry::instance()->rgb16("")) ;
196
    }
197
    return d->transfoToRGBA16;
198
}
199
const KoColorConversionTransformation* KoColorSpace::fromRgbA16Converter() const
200
{
201 202
    if (!d->transfoFromRGBA16) {
        d->transfoFromRGBA16 = KoColorSpaceRegistry::instance()->colorConversionSystem()->createColorConverter(KoColorSpaceRegistry::instance()->rgb16("") , this) ;
203
    }
204 205 206 207 208
    return d->transfoFromRGBA16;
}

void KoColorSpace::toLabA16(const quint8 * src, quint8 * dst, quint32 nPixels) const
{
209
    toLabA16Converter()->transform(src, dst, nPixels);
210 211 212 213
}

void KoColorSpace::fromLabA16(const quint8 * src, quint8 * dst, quint32 nPixels) const
{
214
    fromLabA16Converter()->transform(src, dst, nPixels);
215 216 217 218
}

void KoColorSpace::toRgbA16(const quint8 * src, quint8 * dst, quint32 nPixels) const
{
219
    toRgbA16Converter()->transform(src, dst, nPixels);
220 221 222 223
}

void KoColorSpace::fromRgbA16(const quint8 * src, quint8 * dst, quint32 nPixels) const
{
224
    fromRgbA16Converter()->transform(src, dst, nPixels);
225 226
}

227
KoColorConversionTransformation* KoColorSpace::createColorConverter(const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, bool blackpointCompensation) const
228
{
229
    if (*this == *dstColorSpace) {
230
        return new KoCopyColorConversionTransformation(this);
231
    } else {
232
        return KoColorSpaceRegistry::instance()->colorConversionSystem()->createColorConverter(this, dstColorSpace, renderingIntent, blackpointCompensation);
233
    }
234 235
}

236
bool KoColorSpace::convertPixelsTo(const quint8 * src,
Boudewijn Rempt's avatar
Boudewijn Rempt committed
237 238 239
                                   quint8 * dst,
                                   const KoColorSpace * dstColorSpace,
                                   quint32 numPixels,
240 241
                                   KoColorConversionTransformation::Intent renderingIntent,
                                   bool blackpointCompensation) const
242
{
243 244
    if (*this == *dstColorSpace) {
        memcpy(dst, src, numPixels * sizeof(quint8) * pixelSize());
245
    } else {
246
        KoCachedColorConversionTransformation cct = KoColorSpaceRegistry::instance()->colorConversionCache()->cachedConverter(this, dstColorSpace, renderingIntent, blackpointCompensation);
247 248
        cct.transformation()->transform(src, dst, numPixels);
    }
249 250
    return true;
}
251

252

Silvio Heinrich's avatar
Silvio Heinrich committed
253
void KoColorSpace::bitBlt(quint8* dst,
254
                          qint32 dststride,
Silvio Heinrich's avatar
Silvio Heinrich committed
255 256
                          const KoColorSpace* srcSpace,
                          const quint8* src,
257
                          qint32 srcRowStride,
Silvio Heinrich's avatar
Silvio Heinrich committed
258
                          const quint8* srcAlphaMask,
259 260 261 262
                          qint32 maskRowStride,
                          quint8 opacity,
                          qint32 rows,
                          qint32 cols,
Silvio Heinrich's avatar
Silvio Heinrich committed
263 264
                          const QString& op,
                          const QBitArray& channelFlags) const
265
{
266
    if (d->compositeOps.contains(op)) {
267
        bitBlt(dst, dststride, srcSpace, src, srcRowStride, srcAlphaMask, maskRowStride, opacity, rows, cols, d->compositeOps.value(op), channelFlags);
268
    } else {
269
        bitBlt(dst, dststride, srcSpace, src, srcRowStride, srcAlphaMask, maskRowStride, opacity, rows, cols, d->compositeOps.value(COMPOSITE_OVER), channelFlags);
270
    }
271

272 273
}

Silvio Heinrich's avatar
Silvio Heinrich committed
274
void KoColorSpace::bitBlt(quint8* dst,
275
                          qint32 dstRowStride,
Silvio Heinrich's avatar
Silvio Heinrich committed
276 277
                          const KoColorSpace* srcSpace,
                          const quint8* src,
278
                          qint32 srcRowStride,
279
                          const quint8 *srcAlphaMask,
280 281 282 283
                          qint32 maskRowStride,
                          quint8 opacity,
                          qint32 rows,
                          qint32 cols,
Silvio Heinrich's avatar
Silvio Heinrich committed
284 285
                          const KoCompositeOp* op,
                          const QBitArray& channelFlags) const
286
{
287
    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());
288

289 290 291
    if (rows <= 0 || cols <= 0)
        return;

292
    if (!(*this == *srcSpace)) {
293

Boudewijn Rempt's avatar
Boudewijn Rempt committed
294
        quint32 conversionBufferStride = cols * pixelSize();
295 296
        QVector<quint8> * conversionCache =
            threadLocalConversionCache(rows * conversionBufferStride);
297

Boudewijn Rempt's avatar
Boudewijn Rempt committed
298
        quint8* conversionData = conversionCache->data();
299 300 301

        for (qint32 row = 0; row < rows; row++) {
            srcSpace->convertPixelsTo(src + row * srcRowStride,
302
                                      conversionData + row * conversionBufferStride,
Boudewijn Rempt's avatar
Boudewijn Rempt committed
303
                                      this, cols);
304 305
        }

306 307 308 309
        op->composite(dst, dstRowStride,
                      conversionData, conversionBufferStride,
                      srcAlphaMask, maskRowStride,
                      rows,  cols,
310
                      opacity, channelFlags);
311 312 313 314 315
    } else {
        op->composite(dst, dstRowStride,
                      src, srcRowStride,
                      srcAlphaMask, maskRowStride,
                      rows,  cols,
316
                      opacity, channelFlags);
317
    }
Boudewijn Rempt's avatar
Boudewijn Rempt committed
318

319 320
}

321 322 323
void KoColorSpace::bitBlt(const KoColorSpace* srcSpace, const KoCompositeOp::ParameterInfo& params, const KoCompositeOp* op) 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());
324

325 326
    if(params.rows <= 0 || params.cols <= 0)
        return;
327

328 329 330 331
    if(!(*this == *srcSpace)) {
        quint32           conversionBufferStride = params.cols * pixelSize();
        QVector<quint8> * conversionCache        = threadLocalConversionCache(params.rows * conversionBufferStride);
        quint8*           conversionData         = conversionCache->data();
332

333 334 335 336
        for(qint32 row=0; row<params.rows; row++) {
            srcSpace->convertPixelsTo(params.srcRowStart + row*params.srcRowStride    ,
                                      conversionData     + row*conversionBufferStride, this, params.cols);
        }
337

338 339 340 341 342 343 344 345 346
        KoCompositeOp::ParameterInfo paramInfo(params);
        paramInfo.srcRowStart  = conversionData;
        paramInfo.srcRowStride = conversionBufferStride;
        op->composite(paramInfo);
    }
    else op->composite(params);
}


347 348 349
QVector<quint8> * KoColorSpace::threadLocalConversionCache(quint32 size) const
{
    QVector<quint8> * ba = 0;
350 351 352 353
    if (!d->conversionCache.hasLocalData()) {
        ba = new QVector<quint8>(size, '0');
        d->conversionCache.setLocalData(ba);
    } else {
354
        ba = d->conversionCache.localData();
355 356
        if ((quint8)ba->size() < size)
            ba->resize(size);
357 358 359
    }
    return ba;
}
360

361
KoColorTransformation* KoColorSpace::createColorTransformation(const QString & id, const QHash<QString, QVariant> & parameters) const
362
{
363 364 365
    KoColorTransformationFactory* factory = KoColorTransformationFactoryRegistry::instance()->get(id);
    if (!factory) return 0;
    QPair<KoID, KoID> model(colorModelId(), colorDepthId());
366
    QList< QPair<KoID, KoID> > models = factory->supportedModels();
367 368
    if (models.isEmpty() || models.contains(model)) {
        return factory->createTransformation(this, parameters);
369 370
    } else {
        // Find the best solution
371
        // TODO use the color conversion cache
372 373 374 375 376 377
        KoColorConversionTransformation* csToFallBack = 0;
        KoColorConversionTransformation* fallBackToCs = 0;
        KoColorSpaceRegistry::instance()->colorConversionSystem()->createColorConverters(this, models, csToFallBack, fallBackToCs);
        Q_ASSERT(csToFallBack);
        Q_ASSERT(fallBackToCs);
        KoColorTransformation* transfo = factory->createTransformation(fallBackToCs->srcColorSpace(), parameters);
378
        return new KoFallBackColorTransformation(csToFallBack, fallBackToCs, transfo);
379 380
    }
}
381

382
QImage KoColorSpace::convertToQImage(const quint8 *data, qint32 width, qint32 height,
Boudewijn Rempt's avatar
Boudewijn Rempt committed
383
                                     const KoColorProfile *dstProfile,
384 385
                                     KoColorConversionTransformation::Intent renderingIntent,
                                     bool blackpointCompensation) const
386 387 388 389 390 391 392

{
    QImage img = QImage(width, height, QImage::Format_ARGB32);

    const KoColorSpace * dstCS = KoColorSpaceRegistry::instance()->rgb8(dstProfile);

    if (data)
393
        this->convertPixelsTo(const_cast<quint8 *>(data), img.bits(), dstCS, width * height, renderingIntent, blackpointCompensation);
394 395 396

    return img;
}