libs/pigment

KoColorSpace.cpp

Go to the documentation of this file.
00001 /*
00002  *  Copyright (c) 2005 Boudewijn Rempt <boud@valdyas.org>
00003  *
00004  * This library is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU Lesser General Public
00006  * License as published by the Free Software Foundation; either
00007  * version 2 of the License, or (at your option) any later version.
00008  *
00009  * This library is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  * Lesser General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU Lesser General Public License
00015  * along with this library; see the file COPYING.LIB.  If not, write to
00016  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  * Boston, MA 02110-1301, USA.
00018 */
00019 
00020 #include "KoColorSpace.h"
00021 
00022 #include <QThreadStorage>
00023 #include <QByteArray>
00024 #include <QBitArray>
00025 
00026 #include "KoChannelInfo.h"
00027 #include "DebugPigment.h"
00028 #include "KoCompositeOp.h"
00029 #include "KoColorTransformation.h"
00030 #include "KoColorTransformationFactory.h"
00031 #include "KoColorTransformationFactoryRegistry.h"
00032 #include "KoColorConversionCache.h"
00033 #include "KoColorConversionSystem.h"
00034 #include "KoColorSpaceRegistry.h"
00035 #include "KoColorProfile.h"
00036 #include "KoCopyColorConversionTransformation.h"
00037 #include "KoFallBackColorTransformation.h"
00038 #include "KoUniqueNumberForIdServer.h"
00039 
00040 #include "KoColorSpace_p.h"
00041 
00042 KoColorSpace::KoColorSpace()
00043         : d(new Private())
00044 {
00045 }
00046 
00047 KoColorSpace::KoColorSpace(const QString &id, const QString &name, KoMixColorsOp* mixColorsOp, KoConvolutionOp* convolutionOp)
00048         : d(new Private())
00049 {
00050     d->id = id;
00051     d->idNumber = KoUniqueNumberForIdServer::instance()->numberForId(d->id);
00052     d->name = name;
00053     d->mixColorsOp = mixColorsOp;
00054     d->convolutionOp = convolutionOp;
00055     d->transfoToRGBA16 = 0;
00056     d->transfoFromRGBA16 = 0;
00057     d->transfoToLABA16 = 0;
00058     d->transfoFromLABA16 = 0;
00059     d->deletability = NotOwnedByRegistry;
00060 }
00061 
00062 KoColorSpace::~KoColorSpace()
00063 {
00064     Q_ASSERT(d->deletability != OwnedByRegistryDoNotDelete);
00065 
00066     qDeleteAll(d->compositeOps);
00067     foreach(KoChannelInfo * channel, d->channels) {
00068         delete channel;
00069     }
00070     if (d->deletability == NotOwnedByRegistry) {
00071         KoColorConversionCache* cache = KoColorSpaceRegistry::instance()->colorConversionCache();
00072         if (cache) {
00073             cache->colorSpaceIsDestroyed(this);
00074         }
00075     }
00076     delete d->mixColorsOp;
00077     delete d->convolutionOp;
00078     delete d;
00079 }
00080 
00081 bool KoColorSpace::operator==(const KoColorSpace& rhs) const
00082 {
00083     const KoColorProfile* p1 = rhs.profile();
00084     const KoColorProfile* p2 = profile();
00085     return d->idNumber == rhs.d->idNumber && ((p1 == p2) || (*p1 == *p2));
00086 }
00087 
00088 QString KoColorSpace::id() const
00089 {
00090     return d->id;
00091 }
00092 
00093 QString KoColorSpace::name() const
00094 {
00095     return d->name;
00096 }
00097 
00098 QList<KoChannelInfo *> KoColorSpace::channels() const
00099 {
00100     return d->channels;
00101 }
00102 
00103 QBitArray KoColorSpace::channelFlags(bool color, bool alpha, bool substance, bool substrate) const
00104 {
00105     QBitArray ba(d->channels.size());
00106     if (!color && !alpha && !substance && !substrate) return ba;
00107 
00108     for (int i = 0; i < d->channels.size(); ++i) {
00109         KoChannelInfo * channel = d->channels.at(i);
00110         if ((color && channel->channelType() == KoChannelInfo::COLOR) ||
00111                 (alpha && channel->channelType() == KoChannelInfo::ALPHA) ||
00112                 (substrate && channel->channelType() == KoChannelInfo::SUBSTRATE) ||
00113                 (substance && channel->channelType() == KoChannelInfo::SUBSTANCE))
00114             ba.setBit(i, true);
00115     }
00116     return ba;
00117 }
00118 
00119 QBitArray KoColorSpace::setChannelFlagsToPixelOrder(const QBitArray & origChannelFlags) const
00120 {
00121     if (origChannelFlags.isEmpty()) return origChannelFlags;
00122 
00123     QBitArray orderedChannelFlags(origChannelFlags.size());
00124     for (int i = 0; i < origChannelFlags.size(); ++i) {
00125 
00126         KoChannelInfo * channel = d->channels.at(i);
00127         orderedChannelFlags.setBit(channel->pos(), origChannelFlags.testBit(i));
00128     }
00129     return orderedChannelFlags;
00130 }
00131 
00132 QBitArray KoColorSpace::setChannelFlagsToColorSpaceOrder(const QBitArray & origChannelFlags) const
00133 {
00134     if (origChannelFlags.isEmpty()) return origChannelFlags;
00135 
00136     QBitArray orderedChannelFlags(origChannelFlags.size());
00137     for (int i = 0; i < orderedChannelFlags.size(); ++i) {
00138         KoChannelInfo * channel = d->channels.at(i);
00139         orderedChannelFlags.setBit(i, origChannelFlags.testBit(channel->pos()));
00140     }
00141     return orderedChannelFlags;
00142 }
00143 
00144 void KoColorSpace::addChannel(KoChannelInfo * ci)
00145 {
00146     d->channels.push_back(ci);
00147 }
00148 
00149 quint8 *KoColorSpace::allocPixelBuffer(quint32 numPixels, bool clear, quint8 defaultvalue) const
00150 {
00151     if (numPixels == 0) {
00152         return 0;
00153     }
00154     quint8* buf = new quint8[pixelSize()*numPixels];
00155     if (clear) {
00156         memset(buf, defaultvalue, pixelSize() * numPixels);
00157     }
00158     return buf;
00159 }
00160 
00161 QList<KoCompositeOp*> KoColorSpace::compositeOps() const
00162 {
00163     return d->compositeOps.values();
00164 }
00165 
00166 
00167 KoMixColorsOp* KoColorSpace::mixColorsOp() const
00168 {
00169     return d->mixColorsOp;
00170 }
00171 
00172 
00173 KoConvolutionOp* KoColorSpace::convolutionOp() const
00174 {
00175     return d->convolutionOp;
00176 }
00177 
00178 const KoCompositeOp * KoColorSpace::compositeOp(const QString & id) const
00179 {
00180     if (d->compositeOps.contains(id))
00181         return d->compositeOps.value(id);
00182     else {
00183         warnPigment << "Asking for non-existent composite operation " << id << ", returning " << COMPOSITE_OVER;
00184         return d->compositeOps.value(COMPOSITE_OVER);
00185     }
00186 }
00187 
00188 void KoColorSpace::addCompositeOp(const KoCompositeOp * op)
00189 {
00190     if (op->colorSpace()->id() == id()) {
00191         d->compositeOps.insert(op->id(), const_cast<KoCompositeOp*>(op));
00192     }
00193 }
00194 
00195 const KoColorConversionTransformation* KoColorSpace::toLabA16Converter() const
00196 {
00197     if (!d->transfoToLABA16) {
00198         d->transfoToLABA16 = KoColorSpaceRegistry::instance()->colorConversionSystem()->createColorConverter(this, KoColorSpaceRegistry::instance()->lab16("")) ;
00199     }
00200     return d->transfoToLABA16;
00201 }
00202 
00203 const KoColorConversionTransformation* KoColorSpace::fromLabA16Converter() const
00204 {
00205     if (!d->transfoFromLABA16) {
00206         d->transfoFromLABA16 = KoColorSpaceRegistry::instance()->colorConversionSystem()->createColorConverter(KoColorSpaceRegistry::instance()->lab16("") , this) ;
00207     }
00208     return d->transfoFromLABA16;
00209 }
00210 const KoColorConversionTransformation* KoColorSpace::toRgbA16Converter() const
00211 {
00212     if (!d->transfoToRGBA16) {
00213         d->transfoToRGBA16 = KoColorSpaceRegistry::instance()->colorConversionSystem()->createColorConverter(this, KoColorSpaceRegistry::instance()->rgb16("")) ;
00214     }
00215     return d->transfoToRGBA16;
00216 }
00217 const KoColorConversionTransformation* KoColorSpace::fromRgbA16Converter() const
00218 {
00219     if (!d->transfoFromRGBA16) {
00220         d->transfoFromRGBA16 = KoColorSpaceRegistry::instance()->colorConversionSystem()->createColorConverter(KoColorSpaceRegistry::instance()->rgb16("") , this) ;
00221     }
00222     return d->transfoFromRGBA16;
00223 }
00224 
00225 void KoColorSpace::toLabA16(const quint8 * src, quint8 * dst, quint32 nPixels) const
00226 {
00227     toLabA16Converter()->transform(src, dst, nPixels);
00228 }
00229 
00230 void KoColorSpace::fromLabA16(const quint8 * src, quint8 * dst, quint32 nPixels) const
00231 {
00232     fromLabA16Converter()->transform(src, dst, nPixels);
00233 }
00234 
00235 void KoColorSpace::toRgbA16(const quint8 * src, quint8 * dst, quint32 nPixels) const
00236 {
00237     toRgbA16Converter()->transform(src, dst, nPixels);
00238 }
00239 
00240 void KoColorSpace::fromRgbA16(const quint8 * src, quint8 * dst, quint32 nPixels) const
00241 {
00242     fromRgbA16Converter()->transform(src, dst, nPixels);
00243 }
00244 
00245 KoColorConversionTransformation* KoColorSpace::createColorConverter(const KoColorSpace * dstColorSpace, KoColorConversionTransformation::Intent renderingIntent) const
00246 {
00247     if (*this == *dstColorSpace) {
00248         return new KoCopyColorConversionTransformation(this);
00249     } else {
00250         return KoColorSpaceRegistry::instance()->colorConversionSystem()->createColorConverter(this, dstColorSpace, renderingIntent);
00251     }
00252 }
00253 
00254 bool KoColorSpace::convertPixelsTo(const quint8 * src,
00255                                    quint8 * dst,
00256                                    const KoColorSpace * dstColorSpace,
00257                                    quint32 numPixels,
00258                                    KoColorConversionTransformation::Intent renderingIntent) const
00259 {
00260     if (*this == *dstColorSpace) {
00261         memcpy(dst, src, numPixels * sizeof(quint8) * pixelSize());
00262     } else {
00263         KoCachedColorConversionTransformation cct = KoColorSpaceRegistry::instance()->colorConversionCache()->cachedConverter(this, dstColorSpace, renderingIntent);
00264         cct.transformation()->transform(src, dst, numPixels);
00265     }
00266     return true;
00267 }
00268 
00269 
00270 void KoColorSpace::bitBlt(quint8 *dst,
00271                           qint32 dststride,
00272                           const KoColorSpace * srcSpace,
00273                           const quint8 *src,
00274                           qint32 srcRowStride,
00275                           const quint8 *srcAlphaMask,
00276                           qint32 maskRowStride,
00277                           quint8 opacity,
00278                           qint32 rows,
00279                           qint32 cols,
00280                           const QString & op,
00281                           const QBitArray & channelFlags) const
00282 {
00283     if (d->compositeOps.contains(op)) {
00284         bitBlt(dst, dststride, srcSpace, src, srcRowStride, srcAlphaMask, maskRowStride, opacity, rows, cols, d->compositeOps.value(op), channelFlags);
00285     } else {
00286         bitBlt(dst, dststride, srcSpace, src, srcRowStride, srcAlphaMask, maskRowStride, opacity, rows, cols, d->compositeOps.value(COMPOSITE_OVER), channelFlags);
00287     }
00288 
00289 }
00290 
00291 void KoColorSpace::bitBlt(quint8 *dst,
00292                           qint32 dststride,
00293                           const KoColorSpace * srcSpace,
00294                           const quint8 *src,
00295                           qint32 srcRowStride,
00296                           const quint8 *srcAlphaMask,
00297                           qint32 maskRowStride,
00298                           quint8 opacity,
00299                           qint32 rows,
00300                           qint32 cols,
00301                           const QString& op) const
00302 {
00303     if (d->compositeOps.contains(op)) {
00304         bitBlt(dst, dststride, srcSpace, src, srcRowStride, srcAlphaMask, maskRowStride, opacity, rows, cols, d->compositeOps.value(op));
00305     } else {
00306         bitBlt(dst, dststride, srcSpace, src, srcRowStride, srcAlphaMask, maskRowStride, opacity, rows, cols, d->compositeOps.value(COMPOSITE_OVER));
00307     }
00308 }
00309 
00310 void KoColorSpace::bitBlt(quint8 *dst,
00311                           qint32 dstRowStride,
00312                           const KoColorSpace * srcSpace,
00313                           const quint8 *src,
00314                           qint32 srcRowStride,
00315                           const quint8 *srcAlphaMask,
00316                           qint32 maskRowStride,
00317                           quint8 opacity,
00318                           qint32 rows,
00319                           qint32 cols,
00320                           const KoCompositeOp * op,
00321                           const QBitArray & channelFlags) const
00322 {
00323     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());
00324 
00325     if (rows <= 0 || cols <= 0)
00326         return;
00327 
00328     if (!(*this == *srcSpace)) {
00329 
00330         quint32 conversionBufferStride = cols * pixelSize();
00331         QVector<quint8> * conversionCache =
00332             threadLocalConversionCache(rows * conversionBufferStride);
00333 
00334         quint8* conversionData = conversionCache->data();
00335 
00336         for (qint32 row = 0; row < rows; row++) {
00337             srcSpace->convertPixelsTo(src + row * srcRowStride,
00338                                       conversionData + row * conversionBufferStride,
00339                                       this, cols);
00340         }
00341 
00342         op->composite(dst, dstRowStride,
00343                       conversionData, conversionBufferStride,
00344                       srcAlphaMask, maskRowStride,
00345                       rows,  cols,
00346                       opacity, channelFlags);
00347     } else {
00348         op->composite(dst, dstRowStride,
00349                       src, srcRowStride,
00350                       srcAlphaMask, maskRowStride,
00351                       rows,  cols,
00352                       opacity, channelFlags);
00353     }
00354 
00355 }
00356 
00357 // XXX: I don't want this code duplication, but also don't want an
00358 //      extra function call in this critical section of code. What to
00359 //      do?
00360 void KoColorSpace::bitBlt(quint8 *dst,
00361                           qint32 dstRowStride,
00362                           const KoColorSpace * srcSpace,
00363                           const quint8 *src,
00364                           qint32 srcRowStride,
00365                           const quint8 *srcAlphaMask,
00366                           qint32 maskRowStride,
00367                           quint8 opacity,
00368                           qint32 rows,
00369                           qint32 cols,
00370                           const KoCompositeOp * op) const
00371 {
00372     Q_ASSERT(*op->colorSpace() == *this);
00373 
00374     if (rows <= 0 || cols <= 0)
00375         return;
00376 
00377     if (this != srcSpace) {
00378         quint32 conversionBufferStride = cols * pixelSize();
00379         QVector<quint8> * conversionCache =
00380             threadLocalConversionCache(rows * conversionBufferStride);
00381 
00382         quint8* conversionData = conversionCache->data();
00383 
00384         for (qint32 row = 0; row < rows; row++) {
00385             srcSpace->convertPixelsTo(src + row * srcRowStride,
00386                                       conversionData + row * conversionBufferStride, this,
00387                                       cols);
00388         }
00389 
00390         op->composite(dst, dstRowStride,
00391                       conversionData, conversionBufferStride,
00392                       srcAlphaMask, maskRowStride,
00393                       rows,  cols,
00394                       opacity);
00395 
00396     } else {
00397         op->composite(dst, dstRowStride,
00398                       src, srcRowStride,
00399                       srcAlphaMask, maskRowStride,
00400                       rows, cols,
00401                       opacity);
00402     }
00403 
00404 }
00405 
00406 QVector<quint8> * KoColorSpace::threadLocalConversionCache(quint32 size) const
00407 {
00408     QVector<quint8> * ba = 0;
00409     if (!d->conversionCache.hasLocalData()) {
00410         ba = new QVector<quint8>(size, '0');
00411         d->conversionCache.setLocalData(ba);
00412     } else {
00413         ba = d->conversionCache.localData();
00414         if ((quint8)ba->size() < size)
00415             ba->resize(size);
00416     }
00417     return ba;
00418 }
00419 
00420 KoColorTransformation* KoColorSpace::createColorTransformation(const QString & id, const QHash<QString, QVariant> & parameters) const
00421 {
00422     KoColorTransformationFactory* factory = KoColorTransformationFactoryRegistry::instance()->get(id);
00423     if (!factory) return 0;
00424     QPair<KoID, KoID> model(colorModelId(), colorDepthId());
00425     QList< QPair<KoID, KoID> > models = factory->supportedModels();
00426     if (models.isEmpty() || models.contains(model)) {
00427         return factory->createTransformation(this, parameters);
00428     } else {
00429         // Find the best solution
00430         // TODO use the color conversion cache
00431         KoColorConversionTransformation* csToFallBack = 0;
00432         KoColorConversionTransformation* fallBackToCs = 0;
00433         KoColorSpaceRegistry::instance()->colorConversionSystem()->createColorConverters(this, models, csToFallBack, fallBackToCs);
00434         Q_ASSERT(csToFallBack);
00435         Q_ASSERT(fallBackToCs);
00436         KoColorTransformation* transfo = factory->createTransformation(fallBackToCs->srcColorSpace(), parameters);
00437         return new KoFallBackColorTransformation(csToFallBack, fallBackToCs, transfo);
00438     }
00439 }
00440 
00441 QImage KoColorSpace::convertToQImage(const quint8 *data, qint32 width, qint32 height,
00442                                      const KoColorProfile *dstProfile,
00443                                      KoColorConversionTransformation::Intent renderingIntent) const
00444 
00445 {
00446     QImage img = QImage(width, height, QImage::Format_ARGB32);
00447 
00448     const KoColorSpace * dstCS = KoColorSpaceRegistry::instance()->rgb8(dstProfile);
00449 
00450     if (data)
00451         this->convertPixelsTo(const_cast<quint8 *>(data), img.bits(), dstCS, width * height, renderingIntent);
00452 
00453     return img;
00454 }