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
#include "KoConvolutionOp.h"
38

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

43

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

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

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

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

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

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

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

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

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

    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
117
                (alpha && channel->channelType() == KoChannelInfo::ALPHA))
118
            ba.setBit(i, 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 141 142 143 144
bool KoColorSpace::hasCompositeOp(const QString& id) const
{
    return d->compositeOps.contains(id);
}

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


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


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

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

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

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

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

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

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

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

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

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

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

256

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

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

266
    if(!(*this == *srcSpace)) {
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 308
         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);
309 310
        }
    }
311 312 313
    else {
        op->composite(params);
    }
314 315 316
}


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

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

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

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

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

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

    return img;
}
367 368 369 370 371

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