KoColorSpace.cpp 9.57 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 22 23

#include <kdebug.h>

24
#include "KoCompositeOp.h"
Cyrille Berger's avatar
Cyrille Berger committed
25
#include "KoColorTransformation.h"
26

27 28 29 30 31 32 33 34 35 36 37 38 39
struct KoColorSpace::Private {
    QString id;
    QString name;
    QHash<QString, KoCompositeOp *> compositeOps;
    KoColorSpaceRegistry * parent;
    Q3ValueVector<KoChannelInfo *> channels;
    KoMixColorsOp* mixColorsOp;
    KoConvolutionOp* convolutionOp;
    mutable Q3MemArray<quint8> conversionCache; // XXX: This will be a bad problem when we have threading.
};

KoColorSpace::KoColorSpace(const QString &id, const QString &name, KoColorSpaceRegistry * parent, KoMixColorsOp* mixColorsOp, KoConvolutionOp* convolutionOp )
    : d (new Private())
40
{
41 42 43 44 45
  d->id = id;
  d->name = name;
  d->parent = parent;
  d->mixColorsOp = mixColorsOp;
  d->convolutionOp = convolutionOp;
46 47
}

48
KoColorSpace::~KoColorSpace()
49
{
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
    delete d->mixColorsOp;
    delete d->convolutionOp;
    delete d;
}

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

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

Q3ValueVector<KoChannelInfo *> KoColorSpace::channels() const
{
    return d->channels;
}

void KoColorSpace::addChannel(KoChannelInfo * ci)
{
    d->channels.push_back(ci);
67 68
}

69
quint8 *KoColorSpace::allocPixelBuffer(quint32 numPixels) const
70 71 72 73
{
    return new quint8[pixelSize()*numPixels];
}

74
QList<KoCompositeOp*> KoColorSpace::userVisiblecompositeOps() const
75
{
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
    return d->compositeOps.values();
}

void KoColorSpace::mixColors(const quint8 **colors, const quint8 *weights, quint32 nColors, quint8 *dst) const
{
    Q_ASSERT(d->mixColorsOp);
    d->mixColorsOp->mixColors(colors, weights, nColors, dst);
}

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

void KoColorSpace::convolveColors(quint8** colors, qint32* kernelValues, KoChannelInfo::enumChannelFlags channelFlags, quint8 *dst, qint32 factor, qint32 offset, qint32 nPixels) const
{
    Q_ASSERT(d->convolutionOp);
    d->convolutionOp->convolveColors(colors, kernelValues, channelFlags, dst, factor, offset, nPixels);
}

KoConvolutionOp* KoColorSpace::convolutionOp() const {
    return d->convolutionOp;
97 98
}

Cyrille Berger's avatar
Cyrille Berger committed
99
const KoCompositeOp * KoColorSpace::compositeOp(const QString & id) const
100
{
101 102
    if ( d->compositeOps.contains( id ) )
        return d->compositeOps.value( id );
103
    else
104
        return d->compositeOps.value( COMPOSITE_OVER );
105 106 107 108 109
}

void KoColorSpace::addCompositeOp(const KoCompositeOp * op)
{
    if ( op->colorSpace()->id() == id()) {
110
        d->compositeOps.insert( op->id(), const_cast<KoCompositeOp*>( op ) );
111 112 113 114
    }
}


115
bool KoColorSpace::convertPixelsTo(const quint8 * src,
116
                                   quint8 * dst,
Cyrille Berger's avatar
Cyrille Berger committed
117
                                   const KoColorSpace * dstColorSpace,
118
                                   quint32 numPixels,
Cyrille Berger's avatar
Cyrille Berger committed
119
                                   qint32 renderingIntent) const
120
{
121
    Q_UNUSED(renderingIntent);
122
    // 4 channels: labA, 2 bytes per lab channel
123
    quint8 *pixels = new quint8[sizeof(quint16)*4*numPixels];
124 125 126 127 128 129 130
    toLabA16(src, pixels,numPixels);
    dstColorSpace->fromLabA16(pixels, dst,numPixels);

    delete [] pixels;

    return true;
}
131 132 133 134 135 136 137 138 139 140 141 142

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
143
                          const QBitArray & channelFlags) const
144
{
145 146
    if ( d->compositeOps.contains( op ) ) {
        bitBlt(dst, dststride, srcSpace, src, srcRowStride, srcAlphaMask, maskRowStride, opacity, rows, cols, d->compositeOps.value( op ), channelFlags);
147 148
    }
    else {
149
        bitBlt(dst, dststride, srcSpace, src, srcRowStride, srcAlphaMask, maskRowStride, opacity, rows, cols, d->compositeOps.value( COMPOSITE_OVER ), channelFlags);
150 151 152 153 154 155 156 157 158 159 160 161 162 163
    }

}

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
164
                          const QString& op) const
165
{
166 167
    if ( d->compositeOps.contains( op ) ) {
        bitBlt(dst, dststride, srcSpace, src, srcRowStride, srcAlphaMask, maskRowStride, opacity, rows, cols, d->compositeOps.value( op ));
168 169
    }
    else {
170
        bitBlt(dst, dststride, srcSpace, src, srcRowStride, srcAlphaMask, maskRowStride, opacity, rows, cols, d->compositeOps.value( COMPOSITE_OVER ) );
171 172
    }
}
173 174 175 176 177 178 179 180 181 182 183 184

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 KoCompositeOp * op,
Cyrille Berger's avatar
Cyrille Berger committed
185
                                   const QBitArray & channelFlags) const
186 187 188 189 190 191 192 193
{
    if (rows <= 0 || cols <= 0)
        return;

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

        // If our conversion cache is too small, extend it.
194
        if (!d->conversionCache.resize( len, Q3GArray::SpeedOptim )) {
195 196 197 198 199 200 201
            kWarning() << "Could not allocate enough memory for the conversion!\n";
            // XXX: We should do a slow, pixel by pixel bitblt here...
            abort();
        }

        for (qint32 row = 0; row < rows; row++) {
            srcSpace->convertPixelsTo(src + row * srcRowStride,
202
                                      d->conversionCache.data() + row * cols * pixelSize(), this,
203 204 205 206 207 208 209
                                      cols);
        }

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

        op->composite( dst, dststride,
210
                       d->conversionCache.data(), srcRowStride,
211 212 213 214 215 216 217 218 219 220 221 222 223 224
                       srcAlphaMask, maskRowStride,
                       rows,  cols,
                       opacity, channelFlags );

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

225
// XXX: I don't want this code duplication, but also don't want an
226 227 228 229 230 231 232 233 234 235 236 237
//      extra function call in this critical section of code. What to
//      do?
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
238
                                   const KoCompositeOp * op) const
239 240 241 242 243 244 245 246
{
    if (rows <= 0 || cols <= 0)
        return;

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

        // If our conversion cache is too small, extend it.
247
        if (!d->conversionCache.resize( len, Q3GArray::SpeedOptim )) {
248 249 250 251 252 253 254
            kWarning() << "Could not allocate enough memory for the conversion!\n";
            // XXX: We should do a slow, pixel by pixel bitblt here...
            abort();
        }

        for (qint32 row = 0; row < rows; row++) {
            srcSpace->convertPixelsTo(src + row * srcRowStride,
255
                                      d->conversionCache.data() + row * cols * pixelSize(), this,
256 257 258 259 260 261 262
                                      cols);
        }

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

        op->composite( dst, dststride,
263
                       d->conversionCache.data(), srcRowStride,
264 265 266 267 268 269 270 271 272 273 274 275 276
                       srcAlphaMask, maskRowStride,
                       rows,  cols,
                       opacity);

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