KoColorSpace.cpp 13.6 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 "KoColorSpace.h"
21
#include "KoColorSpace_p.h"
22

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

38 39 40 41
#include <QThreadStorage>
#include <QByteArray>
#include <QBitArray>

42

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

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

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

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

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

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

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

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

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

    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
116
                (alpha && channel->channelType() == KoChannelInfo::ALPHA))
117
            ba.setBit(i, true);
118 119 120 121
    }
    return ba;
}

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

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

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

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


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


156 157
KoConvolutionOp* KoColorSpace::convolutionOp() const
{
158
    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 {
Boudewijn Rempt's avatar
Boudewijn Rempt committed
166
        warnPigment << "Asking for non-existent composite operation " << id << ", returning " << COMPOSITE_OVER;
167
        return d->compositeOps.value(COMPOSITE_OVER);
168
    }
169 170 171 172
}

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

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

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

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

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

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

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

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

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

255

Boudewijn Rempt's avatar
Boudewijn Rempt committed
256 257 258
void KoColorSpace::bitBlt(const KoColorSpace* srcSpace, const KoCompositeOp::ParameterInfo& params, const KoCompositeOp* op,
                          KoColorConversionTransformation::Intent renderingIntent,
                          KoColorConversionTransformation::ConversionFlags conversionFlags) const
259 260
{
    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());
261

262 263
    if(params.rows <= 0 || params.cols <= 0)
        return;
264

265
    if(!(*this == *srcSpace)) {
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
         if (preferCompositionInSourceColorSpace() &&
             srcSpace->hasCompositeOp(op->id())) {

             quint32           conversionDstBufferStride = params.cols * srcSpace->pixelSize();
             QVector<quint8> * conversionDstCache        = threadLocalConversionCache(params.rows * conversionDstBufferStride);
             quint8*           conversionDstData         = conversionDstCache->data();

             for(qint32 row=0; row<params.rows; row++) {
                 convertPixelsTo(params.dstRowStart + row * params.dstRowStride,
                                 conversionDstData  + row * conversionDstBufferStride, srcSpace, params.cols,
                                 renderingIntent, conversionFlags);
             }

             // FIXME: do not calculate the otherOp every time
             const KoCompositeOp *otherOp = srcSpace->compositeOp(op->id());

             KoCompositeOp::ParameterInfo paramInfo(params);
             paramInfo.dstRowStart  = conversionDstData;
             paramInfo.dstRowStride = conversionDstBufferStride;
             otherOp->composite(paramInfo);

             for(qint32 row=0; row<params.rows; row++) {
                 srcSpace->convertPixelsTo(conversionDstData  + row * conversionDstBufferStride,
                                           params.dstRowStart + row * params.dstRowStride, this, params.cols,
                                           renderingIntent, conversionFlags);
             }

        } else {
            quint32           conversionBufferStride = params.cols * pixelSize();
            QVector<quint8> * conversionCache        = threadLocalConversionCache(params.rows * conversionBufferStride);
            quint8*           conversionData         = conversionCache->data();

            for(qint32 row=0; row<params.rows; row++) {
                srcSpace->convertPixelsTo(params.srcRowStart + row * params.srcRowStride,
                                          conversionData     + row * conversionBufferStride, this, params.cols,
                                          renderingIntent, conversionFlags);
            }

            KoCompositeOp::ParameterInfo paramInfo(params);
            paramInfo.srcRowStart  = conversionData;
            paramInfo.srcRowStride = conversionBufferStride;
            op->composite(paramInfo);
308 309
        }
    }
310 311 312
    else {
        op->composite(params);
    }
313 314 315
}


316 317 318
QVector<quint8> * KoColorSpace::threadLocalConversionCache(quint32 size) const
{
    QVector<quint8> * ba = 0;
319 320 321 322
    if (!d->conversionCache.hasLocalData()) {
        ba = new QVector<quint8>(size, '0');
        d->conversionCache.setLocalData(ba);
    } else {
323
        ba = d->conversionCache.localData();
324 325
        if ((quint8)ba->size() < size)
            ba->resize(size);
326 327 328
    }
    return ba;
}
329

330
KoColorTransformation* KoColorSpace::createColorTransformation(const QString & id, const QHash<QString, QVariant> & parameters) const
331
{
332 333 334
    KoColorTransformationFactory* factory = KoColorTransformationFactoryRegistry::instance()->get(id);
    if (!factory) return 0;
    QPair<KoID, KoID> model(colorModelId(), colorDepthId());
335
    QList< QPair<KoID, KoID> > models = factory->supportedModels();
336 337
    if (models.isEmpty() || models.contains(model)) {
        return factory->createTransformation(this, parameters);
338 339
    } else {
        // Find the best solution
340
        // TODO use the color conversion cache
341 342 343 344 345 346
        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);
347
        return new KoFallBackColorTransformation(csToFallBack, fallBackToCs, transfo);
348 349
    }
}
350

351
QImage KoColorSpace::convertToQImage(const quint8 *data, qint32 width, qint32 height,
Boudewijn Rempt's avatar
Boudewijn Rempt committed
352
                                     const KoColorProfile *dstProfile,
353
                                     KoColorConversionTransformation::Intent renderingIntent,
354
                                     KoColorConversionTransformation::ConversionFlags conversionFlags) const
355 356 357 358 359 360 361

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

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

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

    return img;
}
366 367 368 369 370

bool KoColorSpace::preferCompositionInSourceColorSpace() const
{
    return false;
}