KoColorSpace.cpp 15.1 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;
}

107 108
QBitArray KoColorSpace::channelFlags(bool color, bool alpha, bool substance, bool substrate) const
{
109 110 111 112 113 114 115 116 117
    QBitArray ba(d->channels.size());
    if (!color && !alpha && !substance && !substrate) 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) ||
                (substrate && channel->channelType() == KoChannelInfo::SUBSTRATE) ||
                (substance && channel->channelType() == KoChannelInfo::SUBSTANCE))
118
            ba.setBit(channel->index(), true);
119 120 121 122
    }
    return ba;
}

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

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

140
QList<KoCompositeOp*> KoColorSpace::compositeOps() const
141
{
142 143 144 145
    return d->compositeOps.values();
}


146 147
KoMixColorsOp* KoColorSpace::mixColorsOp() const
{
148 149 150 151
    return d->mixColorsOp;
}


152 153
KoConvolutionOp* KoColorSpace::convolutionOp() const
{
154
    return d->convolutionOp;
155 156
}

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

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

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

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

void KoColorSpace::toLabA16(const quint8 * src, quint8 * dst, quint32 nPixels) const
{
206
    toLabA16Converter()->transform(src, dst, nPixels);
207 208 209 210
}

void KoColorSpace::fromLabA16(const quint8 * src, quint8 * dst, quint32 nPixels) const
{
211
    fromLabA16Converter()->transform(src, dst, nPixels);
212 213 214 215
}

void KoColorSpace::toRgbA16(const quint8 * src, quint8 * dst, quint32 nPixels) const
{
216
    toRgbA16Converter()->transform(src, dst, nPixels);
217 218 219 220
}

void KoColorSpace::fromRgbA16(const quint8 * src, quint8 * dst, quint32 nPixels) const
{
221
    fromRgbA16Converter()->transform(src, dst, nPixels);
222 223
}

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

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

248

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

}

void KoColorSpace::bitBlt(quint8 *dst,
                          qint32 dststride,
272
                          const KoColorSpace * srcSpace,
273 274 275 276 277 278 279
                          const quint8 *src,
                          qint32 srcRowStride,
                          const quint8 *srcAlphaMask,
                          qint32 maskRowStride,
                          quint8 opacity,
                          qint32 rows,
                          qint32 cols,
Cyrille Berger's avatar
Cyrille Berger committed
280
                          const QString& op) const
281
{
282 283 284 285
    if (d->compositeOps.contains(op)) {
        bitBlt(dst, dststride, srcSpace, src, srcRowStride, srcAlphaMask, maskRowStride, opacity, rows, cols, d->compositeOps.value(op));
    } else {
        bitBlt(dst, dststride, srcSpace, src, srcRowStride, srcAlphaMask, maskRowStride, opacity, rows, cols, d->compositeOps.value(COMPOSITE_OVER));
286 287
    }
}
288 289

void KoColorSpace::bitBlt(quint8 *dst,
290
                          qint32 dstRowStride,
291
                          const KoColorSpace * srcSpace,
292 293 294 295 296 297 298 299 300
                          const quint8 *src,
                          qint32 srcRowStride,
                          const quint8 *srcAlphaMask,
                          qint32 maskRowStride,
                          quint8 opacity,
                          qint32 rows,
                          qint32 cols,
                          const KoCompositeOp * op,
                          const QBitArray & channelFlags) const
301
{
302
    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());
303

304 305 306
    if (rows <= 0 || cols <= 0)
        return;

307
    if (!(*this == *srcSpace)) {
308

Boudewijn Rempt's avatar
Boudewijn Rempt committed
309
        quint32 conversionBufferStride = cols * pixelSize();
310 311
        QVector<quint8> * conversionCache =
            threadLocalConversionCache(rows * conversionBufferStride);
312

Boudewijn Rempt's avatar
Boudewijn Rempt committed
313
        quint8* conversionData = conversionCache->data();
314 315 316

        for (qint32 row = 0; row < rows; row++) {
            srcSpace->convertPixelsTo(src + row * srcRowStride,
317
                                      conversionData + row * conversionBufferStride,
Boudewijn Rempt's avatar
Boudewijn Rempt committed
318
                                      this, cols);
319 320
        }

321 322 323 324 325 326 327 328 329 330 331
        op->composite(dst, dstRowStride,
                      conversionData, conversionBufferStride,
                      srcAlphaMask, maskRowStride,
                      rows,  cols,
                      opacity, channelFlags);
    } else {
        op->composite(dst, dstRowStride,
                      src, srcRowStride,
                      srcAlphaMask, maskRowStride,
                      rows,  cols,
                      opacity, channelFlags);
332
    }
Boudewijn Rempt's avatar
Boudewijn Rempt committed
333

334 335
}

336
// XXX: I don't want this code duplication, but also don't want an
337 338 339
//      extra function call in this critical section of code. What to
//      do?
void KoColorSpace::bitBlt(quint8 *dst,
340
                          qint32 dstRowStride,
341
                          const KoColorSpace * srcSpace,
342 343 344 345 346 347 348 349
                          const quint8 *src,
                          qint32 srcRowStride,
                          const quint8 *srcAlphaMask,
                          qint32 maskRowStride,
                          quint8 opacity,
                          qint32 rows,
                          qint32 cols,
                          const KoCompositeOp * op) const
350
{
351
    Q_ASSERT(*op->colorSpace() == *this);
352

353 354 355 356
    if (rows <= 0 || cols <= 0)
        return;

    if (this != srcSpace) {
Boudewijn Rempt's avatar
Boudewijn Rempt committed
357
        quint32 conversionBufferStride = cols * pixelSize();
358 359
        QVector<quint8> * conversionCache =
            threadLocalConversionCache(rows * conversionBufferStride);
360

361
        quint8* conversionData = conversionCache->data();
362 363 364

        for (qint32 row = 0; row < rows; row++) {
            srcSpace->convertPixelsTo(src + row * srcRowStride,
365
                                      conversionData + row * conversionBufferStride, this,
366 367 368
                                      cols);
        }

369 370 371 372 373
        op->composite(dst, dstRowStride,
                      conversionData, conversionBufferStride,
                      srcAlphaMask, maskRowStride,
                      rows,  cols,
                      opacity);
374

375 376 377 378 379 380
    } else {
        op->composite(dst, dstRowStride,
                      src, srcRowStride,
                      srcAlphaMask, maskRowStride,
                      rows, cols,
                      opacity);
381
    }
Boudewijn Rempt's avatar
Boudewijn Rempt committed
382

383
}
384 385 386 387

QVector<quint8> * KoColorSpace::threadLocalConversionCache(quint32 size) const
{
    QVector<quint8> * ba = 0;
388 389 390 391
    if (!d->conversionCache.hasLocalData()) {
        ba = new QVector<quint8>(size, '0');
        d->conversionCache.setLocalData(ba);
    } else {
392
        ba = d->conversionCache.localData();
393 394
        if ((quint8)ba->size() < size)
            ba->resize(size);
395 396 397
    }
    return ba;
}
398

399
KoColorTransformation* KoColorSpace::createColorTransformation(const QString & id, const QHash<QString, QVariant> & parameters) const
400
{
401 402 403
    KoColorTransformationFactory* factory = KoColorTransformationFactoryRegistry::instance()->get(id);
    if (!factory) return 0;
    QPair<KoID, KoID> model(colorModelId(), colorDepthId());
404
    QList< QPair<KoID, KoID> > models = factory->supportedModels();
405 406
    if (models.isEmpty() || models.contains(model)) {
        return factory->createTransformation(this, parameters);
407 408
    } else {
        // Find the best solution
409
        // TODO use the color conversion cache
410 411 412 413 414 415
        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);
416
        return new KoFallBackColorTransformation(csToFallBack, fallBackToCs, transfo);
417 418
    }
}
419

420
QImage KoColorSpace::convertToQImage(const quint8 *data, qint32 width, qint32 height,
Boudewijn Rempt's avatar
Boudewijn Rempt committed
421 422
                                     const KoColorProfile *dstProfile,
                                     KoColorConversionTransformation::Intent renderingIntent) const
423 424 425 426 427 428 429 430 431 432 433

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

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

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

    return img;
}