00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
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
00358
00359
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
00430
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 }