diff --git a/libs/pigment/KoColor.h b/libs/pigment/KoColor.h --- a/libs/pigment/KoColor.h +++ b/libs/pigment/KoColor.h @@ -24,6 +24,8 @@ #include #include "kritapigment_export.h" #include "KoColorConversionTransformation.h" +#include "KoColorSpaceRegistry.h" +#include "KoColorSpaceTraits.h" #include @@ -41,10 +43,12 @@ { public: - /// Create an empty KoColor. It will be valid, but also black and transparent - KoColor(); + static void init(); - ~KoColor(); + /// Create an empty KoColor. It will be valid, but also black and transparent + KoColor() : d(s_prefab) { + Q_ASSERT(d.colorSpace != nullptr); // valid and initialized? + } /// Create a null KoColor. It will be valid, but all channels will be set to 0 explicit KoColor(const KoColorSpace * colorSpace); @@ -59,19 +63,36 @@ KoColor(const KoColor &src, const KoColorSpace * colorSpace); /// Copy constructor -- deep copies the colors. - KoColor(const KoColor & rhs); + KoColor(const KoColor & rhs) : d(rhs.d) { + assertPermanentColorspace(); + } /** * assignment operator to copy the data from the param color into this one. * @param other the color we are going to copy * @return this color */ - KoColor &operator=(const KoColor &other); + KoColor &operator=(const KoColor &rhs) { + if (&rhs == this) { + return *this; + } + + d = rhs.d; + assertPermanentColorspace(); - bool operator==(const KoColor &other) const; + return *this; + } + + bool operator==(const KoColor &other) const { + if (*colorSpace() != *other.colorSpace()) + return false; + return memcmp(d.data, other.d.data, d.size) == 0; + } /// return the current colorSpace - const KoColorSpace * colorSpace() const; + const KoColorSpace * colorSpace() const { + return d.colorSpace; + } /// return the current profile const KoColorProfile *profile() const; @@ -112,21 +133,25 @@ qreal opacityF() const; /// Convenient function for converting from a QColor - void fromQColor(const QColor& c) const; + void fromQColor(const QColor& c); /** * @return the buffer associated with this color object to be used with the * transformation object created by the color space of this KoColor * or to copy to a different buffer from the same color space */ - quint8 * data(); + quint8 * data() { + return d.data; + } /** * @return the buffer associated with this color object to be used with the * transformation object created by the color space of this KoColor * or to copy to a different buffer from the same color space */ - const quint8 * data() const; + const quint8 * data() const { + return d.data; + } /** * Serialize this color following Create's swatch color specification available @@ -184,8 +209,39 @@ #endif private: - class Private; - Private * const d; + void assertPermanentColorspace() { +#ifndef NODEBUG + if (d.colorSpace) { + // here we want to do a check on pointer, since d-colorSpace is supposed to already be a permanent one + Q_ASSERT(d.colorSpace == KoColorSpaceRegistry::instance()->permanentColorspace(d.colorSpace)); + } +#endif + } + + class Q_DECL_HIDDEN Private + { + public: + Private() : colorSpace(nullptr) {} + + Private(const Private &rhs) { + *this = rhs; + } + + Private &operator=(const Private &rhs) { + colorSpace = rhs.colorSpace; + size = rhs.size; + memcpy(data, rhs.data, size); + return *this; + } + + const KoColorSpace * colorSpace; + quint8 data[MAX_PIXEL_SIZE]; + quint8 size; + }; + + Private d; + + static Private s_prefab; }; Q_DECLARE_METATYPE(KoColor) diff --git a/libs/pigment/KoColor.cpp b/libs/pigment/KoColor.cpp --- a/libs/pigment/KoColor.cpp +++ b/libs/pigment/KoColor.cpp @@ -33,111 +33,64 @@ #include "KoColorSpaceRegistry.h" #include "KoChannelInfo.h" +KoColor::Private KoColor::s_prefab; -class Q_DECL_HIDDEN KoColor::Private +void KoColor::init() { -public: - Private() : data(0), colorSpace(0) {} - - ~Private() { - delete [] data; - } - - quint8 * data; - const KoColorSpace * colorSpace; -}; - -KoColor::KoColor() - : d(new Private()) -{ - d->colorSpace = KoColorSpaceRegistry::instance()->rgb16(0); - d->data = new quint8[d->colorSpace->pixelSize()]; - memset(d->data, 0, d->colorSpace->pixelSize()); - d->colorSpace->setOpacity(d->data, OPACITY_OPAQUE_U8, 1); + printf("getting KoColorSpaceRegistry::instance()\n"); + KoColorSpaceRegistry::instance(); + printf("getting KoColorSpaceRegistry::instance()->rgb16(0)\n"); + KoColorSpaceRegistry::instance()->rgb16(0); + printf("rest\n"); + s_prefab.colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(KoColorSpaceRegistry::instance()->rgb16(0)); + s_prefab.size = s_prefab.colorSpace->pixelSize(); + Q_ASSERT(s_prefab.size <= MAX_PIXEL_SIZE); + s_prefab.colorSpace->fromQColor(Qt::black, s_prefab.data); + s_prefab.colorSpace->setOpacity(s_prefab.data, OPACITY_OPAQUE_U8, 1); } KoColor::KoColor(const KoColorSpace * colorSpace) - : d(new Private()) { Q_ASSERT(colorSpace); - d->colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(colorSpace); - d->data = new quint8[d->colorSpace->pixelSize()]; - memset(d->data, 0, d->colorSpace->pixelSize()); -} - -KoColor::~KoColor() -{ - delete d; + d.colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(colorSpace); + d.size = d.colorSpace->pixelSize(); + Q_ASSERT(d.size <= MAX_PIXEL_SIZE); + memset(d.data, 0, d.size); } KoColor::KoColor(const QColor & color, const KoColorSpace * colorSpace) - : d(new Private()) { Q_ASSERT(color.isValid()); Q_ASSERT(colorSpace); - d->colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(colorSpace); + d.colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(colorSpace); - d->data = new quint8[colorSpace->pixelSize()]; - memset(d->data, 0, d->colorSpace->pixelSize()); + d.size = d.colorSpace->pixelSize(); + Q_ASSERT(d.size <= MAX_PIXEL_SIZE); + memset(d.data, 0, d.size); - d->colorSpace->fromQColor(color, d->data); + d.colorSpace->fromQColor(color, d.data); } KoColor::KoColor(const quint8 * data, const KoColorSpace * colorSpace) - : d(new Private()) { Q_ASSERT(colorSpace); Q_ASSERT(data); - d->colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(colorSpace); - d->data = new quint8[colorSpace->pixelSize()]; - memset(d->data, 0, d->colorSpace->pixelSize()); - memmove(d->data, data, colorSpace->pixelSize()); + d.colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(colorSpace); + d.size = d.colorSpace->pixelSize(); + Q_ASSERT(d.size <= MAX_PIXEL_SIZE); + memmove(d.data, data, d.size); } KoColor::KoColor(const KoColor &src, const KoColorSpace * colorSpace) - : d(new Private()) { Q_ASSERT(colorSpace); - d->colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(colorSpace); - d->data = new quint8[colorSpace->pixelSize()]; - memset(d->data, 0, d->colorSpace->pixelSize()); - - src.colorSpace()->convertPixelsTo(src.d->data, d->data, colorSpace, 1, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); -} - -KoColor::KoColor(const KoColor & rhs) - : d(new Private()) -{ - d->colorSpace = rhs.colorSpace(); - Q_ASSERT(*d->colorSpace == *KoColorSpaceRegistry::instance()->permanentColorspace(d->colorSpace)); - if (d->colorSpace && rhs.d->data) { - d->data = new quint8[d->colorSpace->pixelSize()]; - memcpy(d->data, rhs.d->data, d->colorSpace->pixelSize()); - } -} - -KoColor & KoColor::operator=(const KoColor & rhs) -{ - if (this == &rhs) return *this; - - delete [] d->data; - d->data = 0; - d->colorSpace = rhs.colorSpace(); + d.colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(colorSpace); + d.size = d.colorSpace->pixelSize(); + Q_ASSERT(d.size <= MAX_PIXEL_SIZE); + memset(d.data, 0, d.size); - if (rhs.d->colorSpace && rhs.d->data) { - 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 - d->data = new quint8[d->colorSpace->pixelSize()]; - memcpy(d->data, rhs.d->data, d->colorSpace->pixelSize()); - } - return * this; -} - -bool KoColor::operator==(const KoColor &other) const -{ - if (*colorSpace() != *other.colorSpace()) - return false; - return memcmp(d->data, other.d->data, d->colorSpace->pixelSize()) == 0; + src.colorSpace()->convertPixelsTo(src.d.data, d.data, colorSpace, 1, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); } void KoColor::convertTo(const KoColorSpace * cs, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) @@ -145,17 +98,19 @@ //dbgPigment <<"Our colormodel:" << d->colorSpace->id().name() // << ", new colormodel: " << cs->id().name() << "\n"; - if (*d->colorSpace == *cs) + if (*d.colorSpace == *cs) return; - quint8 * data = new quint8[cs->pixelSize()]; - memset(data, 0, cs->pixelSize()); + quint8 data[MAX_PIXEL_SIZE]; + const size_t size = cs->pixelSize(); + Q_ASSERT(size <= MAX_PIXEL_SIZE); + memset(data, 0, size); - d->colorSpace->convertPixelsTo(d->data, data, cs, 1, renderingIntent, conversionFlags); + d.colorSpace->convertPixelsTo(d.data, data, cs, 1, renderingIntent, conversionFlags); - delete [] d->data; - d->data = data; - d->colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(cs); + memcpy(d.data, data, size); + d.size = size; + d.colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(cs); } void KoColor::convertTo(const KoColorSpace * cs) @@ -168,31 +123,29 @@ void KoColor::setProfile(const KoColorProfile *profile) { const KoColorSpace *dstColorSpace = - KoColorSpaceRegistry::instance()->colorSpace(colorSpace()->colorModelId().id(), colorSpace()->colorDepthId().id(), profile); + KoColorSpaceRegistry::instance()->colorSpace(colorSpace()->colorModelId().id(), colorSpace()->colorDepthId().id(), profile); if (!dstColorSpace) return; - d->colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(dstColorSpace); + d.colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(dstColorSpace); } void KoColor::setColor(const quint8 * data, const KoColorSpace * colorSpace) { - Q_ASSERT(data); Q_ASSERT(colorSpace); - if(d->colorSpace->pixelSize() != colorSpace->pixelSize()) - { - delete [] d->data; - d->data = new quint8[colorSpace->pixelSize()]; - } - memcpy(d->data, data, colorSpace->pixelSize()); - d->colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(colorSpace); + + const size_t size = colorSpace->pixelSize(); + Q_ASSERT(size <= MAX_PIXEL_SIZE); + + memcpy(d.data, data, size); + d.colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(colorSpace); } // To save the user the trouble of doing color->colorSpace()->toQColor(color->data(), &c, &a, profile void KoColor::toQColor(QColor *c) const { Q_ASSERT(c); - if (d->colorSpace && d->data) { - d->colorSpace->toQColor(d->data, c); + if (d.colorSpace) { + d.colorSpace->toQColor(d.data, c); } } @@ -203,18 +156,18 @@ return c; } -void KoColor::fromQColor(const QColor& c) const +void KoColor::fromQColor(const QColor& c) { - if (d->colorSpace && d->data) { - d->colorSpace->fromQColor(c, d->data); + if (d.colorSpace) { + d.colorSpace->fromQColor(c, d.data); } } #ifndef NDEBUG void KoColor::dump() const { - dbgPigment <<"KoColor (" << this <<")," << d->colorSpace->id() <<""; - QList channels = d->colorSpace->channels(); + dbgPigment <<"KoColor (" << this <<")," << d.colorSpace->id() <<""; + QList channels = d.colorSpace->channels(); QList::const_iterator begin = channels.constBegin(); QList::const_iterator end = channels.constEnd(); @@ -224,13 +177,13 @@ // XXX: setNum always takes a byte. if (ch->size() == sizeof(quint8)) { // Byte - dbgPigment <<"Channel (byte):" << ch->name() <<":" << QString().setNum(d->data[ch->pos()]) <<""; + dbgPigment <<"Channel (byte):" << ch->name() <<":" << QString().setNum(d.data[ch->pos()]) <<""; } else if (ch->size() == sizeof(quint16)) { // Short (may also by an nvidia half) - dbgPigment <<"Channel (short):" << ch->name() <<":" << QString().setNum(*((const quint16 *)(d->data+ch->pos()))) <<""; + dbgPigment <<"Channel (short):" << ch->name() <<":" << QString().setNum(*((const quint16 *)(d.data+ch->pos()))) <<""; } else if (ch->size() == sizeof(quint32)) { // Integer (may also be float... Find out how to distinguish these!) - dbgPigment <<"Channel (int):" << ch->name() <<":" << QString().setNum(*((const quint32 *)(d->data+ch->pos()))) <<""; + dbgPigment <<"Channel (int):" << ch->name() <<":" << QString().setNum(*((const quint32 *)(d.data+ch->pos()))) <<""; } } } @@ -238,49 +191,34 @@ void KoColor::fromKoColor(const KoColor& src) { - src.colorSpace()->convertPixelsTo(src.d->data, d->data, colorSpace(), 1, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); + src.colorSpace()->convertPixelsTo(src.d.data, d.data, colorSpace(), 1, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); } const KoColorProfile *KoColor::profile() const { - return d->colorSpace->profile(); -} - -quint8 * KoColor::data() -{ - return d->data; -} - -const quint8 * KoColor::data() const -{ - return d->data; -} - -const KoColorSpace * KoColor::colorSpace() const -{ - return d->colorSpace; + return d.colorSpace->profile(); } void KoColor::toXML(QDomDocument& doc, QDomElement& colorElt) const { - d->colorSpace->colorToXML(d->data, doc, colorElt); + d.colorSpace->colorToXML(d.data, doc, colorElt); } void KoColor::setOpacity(quint8 alpha) { - d->colorSpace->setOpacity(d->data, alpha, 1); + d.colorSpace->setOpacity(d.data, alpha, 1); } void KoColor::setOpacity(qreal alpha) { - d->colorSpace->setOpacity(d->data, alpha, 1); + d.colorSpace->setOpacity(d.data, alpha, 1); } quint8 KoColor::opacityU8() const { - return d->colorSpace->opacityU8(d->data); + return d.colorSpace->opacityU8(d.data); } qreal KoColor::opacityF() const { - return d->colorSpace->opacityF(d->data); + return d.colorSpace->opacityF(d.data); } KoColor KoColor::fromXML(const QDomElement& elt, const QString& bitDepthId) diff --git a/libs/pigment/KoColorSpaceTraits.h b/libs/pigment/KoColorSpaceTraits.h --- a/libs/pigment/KoColorSpaceTraits.h +++ b/libs/pigment/KoColorSpaceTraits.h @@ -26,6 +26,10 @@ #include "KoColorSpaceMaths.h" #include "DebugPigment.h" +const int MAX_CHANNELS_TYPE_SIZE = sizeof(double); +const int MAX_CHANNELS_NB = 5; +const int MAX_PIXEL_SIZE = MAX_CHANNELS_NB * MAX_CHANNELS_TYPE_SIZE; + /** * This class is the base class to define the main characteristics of a colorspace * which inherits KoColorSpaceAbstract. @@ -36,7 +40,7 @@ * - _channels_nb_ is the total number of channels in an image (for example RGB is 3 but RGBA is four) * - _alpha_pos_ is the position of the alpha channel among the channels, if there is no alpha channel, * then _alpha_pos_ is set to -1 - * + * * For instance a colorspace of three color channels and alpha channel in 16bits, * will be defined as KoColorSpaceTrait\. The same without the alpha * channel is KoColorSpaceTrait\ @@ -44,25 +48,28 @@ */ template struct KoColorSpaceTrait { - + + static_assert(sizeof(_channels_type_) <= MAX_CHANNELS_TYPE_SIZE, "MAX_CHANNELS_TYPE_SIZE too small"); + static_assert(_channels_nb_ <= MAX_CHANNELS_NB, "MAX_CHANNELS_NB too small"); + /// the type of the value of the channels of this color space typedef _channels_type_ channels_type; - + /// the number of channels in this color space static const quint32 channels_nb = _channels_nb_; - + /// the position of the alpha channel in the channels of the pixel (or -1 if no alpha /// channel. static const qint32 alpha_pos = _alpha_pos_; - + /// the number of bit for each channel static const int depth = KoColorSpaceMathsTraits<_channels_type_>::bits; - + /** * @return the size in byte of one pixel */ static const quint32 pixelSize = channels_nb * sizeof(channels_type); - + /** * @return the value of the alpha channel for this pixel in the 0..255 range */ @@ -71,13 +78,13 @@ channels_type c = nativeArray(U8_pixel)[alpha_pos]; return KoColorSpaceMaths::scaleToA(c); } - + inline static qreal opacityF(const quint8 * U8_pixel) { if (alpha_pos < 0) return OPACITY_OPAQUE_F; channels_type c = nativeArray(U8_pixel)[alpha_pos]; return KoColorSpaceMaths::scaleToA(c); } - + /** * Set the alpha channel for this pixel from a value in the 0..255 range */ @@ -89,7 +96,7 @@ nativeArray(pixels)[alpha_pos] = valpha; } } - + inline static void setOpacity(quint8 * pixels, qreal alpha, qint32 nPixels) { if (alpha_pos < 0) return; qint32 psize = pixelSize; @@ -98,28 +105,28 @@ nativeArray(pixels)[alpha_pos] = valpha; } } - + /** * Convenient function for transforming a quint8* array in a pointer of the native channels type */ inline static const channels_type* nativeArray(const quint8 * a) { return reinterpret_cast(a); } - + /** * Convenient function for transforming a quint8* array in a pointer of the native channels type */ inline static channels_type* nativeArray(quint8 * a) { return reinterpret_cast< channels_type*>(a); } - + /** * Allocate nPixels pixels for this colorspace. */ inline static quint8* allocate(quint32 nPixels) { return new quint8[ nPixels * pixelSize ]; } - + inline static void singleChannelPixel(quint8 *dstPixel, const quint8 *srcPixel, quint32 channelIndex) { const channels_type* src = nativeArray(srcPixel); channels_type* dst = nativeArray(dstPixel); @@ -131,7 +138,7 @@ } } } - + inline static QString channelValueText(const quint8 *pixel, quint32 channelIndex) { if (channelIndex > channels_nb) return QString("Error"); channels_type c = nativeArray(pixel)[channelIndex]; diff --git a/libs/ui/KisApplication.cpp b/libs/ui/KisApplication.cpp --- a/libs/ui/KisApplication.cpp +++ b/libs/ui/KisApplication.cpp @@ -171,6 +171,10 @@ setApplicationVersion(version); setWindowIcon(KisIconUtils::loadIcon("calligrakrita")); + // no KoColor creation allowed before this point; note that KoColor::init() needs QApplication (as it will call + // KoColorSpaceRegistry::init() which needs a QApplication instance). + KoColor::init(); + if (qgetenv("KRITA_NO_STYLE_OVERRIDE").isEmpty()) { QStringList styles = QStringList() << "breeze" << "fusion" << "plastique"; if (!styles.contains(style()->objectName().toLower())) {