KoColorSpace.cpp 14.3 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
#include <QThreadStorage>
#include <QByteArray>
21
#include <QBitArray>
22

23
#include "KoColorSpace.h"
24
#include "KoChannelInfo.h"
25 26
#include <kdebug.h>

27
#include "KoCompositeOp.h"
Cyrille Berger's avatar
Cyrille Berger committed
28
#include "KoColorTransformation.h"
29 30
#include "KoColorConversionSystem.h"
#include "KoColorSpaceRegistry.h"
31
#include "KoCopyColorConversionTransformation.h"
32

33 34 35 36 37
struct KoColorSpace::Private {
    QString id;
    QString name;
    QHash<QString, KoCompositeOp *> compositeOps;
    KoColorSpaceRegistry * parent;
Adrian Page's avatar
q3--  
Adrian Page committed
38
    QList<KoChannelInfo *> channels;
39 40
    KoMixColorsOp* mixColorsOp;
    KoConvolutionOp* convolutionOp;
41 42
    QThreadStorage< QVector<quint8>* > conversionCache;

43 44
    mutable const KoColorSpace *lastUsedDstColorSpace;
    mutable KoColorConversionTransformation* lastUsedTransform;
45 46 47 48 49
    
    mutable KoColorConversionTransformation* transfoToRGBA16;
    mutable KoColorConversionTransformation* transfoFromRGBA16;
    mutable KoColorConversionTransformation* transfoToLABA16;
    mutable KoColorConversionTransformation* transfoFromLABA16;
50 51 52 53 54

// cmsHTRANSFORM is a void *, so this should work.
    typedef QMap<const KoColorSpace *, KoColorConversionTransformation*>  TransformMap;
    mutable TransformMap transforms; // Cache for existing transforms

55 56
};

Stefan Nikolaus's avatar
Stefan Nikolaus committed
57 58 59 60 61
KoColorSpace::KoColorSpace()
    : d (new Private())
{
}

62
KoColorSpace::KoColorSpace(const QString &id, const QString &name, KoColorSpaceRegistry * parent, KoMixColorsOp* mixColorsOp, KoConvolutionOp* convolutionOp )
63
    : d (new Private())
64
{
65 66 67 68 69
    d->id = id;
    d->name = name;
    d->parent = parent;
    d->mixColorsOp = mixColorsOp;
    d->convolutionOp = convolutionOp;
70 71
    d->lastUsedDstColorSpace = 0;
    d->lastUsedTransform = 0;
72 73 74 75
    d->transfoToRGBA16 = 0;
    d->transfoFromRGBA16 = 0;
    d->transfoToLABA16 = 0;
    d->transfoFromLABA16 = 0;
76 77
}

78
KoColorSpace::~KoColorSpace()
79
{
80 81 82 83 84 85 86 87 88
    delete d->mixColorsOp;
    delete d->convolutionOp;
    delete d;
}

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

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

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

94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
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;
}

110
QBitArray KoColorSpace::setChannelFlagsToPixelOrder(const QBitArray & origChannelFlags) const
111
{
112 113 114
    if ( origChannelFlags.isEmpty() ) return origChannelFlags;

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

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

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;
134 135
}

136 137 138
void KoColorSpace::addChannel(KoChannelInfo * ci)
{
    d->channels.push_back(ci);
139 140
}

141
quint8 *KoColorSpace::allocPixelBuffer(quint32 numPixels) const
142 143 144 145
{
    return new quint8[pixelSize()*numPixels];
}

146
QList<KoCompositeOp*> KoColorSpace::userVisiblecompositeOps() const
147
{
148 149 150 151 152 153 154 155 156 157 158
    return d->compositeOps.values();
}


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


KoConvolutionOp* KoColorSpace::convolutionOp() const {
    return d->convolutionOp;
159 160
}

Cyrille Berger's avatar
Cyrille Berger committed
161
const KoCompositeOp * KoColorSpace::compositeOp(const QString & id) const
162
{
163 164
    if ( d->compositeOps.contains( id ) )
        return d->compositeOps.value( id );
165
    else
166
        return d->compositeOps.value( COMPOSITE_OVER );
167 168 169 170 171
}

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

176
const KoColorConversionTransformation* KoColorSpace::toLabA16Converter() const
177 178 179 180 181
{
    if(not d->transfoToLABA16)
    {
        d->transfoToLABA16 = d->parent->colorConversionSystem()->createColorConverter(this, KoColorSpaceRegistry::instance()->lab16("") ) ;
    }
182
    return d->transfoToLABA16;
183
}
184
const KoColorConversionTransformation* KoColorSpace::fromLabA16Converter() const
185 186 187 188 189
{
    if(not d->transfoFromLABA16)
    {
        d->transfoFromLABA16 = d->parent->colorConversionSystem()->createColorConverter( KoColorSpaceRegistry::instance()->lab16("") , this ) ;
    }
190
    return d->transfoFromLABA16;
191
}
192
const KoColorConversionTransformation* KoColorSpace::toRgbA16Converter() const
193 194 195 196 197
{
    if(not d->transfoToRGBA16)
    {
        d->transfoToRGBA16 = d->parent->colorConversionSystem()->createColorConverter( this, KoColorSpaceRegistry::instance()->rgb16("") ) ;
    }
198
    return d->transfoToRGBA16;
199
}
200
const KoColorConversionTransformation* KoColorSpace::fromRgbA16Converter() const
201 202 203 204 205
{
    if(not d->transfoFromRGBA16)
    {
        d->transfoFromRGBA16 = d->parent->colorConversionSystem()->createColorConverter( KoColorSpaceRegistry::instance()->rgb16("") , this ) ;
    }
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
    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);
227 228
}

229 230
KoColorConversionTransformation* KoColorSpace::createColorConverter(const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent) const
{
231 232
    if( this == dstColorSpace)
    {
233
        return new KoCopyColorConversionTransformation(this);
234 235 236
    } else {
        return d->parent->colorConversionSystem()->createColorConverter( this, dstColorSpace, renderingIntent);
    }
237 238
}

239
bool KoColorSpace::convertPixelsTo(const quint8 * src,
240 241 242 243
        quint8 * dst,
        const KoColorSpace * dstColorSpace,
        quint32 numPixels,
        KoColorConversionTransformation::Intent renderingIntent) const
244
{
245 246 247 248 249 250 251 252 253 254
    if (dstColorSpace->id() == this->id()
        && dstColorSpace->profile() == profile())
    {
        if (src!= dst)
            memcpy (dst, src, numPixels * this->pixelSize());

        return true;
    }

    KoColorConversionTransformation* tf = 0;
255

256 257 258 259 260
    if (d->lastUsedTransform != 0 && d->lastUsedDstColorSpace != 0) {
        if (dstColorSpace->id() == d->lastUsedDstColorSpace->id() &&
            dstColorSpace->profile() == d->lastUsedDstColorSpace->profile()) {
            tf = d->lastUsedTransform;
            }
261
    }
262

263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278
    if (not tf) {

        if (!d->transforms.contains(dstColorSpace)) {
    // XXX: Should we clear the transform cache if it gets too big?
            tf = this->createColorConverter(dstColorSpace, renderingIntent);
            d->transforms[dstColorSpace] = tf;
        }
        else {
            tf = d->transforms[dstColorSpace];
        }

        d->lastUsedTransform = tf;
        d->lastUsedDstColorSpace = dstColorSpace;
    }
    tf->transform(src, dst, numPixels);

279 280
    return true;
}
281

282

283 284 285 286 287 288 289 290 291 292 293
void KoColorSpace::bitBlt(quint8 *dst,
                          qint32 dststride,
                          KoColorSpace * srcSpace,
                          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
294
                          const QBitArray & channelFlags) const
295
{
296 297
    if ( d->compositeOps.contains( op ) ) {
        bitBlt(dst, dststride, srcSpace, src, srcRowStride, srcAlphaMask, maskRowStride, opacity, rows, cols, d->compositeOps.value( op ), channelFlags);
298 299
    }
    else {
300
        bitBlt(dst, dststride, srcSpace, src, srcRowStride, srcAlphaMask, maskRowStride, opacity, rows, cols, d->compositeOps.value( COMPOSITE_OVER ), channelFlags);
301 302 303 304 305 306 307 308 309 310 311 312 313 314
    }

}

void KoColorSpace::bitBlt(quint8 *dst,
                          qint32 dststride,
                          KoColorSpace * srcSpace,
                          const quint8 *src,
                          qint32 srcRowStride,
                          const quint8 *srcAlphaMask,
                          qint32 maskRowStride,
                          quint8 opacity,
                          qint32 rows,
                          qint32 cols,
Cyrille Berger's avatar
Cyrille Berger committed
315
                          const QString& op) const
316
{
317 318
    if ( d->compositeOps.contains( op ) ) {
        bitBlt(dst, dststride, srcSpace, src, srcRowStride, srcAlphaMask, maskRowStride, opacity, rows, cols, d->compositeOps.value( op ));
319 320
    }
    else {
321
        bitBlt(dst, dststride, srcSpace, src, srcRowStride, srcAlphaMask, maskRowStride, opacity, rows, cols, d->compositeOps.value( COMPOSITE_OVER ) );
322 323
    }
}
324 325

void KoColorSpace::bitBlt(quint8 *dst,
326 327 328 329 330 331 332 333 334 335 336
                          qint32 dststride,
                          KoColorSpace * srcSpace,
                          const quint8 *src,
                          qint32 srcRowStride,
                          const quint8 *srcAlphaMask,
                          qint32 maskRowStride,
                          quint8 opacity,
                          qint32 rows,
                          qint32 cols,
                          const KoCompositeOp * op,
                          const QBitArray & channelFlags) const
337
{
338 339
    Q_ASSERT(op->colorSpace() == this);

340 341 342 343 344 345
    if (rows <= 0 || cols <= 0)
        return;

    if (this != srcSpace) {
        quint32 len = pixelSize() * rows * cols;

346 347
        QVector<quint8> * conversionCache = threadLocalConversionCache(len);
        quint8* conversionData = conversionCache->data();
348 349 350

        for (qint32 row = 0; row < rows; row++) {
            srcSpace->convertPixelsTo(src + row * srcRowStride,
351
                                      conversionData + row * cols * pixelSize(), this,
352 353 354 355 356 357 358
                                      cols);
        }

        // The old srcRowStride is no longer valid because we converted to the current cs
        srcRowStride = cols * pixelSize();

        op->composite( dst, dststride,
359
                       conversionData, srcRowStride,
360 361 362 363 364 365 366 367 368 369 370 371 372 373
                       srcAlphaMask, maskRowStride,
                       rows,  cols,
                       opacity, channelFlags );

    }
    else {
        op->composite( dst, dststride,
                       src, srcRowStride,
                       srcAlphaMask, maskRowStride,
                       rows,  cols,
                       opacity, channelFlags );
    }
}

374
// XXX: I don't want this code duplication, but also don't want an
375 376 377
//      extra function call in this critical section of code. What to
//      do?
void KoColorSpace::bitBlt(quint8 *dst,
378 379 380 381 382 383 384 385 386 387
                          qint32 dststride,
                          KoColorSpace * srcSpace,
                          const quint8 *src,
                          qint32 srcRowStride,
                          const quint8 *srcAlphaMask,
                          qint32 maskRowStride,
                          quint8 opacity,
                          qint32 rows,
                          qint32 cols,
                          const KoCompositeOp * op) const
388
{
389 390
    Q_ASSERT(op->colorSpace() == this);

391 392 393 394 395 396
    if (rows <= 0 || cols <= 0)
        return;

    if (this != srcSpace) {
        quint32 len = pixelSize() * rows * cols;

397 398
        QVector<quint8> * conversionCache = threadLocalConversionCache(len);
        quint8* conversionData = conversionCache->data();
399 400 401

        for (qint32 row = 0; row < rows; row++) {
            srcSpace->convertPixelsTo(src + row * srcRowStride,
402
                                      conversionData + row * cols * pixelSize(), this,
403 404 405 406 407 408 409
                                      cols);
        }

        // The old srcRowStride is no longer valid because we converted to the current cs
        srcRowStride = cols * pixelSize();

        op->composite( dst, dststride,
410
                       conversionData, srcRowStride,
411 412 413 414 415 416 417 418 419 420 421 422 423
                       srcAlphaMask, maskRowStride,
                       rows,  cols,
                       opacity);

    }
    else {
        op->composite( dst, dststride,
                       src,srcRowStride,
                       srcAlphaMask, maskRowStride,
                       rows,  cols,
                       opacity);
    }
}
424 425 426 427 428

QVector<quint8> * KoColorSpace::threadLocalConversionCache(quint32 size) const
{
    QVector<quint8> * ba = 0;
    if ( !d->conversionCache.hasLocalData() ) {
429
        ba = new QVector<quint8>( size, '0' );
430 431 432 433 434 435 436 437 438
        d->conversionCache.setLocalData( ba );
    }
    else {
        ba = d->conversionCache.localData();
        if ( ( quint8 )ba->size() < size )
            ba->resize( size );
    }
    return ba;
}