KoColorSpace.cpp 11 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 31 32 33 34 35 36 37
struct KoColorSpace::Private {
    QString id;
    QString name;
    QHash<QString, KoCompositeOp *> compositeOps;
    KoColorSpaceRegistry * parent;
    Q3ValueVector<KoChannelInfo *> channels;
    KoMixColorsOp* mixColorsOp;
    KoConvolutionOp* convolutionOp;
38 39 40
    QThreadStorage< QVector<quint8>* > conversionCache;

    DWORD cmType;  // The colorspace type as defined by littlecms
41
    icColorSpaceSignature colorSpaceSignature; // The colorspace signature as defined in icm/icc files
42 43
};

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

49
KoColorSpace::KoColorSpace(const QString &id, const QString &name, KoColorSpaceRegistry * parent, KoMixColorsOp* mixColorsOp, KoConvolutionOp* convolutionOp, DWORD cmType, icColorSpaceSignature colorSpaceSignature )
50
    : d (new Private())
51
{
52 53 54 55 56 57 58
    d->id = id;
    d->name = name;
    d->parent = parent;
    d->mixColorsOp = mixColorsOp;
    d->convolutionOp = convolutionOp;
    d->cmType = cmType;
    d->colorSpaceSignature = colorSpaceSignature;
59 60
}

61
KoColorSpace::~KoColorSpace()
62
{
63 64 65 66 67 68 69 70 71
    delete d->mixColorsOp;
    delete d->convolutionOp;
    delete d;
}

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

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

72 73 74
quint32 KoColorSpace::colorSpaceType() const { return d->cmType; }
icColorSpaceSignature KoColorSpace::colorSpaceSignature() const { return d->colorSpaceSignature; }

75 76 77 78 79
Q3ValueVector<KoChannelInfo *> KoColorSpace::channels() const
{
    return d->channels;
}

80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
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;
}

96
QBitArray KoColorSpace::setChannelFlagsToPixelOrder(const QBitArray & origChannelFlags) const
97
{
98 99 100
    if ( origChannelFlags.isEmpty() ) return origChannelFlags;

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

103 104 105 106
        KoChannelInfo * channel = d->channels.at( i );
        orderedChannelFlags.setBit( channel->pos(), origChannelFlags.testBit( i ) );
    }
    return orderedChannelFlags;
107 108 109 110 111 112 113 114 115 116 117 118 119
}

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;
120 121
}

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

127
quint8 *KoColorSpace::allocPixelBuffer(quint32 numPixels) const
128 129 130 131
{
    return new quint8[pixelSize()*numPixels];
}

132
QList<KoCompositeOp*> KoColorSpace::userVisiblecompositeOps() const
133
{
134 135 136 137 138 139 140 141 142 143 144
    return d->compositeOps.values();
}


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


KoConvolutionOp* KoColorSpace::convolutionOp() const {
    return d->convolutionOp;
145 146
}

Cyrille Berger's avatar
Cyrille Berger committed
147
const KoCompositeOp * KoColorSpace::compositeOp(const QString & id) const
148
{
149 150
    if ( d->compositeOps.contains( id ) )
        return d->compositeOps.value( id );
151
    else
152
        return d->compositeOps.value( COMPOSITE_OVER );
153 154 155 156 157
}

void KoColorSpace::addCompositeOp(const KoCompositeOp * op)
{
    if ( op->colorSpace()->id() == id()) {
158
        d->compositeOps.insert( op->id(), const_cast<KoCompositeOp*>( op ) );
159 160 161
    }
}

162
bool KoColorSpace::convertPixelsTo(const quint8 * src,
163
                                   quint8 * dst,
Cyrille Berger's avatar
Cyrille Berger committed
164
                                   const KoColorSpace * dstColorSpace,
165
                                   quint32 numPixels,
Cyrille Berger's avatar
Cyrille Berger committed
166
                                   qint32 renderingIntent) const
167
{
168
    Q_UNUSED(renderingIntent);
169
    // 4 channels: labA, 2 bytes per lab channel
170
    quint8 *pixels = new quint8[sizeof(quint16)*4*numPixels];
171 172 173 174 175 176 177
    toLabA16(src, pixels,numPixels);
    dstColorSpace->fromLabA16(pixels, dst,numPixels);

    delete [] pixels;

    return true;
}
178 179 180 181 182 183 184 185 186 187 188 189

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
190
                          const QBitArray & channelFlags) const
191
{
192 193
    if ( d->compositeOps.contains( op ) ) {
        bitBlt(dst, dststride, srcSpace, src, srcRowStride, srcAlphaMask, maskRowStride, opacity, rows, cols, d->compositeOps.value( op ), channelFlags);
194 195
    }
    else {
196
        bitBlt(dst, dststride, srcSpace, src, srcRowStride, srcAlphaMask, maskRowStride, opacity, rows, cols, d->compositeOps.value( COMPOSITE_OVER ), channelFlags);
197 198 199 200 201 202 203 204 205 206 207 208 209 210
    }

}

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
211
                          const QString& op) const
212
{
213 214
    if ( d->compositeOps.contains( op ) ) {
        bitBlt(dst, dststride, srcSpace, src, srcRowStride, srcAlphaMask, maskRowStride, opacity, rows, cols, d->compositeOps.value( op ));
215 216
    }
    else {
217
        bitBlt(dst, dststride, srcSpace, src, srcRowStride, srcAlphaMask, maskRowStride, opacity, rows, cols, d->compositeOps.value( COMPOSITE_OVER ) );
218 219
    }
}
220 221

void KoColorSpace::bitBlt(quint8 *dst,
222 223 224 225 226 227 228 229 230 231 232
                          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
233 234 235 236 237 238 239
{
    if (rows <= 0 || cols <= 0)
        return;

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

240 241
        QVector<quint8> * conversionCache = threadLocalConversionCache(len);
        quint8* conversionData = conversionCache->data();
242 243 244

        for (qint32 row = 0; row < rows; row++) {
            srcSpace->convertPixelsTo(src + row * srcRowStride,
245
                                      conversionData + row * cols * pixelSize(), this,
246 247 248 249 250 251 252
                                      cols);
        }

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

        op->composite( dst, dststride,
253
                       conversionData, srcRowStride,
254 255 256 257 258 259 260 261 262 263 264 265 266 267
                       srcAlphaMask, maskRowStride,
                       rows,  cols,
                       opacity, channelFlags );

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

268
// XXX: I don't want this code duplication, but also don't want an
269 270 271
//      extra function call in this critical section of code. What to
//      do?
void KoColorSpace::bitBlt(quint8 *dst,
272 273 274 275 276 277 278 279 280 281
                          qint32 dststride,
                          KoColorSpace * srcSpace,
                          const quint8 *src,
                          qint32 srcRowStride,
                          const quint8 *srcAlphaMask,
                          qint32 maskRowStride,
                          quint8 opacity,
                          qint32 rows,
                          qint32 cols,
                          const KoCompositeOp * op) const
282 283 284 285 286 287 288
{
    if (rows <= 0 || cols <= 0)
        return;

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

289 290
        QVector<quint8> * conversionCache = threadLocalConversionCache(len);
        quint8* conversionData = conversionCache->data();
291 292 293

        for (qint32 row = 0; row < rows; row++) {
            srcSpace->convertPixelsTo(src + row * srcRowStride,
294
                                      conversionData + row * cols * pixelSize(), this,
295 296 297 298 299 300 301
                                      cols);
        }

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

        op->composite( dst, dststride,
302
                       conversionData, srcRowStride,
303 304 305 306 307 308 309 310 311 312 313 314 315
                       srcAlphaMask, maskRowStride,
                       rows,  cols,
                       opacity);

    }
    else {
        op->composite( dst, dststride,
                       src,srcRowStride,
                       srcAlphaMask, maskRowStride,
                       rows,  cols,
                       opacity);
    }
}
316 317 318 319 320 321 322 323 324 325 326 327 328 329 330

QVector<quint8> * KoColorSpace::threadLocalConversionCache(quint32 size) const
{
    QVector<quint8> * ba = 0;
    if ( !d->conversionCache.hasLocalData() ) {
        ba = new QVector<quint8>( '0', size );
        d->conversionCache.setLocalData( ba );
    }
    else {
        ba = d->conversionCache.localData();
        if ( ( quint8 )ba->size() < size )
            ba->resize( size );
    }
    return ba;
}