KoColorSpace.cpp 15.7 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 43 44 45 46
KoColorSpace::KoColorSpace()
    : d (new Private())
{
}

47
KoColorSpace::KoColorSpace(const QString &id, const QString &name, KoMixColorsOp* mixColorsOp, KoConvolutionOp* convolutionOp )
48
    : 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);
Cyrille Berger's avatar
Cyrille Berger committed
67 68 69 70
    foreach(KoChannelInfo * channel, d->channels)
    {
        delete channel;
    }
71 72 73 74 75
    if (d->deletability == NotOwnedByRegistry) {
        KoColorConversionCache* cache = KoColorSpaceRegistry::instance()->colorConversionCache();
        if (cache) {
            cache->colorSpaceIsDestroyed(this);
        }
76
    }
77 78 79 80 81
    delete d->mixColorsOp;
    delete d->convolutionOp;
    delete d;
}

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

88 89 90 91
QString KoColorSpace::id() const {return d->id;}

QString KoColorSpace::name() const {return d->name;}

Adrian Page's avatar
q3--  
Adrian Page committed
92
QList<KoChannelInfo *> KoColorSpace::channels() const
93 94 95 96
{
    return d->channels;
}

97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
QBitArray KoColorSpace::channelFlags(bool color, bool alpha, bool substance, bool substrate) const
{
    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 ) )
            ba.setBit( i, true );
    }
    return ba;
}

113
QBitArray KoColorSpace::setChannelFlagsToPixelOrder(const QBitArray & origChannelFlags) const
114
{
115 116 117
    if ( origChannelFlags.isEmpty() ) return origChannelFlags;

    QBitArray orderedChannelFlags( origChannelFlags.size() );
118
    for ( int i = 0; i < origChannelFlags.size(); ++i ) {
119

120 121 122 123
        KoChannelInfo * channel = d->channels.at( i );
        orderedChannelFlags.setBit( channel->pos(), origChannelFlags.testBit( i ) );
    }
    return orderedChannelFlags;
124 125 126 127 128 129 130 131 132 133 134 135 136
}

QBitArray KoColorSpace::setChannelFlagsToColorSpaceOrder( const QBitArray & origChannelFlags ) const
{
    if ( origChannelFlags.isEmpty() ) return origChannelFlags;

    QBitArray orderedChannelFlags( origChannelFlags.size() );
    for ( int i = 0; i < orderedChannelFlags.size(); ++i )
    {
        KoChannelInfo * channel = d->channels.at( i );
        orderedChannelFlags.setBit( i, origChannelFlags.testBit( channel->pos() ) );
    }
    return orderedChannelFlags;
137 138
}

139 140 141
void KoColorSpace::addChannel(KoChannelInfo * ci)
{
    d->channels.push_back(ci);
142 143
}

144
quint8 *KoColorSpace::allocPixelBuffer(quint32 numPixels, bool clear, quint8 defaultvalue) const
145
{
146 147 148 149 150 151 152 153
    if (numPixels == 0) {
        return 0;
    }
    quint8* buf = new quint8[pixelSize()*numPixels];
    if (clear) {
        memset(buf, defaultvalue, pixelSize() * numPixels);
    }
    return buf;
154 155
}

156
QList<KoCompositeOp*> KoColorSpace::compositeOps() const
157
{
158 159 160 161 162 163 164 165 166 167 168
    return d->compositeOps.values();
}


KoMixColorsOp* KoColorSpace::mixColorsOp() const {
    return d->mixColorsOp;
}


KoConvolutionOp* KoColorSpace::convolutionOp() const {
    return d->convolutionOp;
169 170
}

Cyrille Berger's avatar
Cyrille Berger committed
171
const KoCompositeOp * KoColorSpace::compositeOp(const QString & id) const
172
{
173 174
    if ( d->compositeOps.contains( id ) )
        return d->compositeOps.value( id );
175
    else {
Boudewijn Rempt's avatar
Boudewijn Rempt committed
176
        warnPigment << "Asking for non-existent composite operation " << id << ", returning " << COMPOSITE_OVER;
177
        return d->compositeOps.value( COMPOSITE_OVER );
178
    }
179 180 181 182 183
}

void KoColorSpace::addCompositeOp(const KoCompositeOp * op)
{
    if ( op->colorSpace()->id() == id()) {
184
        d->compositeOps.insert( op->id(), const_cast<KoCompositeOp*>( op ) );
185 186 187
    }
}

188
const KoColorConversionTransformation* KoColorSpace::toLabA16Converter() const
189
{
Boudewijn Rempt's avatar
Boudewijn Rempt committed
190
    if(!d->transfoToLABA16)
191
    {
192
        d->transfoToLABA16 = KoColorSpaceRegistry::instance()->colorConversionSystem()->createColorConverter(this, KoColorSpaceRegistry::instance()->lab16("") ) ;
193
    }
194
    return d->transfoToLABA16;
195
}
196

197
const KoColorConversionTransformation* KoColorSpace::fromLabA16Converter() const
198
{
Boudewijn Rempt's avatar
Boudewijn Rempt committed
199
    if(!d->transfoFromLABA16)
200
    {
201
        d->transfoFromLABA16 = KoColorSpaceRegistry::instance()->colorConversionSystem()->createColorConverter( KoColorSpaceRegistry::instance()->lab16("") , this ) ;
202
    }
203
    return d->transfoFromLABA16;
204
}
205
const KoColorConversionTransformation* KoColorSpace::toRgbA16Converter() const
206
{
Boudewijn Rempt's avatar
Boudewijn Rempt committed
207
    if(!d->transfoToRGBA16)
208
    {
209
        d->transfoToRGBA16 = KoColorSpaceRegistry::instance()->colorConversionSystem()->createColorConverter( this, KoColorSpaceRegistry::instance()->rgb16("") ) ;
210
    }
211
    return d->transfoToRGBA16;
212
}
213
const KoColorConversionTransformation* KoColorSpace::fromRgbA16Converter() const
214
{
Boudewijn Rempt's avatar
Boudewijn Rempt committed
215
    if(!d->transfoFromRGBA16)
216
    {
217
        d->transfoFromRGBA16 = KoColorSpaceRegistry::instance()->colorConversionSystem()->createColorConverter( KoColorSpaceRegistry::instance()->rgb16("") , this ) ;
218
    }
219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
    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);
240 241
}

242 243
KoColorConversionTransformation* KoColorSpace::createColorConverter(const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent) const
{
244
    if( *this == *dstColorSpace)
245
    {
246
        return new KoCopyColorConversionTransformation(this);
247
    } else {
248
        return KoColorSpaceRegistry::instance()->colorConversionSystem()->createColorConverter( this, dstColorSpace, renderingIntent);
249
    }
250 251
}

252
bool KoColorSpace::convertPixelsTo(const quint8 * src,
253 254 255 256
        quint8 * dst,
        const KoColorSpace * dstColorSpace,
        quint32 numPixels,
        KoColorConversionTransformation::Intent renderingIntent) const
257
{
258 259 260 261 262 263 264
    if( *this == *dstColorSpace)
    {
        memcpy( dst, src, numPixels * sizeof(quint8) * pixelSize());
    } else {
        KoCachedColorConversionTransformation cct = KoColorSpaceRegistry::instance()->colorConversionCache()->cachedConverter(this, dstColorSpace, renderingIntent);
        cct.transformation()->transform(src, dst, numPixels);
    }
265 266
    return true;
}
267

268

269 270
void KoColorSpace::bitBlt(quint8 *dst,
                          qint32 dststride,
271
                          const KoColorSpace * srcSpace,
272 273 274 275 276 277 278 279
                          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
280
                          const QBitArray & channelFlags) const
281
{
282 283
    if ( d->compositeOps.contains( op ) ) {
        bitBlt(dst, dststride, srcSpace, src, srcRowStride, srcAlphaMask, maskRowStride, opacity, rows, cols, d->compositeOps.value( op ), channelFlags);
284 285
    }
    else {
286
        bitBlt(dst, dststride, srcSpace, src, srcRowStride, srcAlphaMask, maskRowStride, opacity, rows, cols, d->compositeOps.value( COMPOSITE_OVER ), channelFlags);
287 288 289 290 291 292
    }

}

void KoColorSpace::bitBlt(quint8 *dst,
                          qint32 dststride,
293
                          const KoColorSpace * srcSpace,
294 295 296 297 298 299 300
                          const quint8 *src,
                          qint32 srcRowStride,
                          const quint8 *srcAlphaMask,
                          qint32 maskRowStride,
                          quint8 opacity,
                          qint32 rows,
                          qint32 cols,
Cyrille Berger's avatar
Cyrille Berger committed
301
                          const QString& op) const
302
{
303 304
    if ( d->compositeOps.contains( op ) ) {
        bitBlt(dst, dststride, srcSpace, src, srcRowStride, srcAlphaMask, maskRowStride, opacity, rows, cols, d->compositeOps.value( op ));
305 306
    }
    else {
307
        bitBlt(dst, dststride, srcSpace, src, srcRowStride, srcAlphaMask, maskRowStride, opacity, rows, cols, d->compositeOps.value( COMPOSITE_OVER ) );
308 309
    }
}
310 311

void KoColorSpace::bitBlt(quint8 *dst,
312
                          qint32 dstRowStride,
313
                          const KoColorSpace * srcSpace,
314 315 316 317 318 319 320 321 322
                          const quint8 *src,
                          qint32 srcRowStride,
                          const quint8 *srcAlphaMask,
                          qint32 maskRowStride,
                          quint8 opacity,
                          qint32 rows,
                          qint32 cols,
                          const KoCompositeOp * op,
                          const QBitArray & channelFlags) const
323
{
Cyrille Berger's avatar
Cyrille Berger committed
324
    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() );
325

326 327 328
    if (rows <= 0 || cols <= 0)
        return;

329
    if (!(*this == *srcSpace)) {
330

331 332 333
	quint32 conversionBufferStride = cols * pixelSize();
        QVector<quint8> * conversionCache = 
	    threadLocalConversionCache(rows * conversionBufferStride);
334

335
	quint8* conversionData = conversionCache->data();
336 337 338

        for (qint32 row = 0; row < rows; row++) {
            srcSpace->convertPixelsTo(src + row * srcRowStride,
339 340
                                      conversionData + row * conversionBufferStride,
				      this, cols);
341 342
        }

343 344
        op->composite( dst, dstRowStride,
                       conversionData, conversionBufferStride,
345 346 347 348 349 350
                       srcAlphaMask, maskRowStride,
                       rows,  cols,
                       opacity, channelFlags );

    }
    else {
351
        op->composite( dst, dstRowStride,
352 353 354 355 356 357 358
                       src, srcRowStride,
                       srcAlphaMask, maskRowStride,
                       rows,  cols,
                       opacity, channelFlags );
    }
}

359
// XXX: I don't want this code duplication, but also don't want an
360 361 362
//      extra function call in this critical section of code. What to
//      do?
void KoColorSpace::bitBlt(quint8 *dst,
363
                          qint32 dstRowStride,
364
                          const KoColorSpace * srcSpace,
365 366 367 368 369 370 371 372
                          const quint8 *src,
                          qint32 srcRowStride,
                          const quint8 *srcAlphaMask,
                          qint32 maskRowStride,
                          quint8 opacity,
                          qint32 rows,
                          qint32 cols,
                          const KoCompositeOp * op) const
373
{
374
    Q_ASSERT(*op->colorSpace() == *this);
375

376 377 378 379
    if (rows <= 0 || cols <= 0)
        return;

    if (this != srcSpace) {
380 381 382
	quint32 conversionBufferStride = cols * pixelSize();
        QVector<quint8> * conversionCache = 
	    threadLocalConversionCache(rows * conversionBufferStride);
383

384
        quint8* conversionData = conversionCache->data();
385 386 387

        for (qint32 row = 0; row < rows; row++) {
            srcSpace->convertPixelsTo(src + row * srcRowStride,
388
                                      conversionData + row * conversionBufferStride, this,
389 390 391
                                      cols);
        }

392 393
        op->composite( dst, dstRowStride,
                       conversionData, conversionBufferStride,
394 395 396 397 398 399
                       srcAlphaMask, maskRowStride,
                       rows,  cols,
                       opacity);

    }
    else {
400 401
        op->composite( dst, dstRowStride,
                       src, srcRowStride,
402
                       srcAlphaMask, maskRowStride,
403
                       rows, cols,
404 405 406
                       opacity);
    }
}
407 408 409 410 411

QVector<quint8> * KoColorSpace::threadLocalConversionCache(quint32 size) const
{
    QVector<quint8> * ba = 0;
    if ( !d->conversionCache.hasLocalData() ) {
412
        ba = new QVector<quint8>( size, '0' );
413 414 415 416 417 418 419 420 421
        d->conversionCache.setLocalData( ba );
    }
    else {
        ba = d->conversionCache.localData();
        if ( ( quint8 )ba->size() < size )
            ba->resize( size );
    }
    return ba;
}
422

Boudewijn Rempt's avatar
Boudewijn Rempt committed
423
KoColorTransformation* KoColorSpace::createColorTransformation( const QString & id, const QHash<QString, QVariant> & parameters) const
424 425
{
    KoColorTransformationFactory* factory = KoColorTransformationFactoryRegistry::instance()->get( id );
Boudewijn Rempt's avatar
Boudewijn Rempt committed
426
    if(!factory) return 0;
427 428 429 430 431 432 433
    QPair<KoID, KoID> model( colorModelId(), colorDepthId() );
    QList< QPair<KoID, KoID> > models = factory->supportedModels();
    if(models.contains(model))
    {
        return factory->createTransformation( this, parameters);
    } else {
        // Find the best solution
434
        // TODO use the color conversion cache
435 436 437 438 439 440 441 442 443
        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);
        return new KoFallBackColorTransformation( csToFallBack, fallBackToCs, transfo);
    }
}
444

445 446 447 448 449 450 451 452 453 454 455 456 457 458 459
QImage KoColorSpace::convertToQImage(const quint8 *data, qint32 width, qint32 height,
        const KoColorProfile *dstProfile,
        KoColorConversionTransformation::Intent renderingIntent) const

{
    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;
}