KoColor.cpp 10 KB
Newer Older
1 2
/*
 *  Copyright (c) 2005 Boudewijn Rempt <boud@valdyas.org>
Thomas Zander's avatar
Thomas Zander committed
3
 *  Copyright (C) 2007 Thomas Zander <zander@kde.org>
4
 *  Copyright (C) 2007 Cyrille Berger <cberger@cberger.net>
5
 *
6
 * This library is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU Lesser General Public
8 9
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
10
 *
11 12 13
 * 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
14
 * Lesser General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Lesser General Public License
17 18 19 20
 * 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.
*/
21 22 23

#include "KoColor.h"

24
#include <QColor>
Thomas Zander's avatar
Thomas Zander committed
25

26 27
#include <QDomDocument>

Boudewijn Rempt's avatar
Boudewijn Rempt committed
28
#include "DebugPigment.h"
29

30
#include "KoColorModelStandardIds.h"
31 32
#include "KoColorProfile.h"
#include "KoColorSpace.h"
33
#include "KoColorSpaceRegistry.h"
34
#include "KoChannelInfo.h"
35

36

37 38
class KoColor::Private
{
Thomas Zander's avatar
Thomas Zander committed
39 40
public:
    Private() : data(0), colorSpace(0) {}
41

Thomas Zander's avatar
Thomas Zander committed
42
    ~Private() {
43
        delete [] data;
Thomas Zander's avatar
Thomas Zander committed
44
    }
45

Thomas Zander's avatar
Thomas Zander committed
46
    quint8 * data;
47
    const KoColorSpace * colorSpace;
Thomas Zander's avatar
Thomas Zander committed
48 49
};

50
KoColor::KoColor()
51
        : d(new Private())
52
{
53
    d->colorSpace = KoColorSpaceRegistry::instance()->lab16(0);
Thomas Zander's avatar
Thomas Zander committed
54 55
    d->data = new quint8[d->colorSpace->pixelSize()];
    memset(d->data, 0, d->colorSpace->pixelSize());
56
    d->colorSpace->setOpacity(d->data, OPACITY_OPAQUE_U8, 1);
57
}
58

59
KoColor::KoColor(const KoColorSpace * colorSpace)
60
        : d(new Private())
61
{
Cyrille Berger's avatar
Cyrille Berger committed
62
    Q_ASSERT(colorSpace);
63
    d->colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(colorSpace);
Thomas Zander's avatar
Thomas Zander committed
64 65
    d->data = new quint8[d->colorSpace->pixelSize()];
    memset(d->data, 0, d->colorSpace->pixelSize());
66 67
}

68 69 70

KoColor::~KoColor()
{
Thomas Zander's avatar
Thomas Zander committed
71
    delete d;
72 73
}

74
KoColor::KoColor(const QColor & color, const KoColorSpace * colorSpace)
75
        : d(new Private())
76 77 78
{
    Q_ASSERT(color.isValid());
    Q_ASSERT(colorSpace);
79
    d->colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(colorSpace);
80

Thomas Zander's avatar
Thomas Zander committed
81 82 83
    d->data = new quint8[colorSpace->pixelSize()];
    memset(d->data, 0, d->colorSpace->pixelSize());

84
    d->colorSpace->fromQColor(color, d->data);
85 86
}

87
KoColor::KoColor(const quint8 * data, const KoColorSpace * colorSpace)
88
        : d(new Private())
89
{
Cyrille Berger's avatar
Cyrille Berger committed
90 91
    Q_ASSERT(colorSpace);
    Q_ASSERT(data);
92
    d->colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(colorSpace);
Thomas Zander's avatar
Thomas Zander committed
93 94 95
    d->data = new quint8[colorSpace->pixelSize()];
    memset(d->data, 0, d->colorSpace->pixelSize());
    memmove(d->data, data, colorSpace->pixelSize());
96 97 98
}


99
KoColor::KoColor(const KoColor &src, const KoColorSpace * colorSpace)
100
        : d(new Private())
101
{
Cyrille Berger's avatar
Cyrille Berger committed
102
    Q_ASSERT(colorSpace);
103
    d->colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(colorSpace);
Thomas Zander's avatar
Thomas Zander committed
104 105
    d->data = new quint8[colorSpace->pixelSize()];
    memset(d->data, 0, d->colorSpace->pixelSize());
106

107
    src.colorSpace()->convertPixelsTo(src.d->data, d->data, colorSpace, 1, KoColorConversionTransformation::InternalRenderingIntent, KoColorConversionTransformation::InternalConversionFlags);
108 109
}

110
KoColor::KoColor(const KoColor & rhs)
111
        : d(new Private())
112
{
Thomas Zander's avatar
Thomas Zander committed
113
    d->colorSpace = rhs.colorSpace();
114
    Q_ASSERT(*d->colorSpace == *KoColorSpaceRegistry::instance()->permanentColorspace(d->colorSpace));
115
    if (d->colorSpace && rhs.d->data) {
Thomas Zander's avatar
Thomas Zander committed
116 117
        d->data = new quint8[d->colorSpace->pixelSize()];
        memcpy(d->data, rhs.d->data, d->colorSpace->pixelSize());
118
    }
119 120 121 122
}

KoColor & KoColor::operator=(const KoColor & rhs)
{
123 124
    if (this == &rhs) return *this;

Thomas Zander's avatar
Thomas Zander committed
125 126 127
    delete [] d->data;
    d->data = 0;
    d->colorSpace = rhs.colorSpace();
128

Thomas Zander's avatar
Thomas Zander committed
129
    if (rhs.d->colorSpace && rhs.d->data) {
130
        Q_ASSERT(d->colorSpace == KoColorSpaceRegistry::instance()->permanentColorspace(d->colorSpace)); // here we want to do a check on pointer, since d->colorSpace is supposed to already be a permanent one
Thomas Zander's avatar
Thomas Zander committed
131 132
        d->data = new quint8[d->colorSpace->pixelSize()];
        memcpy(d->data, rhs.d->data, d->colorSpace->pixelSize());
133
    }
134 135 136
    return * this;
}

137 138 139 140 141 142 143 144 145
bool KoColor::operator<(const KoColor &other) const
{
    QColor c = toQColor();
    int gray = qGray(c.red(), c.green(), c.blue());

    QColor c2 = other.toQColor();
    return gray > qGray(c2.red(), c2.green(), c2.blue());
}

146
bool KoColor::operator==(const KoColor &other) const
147
{
148 149
    if (!(*colorSpace() == *other.colorSpace()))
        return false;
150 151 152
    return memcmp(d->data, other.d->data, d->colorSpace->pixelSize()) == 0;
}

153
void KoColor::convertTo(const KoColorSpace * cs, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags)
154
{
Boudewijn Rempt's avatar
Boudewijn Rempt committed
155
    //dbgPigment <<"Our colormodel:" << d->colorSpace->id().name()
156 157
    //      << ", new colormodel: " << cs->id().name() << "\n";

158
    if (*d->colorSpace == *cs)
159 160
        return;

Thomas Zander's avatar
Thomas Zander committed
161 162
    quint8 * data = new quint8[cs->pixelSize()];
    memset(data, 0, cs->pixelSize());
163

164
    d->colorSpace->convertPixelsTo(d->data, data, cs, 1, renderingIntent, conversionFlags);
165

Thomas Zander's avatar
Thomas Zander committed
166 167
    delete [] d->data;
    d->data = data;
168
    d->colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(cs);
169 170
}

171 172 173 174 175 176
void KoColor::convertTo(const KoColorSpace * cs)
{
    convertTo(cs,
              KoColorConversionTransformation::InternalRenderingIntent,
              KoColorConversionTransformation::InternalConversionFlags);
}
177

178
void KoColor::setColor(const quint8 * data, const KoColorSpace * colorSpace)
179
{
Cyrille Berger's avatar
Cyrille Berger committed
180 181
    Q_ASSERT(data);
    Q_ASSERT(colorSpace);
182 183 184 185 186
    if(d->colorSpace->pixelSize() != colorSpace->pixelSize())
    {
        delete [] d->data;
        d->data = new quint8[colorSpace->pixelSize()];
    }
Thomas Zander's avatar
Thomas Zander committed
187
    memcpy(d->data, data, colorSpace->pixelSize());
188
    d->colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(colorSpace);
189 190
}

191 192 193
// To save the user the trouble of doing color->colorSpace()->toQColor(color->data(), &c, &a, profile
void KoColor::toQColor(QColor *c) const
{
Cyrille Berger's avatar
Cyrille Berger committed
194
    Q_ASSERT(c);
Thomas Zander's avatar
Thomas Zander committed
195 196
    if (d->colorSpace && d->data) {
        d->colorSpace->toQColor(d->data, c);
197
    }
198 199 200 201 202 203 204 205
}

QColor KoColor::toQColor() const
{
    QColor c;
    toQColor(&c);
    return c;
}
206

207 208
void KoColor::fromQColor(const QColor& c) const
{
Thomas Zander's avatar
Thomas Zander committed
209 210
    if (d->colorSpace && d->data) {
        d->colorSpace->fromQColor(c, d->data);
211 212 213
    }
}

Thomas Zander's avatar
Thomas Zander committed
214
#ifndef NDEBUG
215 216
void KoColor::dump() const
{
Boudewijn Rempt's avatar
Boudewijn Rempt committed
217
    //dbgPigment <<"KoColor (" << this <<")," << d->colorSpace->id().name() <<"";
Adrian Page's avatar
q3--  
Adrian Page committed
218
    QList<KoChannelInfo *> channels = d->colorSpace->channels();
219

Laurent Montel's avatar
Laurent Montel committed
220 221
    QList<KoChannelInfo *>::const_iterator begin = channels.constBegin();
    QList<KoChannelInfo *>::const_iterator end = channels.constEnd();
222

223
    for (QList<KoChannelInfo *>::const_iterator it = begin; it != end; ++it) {
224 225 226 227
        KoChannelInfo * ch = (*it);
        // XXX: setNum always takes a byte.
        if (ch->size() == sizeof(quint8)) {
            // Byte
Boudewijn Rempt's avatar
Boudewijn Rempt committed
228
            //dbgPigment <<"Channel (byte):" << ch->name() <<":" << QString().setNum(d->data[ch->pos()]) <<"";
229
        } else if (ch->size() == sizeof(quint16)) {
230
            // Short (may also by an nvidia half)
Boudewijn Rempt's avatar
Boudewijn Rempt committed
231
            //dbgPigment <<"Channel (short):" << ch->name() <<":" << QString().setNum(*((const quint16 *)(d->data+ch->pos())))  <<"";
232
        } else if (ch->size() == sizeof(quint32)) {
233
            // Integer (may also be float... Find out how to distinguish these!)
Boudewijn Rempt's avatar
Boudewijn Rempt committed
234
            //dbgPigment <<"Channel (int):" << ch->name() <<":" << QString().setNum(*((const quint32 *)(d->data+ch->pos())))  <<"";
235 236 237
        }
    }
}
Thomas Zander's avatar
Thomas Zander committed
238
#endif
239 240 241

void KoColor::fromKoColor(const KoColor& src)
{
242
    src.colorSpace()->convertPixelsTo(src.d->data, d->data, colorSpace(), 1, KoColorConversionTransformation::InternalRenderingIntent, KoColorConversionTransformation::InternalConversionFlags);
243
}
Cyrille Berger's avatar
Cyrille Berger committed
244

245
const KoColorProfile *  KoColor::profile() const
Cyrille Berger's avatar
Cyrille Berger committed
246
{
Thomas Zander's avatar
Thomas Zander committed
247 248 249
    return d->colorSpace->profile();
}

250 251
quint8 * KoColor::data()
{
Thomas Zander's avatar
Thomas Zander committed
252 253 254
    return d->data;
}

255 256
const quint8 * KoColor::data() const
{
Thomas Zander's avatar
Thomas Zander committed
257
    return d->data;
Cyrille Berger's avatar
Cyrille Berger committed
258
}
Thomas Zander's avatar
Thomas Zander committed
259

260 261
const KoColorSpace * KoColor::colorSpace() const
{
Thomas Zander's avatar
Thomas Zander committed
262 263 264
    return d->colorSpace;
}

265 266
void KoColor::toXML(QDomDocument& doc, QDomElement& colorElt) const
{
267
    d->colorSpace->colorToXML(d->data, doc, colorElt);
268 269
}

270
void KoColor::setOpacity(quint8 alpha)
271
{
272
    d->colorSpace->setOpacity(d->data, alpha, 1);
273
}
274
void KoColor::setOpacity(qreal alpha)
275
{
276 277 278 279 280 281 282 283 284
    d->colorSpace->setOpacity(d->data, alpha, 1);
}
quint8 KoColor::opacityU8() const
{
    return d->colorSpace->opacityU8(d->data);
}
qreal KoColor::opacityF() const
{
    return d->colorSpace->opacityF(d->data);
285 286
}

Boudewijn Rempt's avatar
Boudewijn Rempt committed
287
KoColor KoColor::fromXML(const QDomElement& elt, const QString & bitDepthId, const QHash<QString, QString> & aliases)
288
{
289
    QString modelId;
290
    if (elt.tagName() == "CMYK") {
291
        modelId = CMYKAColorModelID.id();
292
    } else if (elt.tagName() == "RGB") {
293
        modelId = RGBAColorModelID.id();
294
    } else if (elt.tagName() == "sRGB") {
295
        modelId = RGBAColorModelID.id();
296
    } else if (elt.tagName() == "Lab") {
297
        modelId = LABAColorModelID.id();
298
    } else if (elt.tagName() == "XYZ") {
299
        modelId = XYZAColorModelID.id();
300
    } else if (elt.tagName() == "Gray") {
301
        modelId = GrayAColorModelID.id();
302
    } else if (elt.tagName() == "YCbCr") {
303 304 305
        modelId = YCbCrAColorModelID.id();
    }
    QString profileName;
306 307 308
    if (elt.tagName() != "sRGB") {
        profileName = elt.attribute("space", "");
        if (aliases.contains(profileName)) {
309 310
            profileName = aliases.value(profileName);
        }
311
        if (!KoColorSpaceRegistry::instance()->profileByName(profileName)) {
312
            profileName.clear();
313
        }
314
    }
315 316
    const KoColorSpace* cs = KoColorSpaceRegistry::instance()->colorSpace(modelId, bitDepthId, profileName);
    if (cs == 0) {
317 318
        QList<KoID> list =  KoColorSpaceRegistry::instance()->colorDepthList(modelId, KoColorSpaceRegistry::AllColorSpaces);
        if (!list.empty()) {
319
            cs = KoColorSpaceRegistry::instance()->colorSpace(modelId, list[0].id(), profileName);
320 321
        }
    }
322
    if (cs) {
323
        KoColor c(cs);
324
        cs->colorFromXML(c.data(), elt);
325 326 327 328
        return c;
    } else {
        return KoColor();
    }
329
}