diff --git a/libs/pigment/KoColor.cpp b/libs/pigment/KoColor.cpp index 4e310ebe50..c7bb76b817 100644 --- a/libs/pigment/KoColor.cpp +++ b/libs/pigment/KoColor.cpp @@ -1,345 +1,284 @@ /* * Copyright (c) 2005 Boudewijn Rempt * Copyright (C) 2007 Thomas Zander * Copyright (C) 2007 Cyrille Berger * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * 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. */ #include "KoColor.h" #include #include #include "DebugPigment.h" #include "KoColorModelStandardIds.h" #include "KoColorProfile.h" #include "KoColorSpace.h" #include "KoColorSpaceRegistry.h" #include "KoChannelInfo.h" +const KoColor *KoColor::s_prefab = nullptr; -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); + KIS_ASSERT(s_prefab == nullptr); + KoColor *prefab = new KoColor(KoColorSpaceRegistry::instance()->rgb16(0)); + prefab->m_colorSpace->fromQColor(Qt::black, prefab->m_data); + prefab->m_colorSpace->setOpacity(prefab->m_data, OPACITY_OPAQUE_U8, 1); + s_prefab = prefab; +#ifndef NODEBUG +#ifndef QT_NO_DEBUG + // warn about rather expensive checks in assertPermanentColorspace(). + qWarning() << "KoColor debug runtime checks are active."; +#endif +#endif } 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; + m_colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(colorSpace); + m_size = m_colorSpace->pixelSize(); + Q_ASSERT(m_size <= MAX_PIXEL_SIZE); + memset(m_data, 0, m_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); + m_colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(colorSpace); - d->data = new quint8[colorSpace->pixelSize()]; - memset(d->data, 0, d->colorSpace->pixelSize()); + m_size = m_colorSpace->pixelSize(); + Q_ASSERT(m_size <= MAX_PIXEL_SIZE); + memset(m_data, 0, m_size); - d->colorSpace->fromQColor(color, d->data); + m_colorSpace->fromQColor(color, m_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()); + m_colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(colorSpace); + m_size = m_colorSpace->pixelSize(); + Q_ASSERT(m_size <= MAX_PIXEL_SIZE); + memmove(m_data, data, m_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; + m_colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(colorSpace); + m_size = m_colorSpace->pixelSize(); + Q_ASSERT(m_size <= MAX_PIXEL_SIZE); + memset(m_data, 0, m_size); - delete [] d->data; - d->data = 0; - d->colorSpace = rhs.colorSpace(); - - 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.m_data, m_data, colorSpace, 1, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); } void KoColor::convertTo(const KoColorSpace * cs, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) { //dbgPigment <<"Our colormodel:" << d->colorSpace->id().name() // << ", new colormodel: " << cs->id().name() << "\n"; - if (*d->colorSpace == *cs) + if (*m_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); + m_colorSpace->convertPixelsTo(m_data, data, cs, 1, renderingIntent, conversionFlags); - delete [] d->data; - d->data = data; - d->colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(cs); + memcpy(m_data, data, size); + m_size = size; + m_colorSpace = KoColorSpaceRegistry::instance()->permanentColorspace(cs); } void KoColor::convertTo(const KoColorSpace * cs) { convertTo(cs, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); } 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); + m_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(m_data, data, size); + m_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 (m_colorSpace) { + m_colorSpace->toQColor(m_data, c); } } QColor KoColor::toQColor() const { QColor c; toQColor(&c); 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 (m_colorSpace) { + m_colorSpace->fromQColor(c, m_data); } } #ifndef NDEBUG void KoColor::dump() const { - dbgPigment <<"KoColor (" << this <<")," << d->colorSpace->id() <<""; - QList channels = d->colorSpace->channels(); + dbgPigment <<"KoColor (" << this <<")," << m_colorSpace->id() <<""; + QList channels = m_colorSpace->channels(); QList::const_iterator begin = channels.constBegin(); QList::const_iterator end = channels.constEnd(); for (QList::const_iterator it = begin; it != end; ++it) { KoChannelInfo * ch = (*it); // 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(m_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 *)(m_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 *)(m_data+ch->pos()))) <<""; } } } #endif void KoColor::fromKoColor(const KoColor& src) { - src.colorSpace()->convertPixelsTo(src.d->data, d->data, colorSpace(), 1, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); + src.colorSpace()->convertPixelsTo(src.m_data, m_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 m_colorSpace->profile(); } void KoColor::toXML(QDomDocument& doc, QDomElement& colorElt) const { - d->colorSpace->colorToXML(d->data, doc, colorElt); + m_colorSpace->colorToXML(m_data, doc, colorElt); } void KoColor::setOpacity(quint8 alpha) { - d->colorSpace->setOpacity(d->data, alpha, 1); + m_colorSpace->setOpacity(m_data, alpha, 1); } void KoColor::setOpacity(qreal alpha) { - d->colorSpace->setOpacity(d->data, alpha, 1); + m_colorSpace->setOpacity(m_data, alpha, 1); } quint8 KoColor::opacityU8() const { - return d->colorSpace->opacityU8(d->data); + return m_colorSpace->opacityU8(m_data); } qreal KoColor::opacityF() const { - return d->colorSpace->opacityF(d->data); + return m_colorSpace->opacityF(m_data); } KoColor KoColor::fromXML(const QDomElement& elt, const QString& bitDepthId) { bool ok; return fromXML(elt, bitDepthId, &ok); } KoColor KoColor::fromXML(const QDomElement& elt, const QString& bitDepthId, bool* ok) { *ok = true; QString modelId; if (elt.tagName() == "CMYK") { modelId = CMYKAColorModelID.id(); } else if (elt.tagName() == "RGB") { modelId = RGBAColorModelID.id(); } else if (elt.tagName() == "sRGB") { modelId = RGBAColorModelID.id(); } else if (elt.tagName() == "Lab") { modelId = LABAColorModelID.id(); } else if (elt.tagName() == "XYZ") { modelId = XYZAColorModelID.id(); } else if (elt.tagName() == "Gray") { modelId = GrayAColorModelID.id(); } else if (elt.tagName() == "YCbCr") { modelId = YCbCrAColorModelID.id(); } QString profileName; if (elt.tagName() != "sRGB") { profileName = elt.attribute("space", ""); if (!KoColorSpaceRegistry::instance()->profileByName(profileName)) { profileName.clear(); } } const KoColorSpace* cs = KoColorSpaceRegistry::instance()->colorSpace(modelId, bitDepthId, profileName); if (cs == 0) { QList list = KoColorSpaceRegistry::instance()->colorDepthList(modelId, KoColorSpaceRegistry::AllColorSpaces); if (!list.empty()) { cs = KoColorSpaceRegistry::instance()->colorSpace(modelId, list[0].id(), profileName); } } if (cs) { KoColor c(cs); // TODO: Provide a way for colorFromXML() to notify the caller if parsing failed. Currently it returns default values on failure. cs->colorFromXML(c.data(), elt); return c; } else { *ok = false; return KoColor(); } } QString KoColor::toQString(const KoColor &color) { QStringList ls; Q_FOREACH (KoChannelInfo *channel, KoChannelInfo::displayOrderSorted(color.colorSpace()->channels())) { int realIndex = KoChannelInfo::displayPositionToChannelIndex(channel->displayPosition(), color.colorSpace()->channels()); ls << channel->name(); ls << color.colorSpace()->channelValueText(color.data(), realIndex); } return ls.join(" "); } diff --git a/libs/pigment/KoColor.h b/libs/pigment/KoColor.h index 24c4d28ef6..5eb3859a95 100644 --- a/libs/pigment/KoColor.h +++ b/libs/pigment/KoColor.h @@ -1,193 +1,239 @@ /* * Copyright (c) 2005 Boudewijn Rempt * Copyright (C) 2007 Thomas Zander * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * 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. */ #ifndef KOCOLOR_H #define KOCOLOR_H #include #include #include "kritapigment_export.h" #include "KoColorConversionTransformation.h" +#include "KoColorSpaceRegistry.h" +#include "KoColorSpaceTraits.h" #include +#include "kis_assert.h" class QDomDocument; class QDomElement; class KoColorProfile; class KoColorSpace; /** * A KoColor describes a color in a certain colorspace. The color is stored in a buffer * that can be manipulated by the function of the color space. */ class KRITAPIGMENT_EXPORT KoColor : public boost::equality_comparable { public: + static void init(); + /// Create an empty KoColor. It will be valid, but also black and transparent - KoColor(); + KoColor() { + const KoColor * const prefab = s_prefab; + + // assert that KoColor::init was called and everything is set up properly. + KIS_ASSERT_X(prefab != nullptr, "KoColor::KoColor()", "KoColor not initialized yet."); - ~KoColor(); + *this = *prefab; + } /// Create a null KoColor. It will be valid, but all channels will be set to 0 explicit KoColor(const KoColorSpace * colorSpace); /// Create a KoColor from a QColor. The QColor is immediately converted to native. The QColor /// is assumed to have the current monitor profile. KoColor(const QColor & color, const KoColorSpace * colorSpace); /// Create a KoColor using a native color strategy. The data is copied. KoColor(const quint8 * data, const KoColorSpace * colorSpace); /// Create a KoColor by converting src into another colorspace KoColor(const KoColor &src, const KoColorSpace * colorSpace); /// Copy constructor -- deep copies the colors. - KoColor(const KoColor & rhs); + KoColor(const KoColor & rhs) { + *this = rhs; + } /** * 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); + inline KoColor &operator=(const KoColor &rhs) { + if (&rhs == this) { + return *this; + } + + m_colorSpace = rhs.m_colorSpace; + m_size = rhs.m_size; + memcpy(m_data, rhs.m_data, m_size); + + assertPermanentColorspace(); + + return *this; + } - bool operator==(const KoColor &other) const; + bool operator==(const KoColor &other) const { + if (*colorSpace() != *other.colorSpace()) + return false; + Q_ASSERT(m_size == other.m_size); + return memcmp(m_data, other.m_data, m_size) == 0; + } /// return the current colorSpace - const KoColorSpace * colorSpace() const; + const KoColorSpace * colorSpace() const { + return m_colorSpace; + } /// return the current profile const KoColorProfile *profile() const; /// Convert this KoColor to the specified colorspace. If the specified colorspace is the /// same as the original colorspace, do nothing. Returns the converted KoColor. void convertTo(const KoColorSpace * cs, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags); void convertTo(const KoColorSpace * cs); /// assign new profile without converting pixel data void setProfile(const KoColorProfile *profile); /// Replace the existing color data, and colorspace with the specified data. /// The data is copied. void setColor(const quint8 * data, const KoColorSpace * colorSpace = 0); /// Convert the color from src and replace the value of the current color with the converted data. /// Don't convert the color if src and this have the same colorspace. void fromKoColor(const KoColor& src); /// a convenience method for the above. void toQColor(QColor *c) const; /// a convenience method for the above. QColor toQColor() const; /** * Convenient function to set the opacity of the color. */ void setOpacity(quint8 alpha); void setOpacity(qreal alpha); /** * Convenient function that return the opacity of the color */ quint8 opacityU8() const; 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 m_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 m_data; + } /** * Serialize this color following Create's swatch color specification available * at http://create.freedesktop.org/wiki/index.php/Swatches_-_colour_file_format * * This function doesn't create the element but rather the , * , ... elements. It is assumed that colorElt is the * element. * * @param colorElt root element for the serialization, it is assumed that this * element is * @param doc is the document containing colorElt */ void toXML(QDomDocument& doc, QDomElement& colorElt) const; /** * Unserialize a color following Create's swatch color specification available * at http://create.freedesktop.org/wiki/index.php/Swatches_-_colour_file_format * * @param elt the element to unserialize (, , ) * @param bitDepthId the bit depth is unspecified by the spec, this allow to select * a preferred bit depth for creating the KoColor object (if that * bit depth isn't available, this function will randomly select * an other bit depth) * @return the unserialize color, or an empty color object if the function failed * to unserialize the color */ static KoColor fromXML(const QDomElement& elt, const QString & bitDepthId); /** * Unserialize a color following Create's swatch color specification available * at http://create.freedesktop.org/wiki/index.php/Swatches_-_colour_file_format * * @param elt the element to unserialize (, , ) * @param bitDepthId the bit depth is unspecified by the spec, this allow to select * a preferred bit depth for creating the KoColor object (if that * bit depth isn't available, this function will randomly select * an other bit depth) * @param ok If a an error occurs, *ok is set to false; otherwise it's set to true * @return the unserialize color, or an empty color object if the function failed * to unserialize the color */ static KoColor fromXML(const QDomElement& elt, const QString & bitDepthId, bool* ok); /** * @brief toQString create a user-visible string of the channel names and the channel values * @param color the color to create the string from * @return a string that can be used to display the values of this color to the user. */ static QString toQString(const KoColor &color); #ifndef NODEBUG /// use qDebug calls to print internal info void dump() const; #endif private: - class Private; - Private * const d; + inline void assertPermanentColorspace() { +#ifndef NODEBUG + if (m_colorSpace) { + Q_ASSERT(*m_colorSpace == *KoColorSpaceRegistry::instance()->permanentColorspace(m_colorSpace)); + } +#endif + } + + const KoColorSpace *m_colorSpace; + quint8 m_data[MAX_PIXEL_SIZE]; + quint8 m_size; + + static const KoColor *s_prefab; }; Q_DECLARE_METATYPE(KoColor) #endif diff --git a/libs/pigment/KoColorSpaceFactory.cpp b/libs/pigment/KoColorSpaceFactory.cpp index 27b1533039..adaa79a70a 100644 --- a/libs/pigment/KoColorSpaceFactory.cpp +++ b/libs/pigment/KoColorSpaceFactory.cpp @@ -1,107 +1,110 @@ /* * Copyright (c) 2010 Cyrille Berger * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * 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. */ #include "KoColorSpaceFactory.h" #include "DebugPigment.h" #include #include "KoColorProfile.h" #include "KoColorSpace.h" #include "KoColorSpaceRegistry.h" +#include "kis_assert.h" + struct Q_DECL_HIDDEN KoColorSpaceFactory::Private { QList colorprofiles; QList colorspaces; QHash availableColorspaces; QMutex mutex; #ifndef NDEBUG QHash stackInformation; #endif }; KoColorSpaceFactory::KoColorSpaceFactory() : d(new Private) { } KoColorSpaceFactory::~KoColorSpaceFactory() { #ifndef NDEBUG // Check that all color spaces have been released int count = 0; count += d->availableColorspaces.size(); for(QHash::const_iterator it = d->stackInformation.constBegin(); it != d->stackInformation.constEnd(); ++it) { errorPigment << "*******************************************"; errorPigment << it.key()->id() << " still in used, and grabbed in: "; errorPigment << it.value(); } if( count != d->colorspaces.size()) { errorPigment << (d->colorspaces.size() - count) << " colorspaces are still used"; } Q_ASSERT(count == d->colorspaces.size()); #endif Q_FOREACH (KoColorSpace* cs, d->colorspaces) { delete cs; } Q_FOREACH (KoColorProfile* profile, d->colorprofiles) { KoColorSpaceRegistry::instance()->removeProfile(profile); delete profile; } delete d; } const KoColorProfile *KoColorSpaceFactory::colorProfile(const QByteArray &rawData, KoColorSpaceFactory::ProfileRegistrationInterface *registrationInterface) const { KoColorProfile* colorProfile = createColorProfile(rawData); if (colorProfile && colorProfile->valid()) { if (const KoColorProfile* existingProfile = registrationInterface->profileByName(colorProfile->name())) { delete colorProfile; return existingProfile; } registrationInterface->registerNewProfile(colorProfile); d->colorprofiles.append(colorProfile); } return colorProfile; } const KoColorSpace *KoColorSpaceFactory::grabColorSpace(const KoColorProfile * profile) { QMutexLocker l(&d->mutex); Q_ASSERT(profile); auto it = d->availableColorspaces.find(profile->name()); KoColorSpace* cs; if (it == d->availableColorspaces.end()) { cs = createColorSpace(profile); + KIS_ASSERT_X(cs != nullptr, "KoColorSpaceFactory::grabColorSpace", "createColorSpace returned nullptr."); if (cs) { d->availableColorspaces[profile->name()] = cs; } } else { cs = it.value(); } return cs; } diff --git a/libs/pigment/KoColorSpaceRegistry.cpp b/libs/pigment/KoColorSpaceRegistry.cpp index 353e4b3de2..f95ed9683d 100644 --- a/libs/pigment/KoColorSpaceRegistry.cpp +++ b/libs/pigment/KoColorSpaceRegistry.cpp @@ -1,818 +1,820 @@ /* * Copyright (c) 2003 Patrick Julien * Copyright (c) 2004,2010 Cyrille Berger * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * 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. */ #include "KoColorSpaceRegistry.h" #include #include #include #include #include #include "KoPluginLoader.h" #include "KoGenericRegistry.h" #include "DebugPigment.h" #include "KoBasicHistogramProducers.h" #include "KoColorSpace.h" #include "KoColorProfile.h" #include "KoColorConversionCache.h" #include "KoColorConversionSystem.h" #include "colorspaces/KoAlphaColorSpace.h" #include "colorspaces/KoLabColorSpace.h" #include "colorspaces/KoRgbU16ColorSpace.h" #include "colorspaces/KoRgbU8ColorSpace.h" #include "colorspaces/KoSimpleColorSpaceEngine.h" #include "KoColorSpace_p.h" #include "kis_assert.h" #include "KoColorProfileStorage.h" #include Q_GLOBAL_STATIC(KoColorSpaceRegistry, s_instance) struct Q_DECL_HIDDEN KoColorSpaceRegistry::Private { // interface for KoColorSpaceFactory struct ProfileRegistrationInterface; // interface for KoColorConversionSystem struct ConversionSystemInterface; Private(KoColorSpaceRegistry *_q) : q(_q) {} KoColorSpaceRegistry *q; KoGenericRegistry colorSpaceFactoryRegistry; KoColorProfileStorage profileStorage; QHash csMap; QScopedPointer conversionSystemInterface; KoColorConversionSystem *colorConversionSystem; KoColorConversionCache* colorConversionCache; const KoColorSpace *rgbU8sRGB; const KoColorSpace *lab16sLAB; const KoColorSpace *alphaCs; const KoColorSpace *alphaU16Cs; #ifdef HAVE_OPENEXR const KoColorSpace *alphaF16Cs; #endif const KoColorSpace *alphaF32Cs; QReadWriteLock registrylock; /** * The function checks if a colorspace with a certain id and profile name can be found in the cache * NOTE: the function doesn't take any lock but it needs to be called inside a d->registryLock * locked either in read or write. * @param csId The colorspace id * @param profileName The colorspace profile name * @retval KoColorSpace The matching colorspace * @retval 0 Null pointer if not match */ const KoColorSpace* getCachedColorSpaceImpl(const QString & csId, const QString & profileName) const; QString idsToCacheName(const QString & csId, const QString & profileName) const; QString defaultProfileForCsIdImpl(const QString &csID); const KoColorProfile * profileForCsIdWithFallbackImpl(const QString &csID, const QString &profileName); QString colorSpaceIdImpl(const QString & colorModelId, const QString & colorDepthId) const; const KoColorSpace *lazyCreateColorSpaceImpl(const QString &csID, const KoColorProfile *profile); /** * Return a colorspace that works with the parameter profile. * @param profileName the name of the KoColorProfile to be combined with the colorspace * @return the wanted colorspace, or 0 when the cs and profile can not be combined. */ template const KoColorSpace * colorSpace1(const QString &colorSpaceId, const QString &pName = QString()); /** * Return a colorspace that works with the parameter profile. * @param colorSpaceId the ID string of the colorspace that you want to have returned * @param profile the profile be combined with the colorspace * @return the wanted colorspace, or 0 when the cs and profile can not be combined. */ const KoColorSpace * colorSpace1(const QString &colorSpaceId, const KoColorProfile *profile); }; struct KoColorSpaceRegistry::Private::ConversionSystemInterface : public KoColorConversionSystem::RegistryInterface { ConversionSystemInterface(KoColorSpaceRegistry *parentRegistry) : q(parentRegistry) { } const KoColorSpace * colorSpace(const QString & colorModelId, const QString & colorDepthId, const QString &profileName) { return q->d->colorSpace1(q->d->colorSpaceIdImpl(colorModelId, colorDepthId), profileName); } const KoColorSpaceFactory* colorSpaceFactory(const QString &colorModelId, const QString &colorDepthId) const { return q->d->colorSpaceFactoryRegistry.get(q->d->colorSpaceIdImpl(colorModelId, colorDepthId)); } QList profilesFor(const KoColorSpaceFactory * csf) const { return q->d->profileStorage.profilesFor(csf); } QList colorSpacesFor(const KoColorProfile* profile) const { QList csfs; Q_FOREACH (KoColorSpaceFactory* csf, q->d->colorSpaceFactoryRegistry.values()) { if (csf->profileIsCompatible(profile)) { csfs.push_back(csf); } } return csfs; } private: KoColorSpaceRegistry *q; }; KoColorSpaceRegistry* KoColorSpaceRegistry::instance() { if (!s_instance.exists()) { s_instance->init(); } return s_instance; } void KoColorSpaceRegistry::init() { d->rgbU8sRGB = 0; d->lab16sLAB = 0; d->alphaCs = 0; d->alphaU16Cs = 0; #ifdef HAVE_OPENEXR d->alphaF16Cs = 0; #endif d->alphaF32Cs = 0; d->conversionSystemInterface.reset(new Private::ConversionSystemInterface(this)); d->colorConversionSystem = new KoColorConversionSystem(d->conversionSystemInterface.data()); d->colorConversionCache = new KoColorConversionCache; KoColorSpaceEngineRegistry::instance()->add(new KoSimpleColorSpaceEngine()); addProfile(new KoDummyColorProfile); // Create the built-in colorspaces QList localFactories; localFactories << new KoAlphaColorSpaceFactory() << new KoAlphaU16ColorSpaceFactory() #ifdef HAVE_OPENEXR << new KoAlphaF16ColorSpaceFactory() #endif << new KoAlphaF32ColorSpaceFactory() << new KoLabColorSpaceFactory() << new KoRgbU8ColorSpaceFactory() << new KoRgbU16ColorSpaceFactory(); Q_FOREACH (KoColorSpaceFactory *factory, localFactories) { add(factory); } KoPluginLoader::PluginsConfig config; config.whiteList = "ColorSpacePlugins"; config.blacklist = "ColorSpacePluginsDisabled"; config.group = "calligra"; KoPluginLoader::instance()->load("Calligra/ColorSpace", "[X-Pigment-PluginVersion] == 28", config); KoPluginLoader::PluginsConfig configExtensions; configExtensions.whiteList = "ColorSpaceExtensionsPlugins"; configExtensions.blacklist = "ColorSpaceExtensionsPluginsDisabled"; configExtensions.group = "calligra"; KoPluginLoader::instance()->load("Calligra/ColorSpaceExtension", "[X-Pigment-PluginVersion] == 28", configExtensions); dbgPigment << "Loaded the following colorspaces:"; Q_FOREACH (const KoID& id, listKeys()) { dbgPigment << "\t" << id.id() << "," << id.name(); } } KoColorSpaceRegistry::KoColorSpaceRegistry() : d(new Private(this)) { d->colorConversionSystem = 0; d->colorConversionCache = 0; } KoColorSpaceRegistry::~KoColorSpaceRegistry() { // Just leak on exit... It's faster. // delete d->colorConversionSystem; // Q_FOREACH (KoColorProfile* profile, d->profileMap) { // delete profile; // } // d->profileMap.clear(); // Q_FOREACH (const KoColorSpace * cs, d->csMap) { // cs->d->deletability = OwnedByRegistryRegistryDeletes; // } // d->csMap.clear(); // // deleting colorspaces calls a function in the cache // delete d->colorConversionCache; // d->colorConversionCache = 0; // // Delete the colorspace factories // qDeleteAll(d->localFactories); delete d; } void KoColorSpaceRegistry::add(KoColorSpaceFactory* item) { QWriteLocker l(&d->registrylock); d->colorSpaceFactoryRegistry.add(item); d->colorConversionSystem->insertColorSpace(item); } void KoColorSpaceRegistry::remove(KoColorSpaceFactory* item) { QWriteLocker l(&d->registrylock); QList toremove; Q_FOREACH (const KoColorSpace * cs, d->csMap) { if (cs->id() == item->id()) { toremove.push_back(d->idsToCacheName(cs->id(), cs->profile()->name())); cs->d->deletability = OwnedByRegistryRegistryDeletes; } } Q_FOREACH (const QString& id, toremove) { d->csMap.remove(id); // TODO: should not it delete the color space when removing it from the map ? } d->colorSpaceFactoryRegistry.remove(item->id()); } void KoColorSpaceRegistry::addProfileAlias(const QString& name, const QString& to) { d->profileStorage.addProfileAlias(name, to); } QString KoColorSpaceRegistry::profileAlias(const QString& name) const { return d->profileStorage.profileAlias(name); } const KoColorProfile* KoColorSpaceRegistry::profileByName(const QString &name) const { return d->profileStorage.profileByName(name); } const KoColorProfile * KoColorSpaceRegistry::profileByUniqueId(const QByteArray &id) const { return d->profileStorage.profileByUniqueId(id); } QList KoColorSpaceRegistry::profilesFor(const QString &csID) const { QReadLocker l(&d->registrylock); return d->profileStorage.profilesFor(d->colorSpaceFactoryRegistry.value(csID)); } const KoColorSpace * KoColorSpaceRegistry::colorSpace(const QString & colorModelId, const QString & colorDepthId, const KoColorProfile *profile) { return d->colorSpace1(colorSpaceId(colorModelId, colorDepthId), profile); } const KoColorSpace * KoColorSpaceRegistry::colorSpace(const QString & colorModelId, const QString & colorDepthId, const QString &profileName) { return d->colorSpace1(colorSpaceId(colorModelId, colorDepthId), profileName); } const KoColorSpace * KoColorSpaceRegistry::colorSpace(const QString & colorModelId, const QString & colorDepthId) { return d->colorSpace1(colorSpaceId(colorModelId, colorDepthId)); } bool KoColorSpaceRegistry::profileIsCompatible(const KoColorProfile *profile, const QString &colorSpaceId) { QReadLocker l(&d->registrylock); KoColorSpaceFactory *csf = d->colorSpaceFactoryRegistry.value(colorSpaceId); return csf ? csf->profileIsCompatible(profile) : false; } void KoColorSpaceRegistry::addProfileToMap(KoColorProfile *p) { d->profileStorage.addProfile(p); } void KoColorSpaceRegistry::addProfile(KoColorProfile *p) { if (!p->valid()) return; QWriteLocker locker(&d->registrylock); if (p->valid()) { addProfileToMap(p); d->colorConversionSystem->insertColorProfile(p); } } void KoColorSpaceRegistry::addProfile(const KoColorProfile* profile) { addProfile(profile->clone()); } void KoColorSpaceRegistry::removeProfile(KoColorProfile* profile) { d->profileStorage.removeProfile(profile); // FIXME: how about removing it from conversion system? } const KoColorSpace* KoColorSpaceRegistry::Private::getCachedColorSpaceImpl(const QString & csID, const QString & profileName) const { auto it = csMap.find(idsToCacheName(csID, profileName)); if (it != csMap.end()) { return it.value(); } return 0; } QString KoColorSpaceRegistry::Private::idsToCacheName(const QString & csID, const QString & profileName) const { return csID + "" + profileName; } QString KoColorSpaceRegistry::defaultProfileForColorSpace(const QString &colorSpaceId) const { QReadLocker l(&d->registrylock); return d->defaultProfileForCsIdImpl(colorSpaceId); } KoColorConversionTransformation *KoColorSpaceRegistry::createColorConverter(const KoColorSpace *srcColorSpace, const KoColorSpace *dstColorSpace, KoColorConversionTransformation::Intent renderingIntent, KoColorConversionTransformation::ConversionFlags conversionFlags) const { QWriteLocker l(&d->registrylock); return d->colorConversionSystem->createColorConverter(srcColorSpace, dstColorSpace, renderingIntent, conversionFlags); } void KoColorSpaceRegistry::createColorConverters(const KoColorSpace *colorSpace, const QList > &possibilities, KoColorConversionTransformation *&fromCS, KoColorConversionTransformation *&toCS) const { QWriteLocker l(&d->registrylock); d->colorConversionSystem->createColorConverters(colorSpace, possibilities, fromCS, toCS); } QString KoColorSpaceRegistry::Private::defaultProfileForCsIdImpl(const QString &csID) { QString defaultProfileName; KoColorSpaceFactory *csf = colorSpaceFactoryRegistry.value(csID); if (csf) { defaultProfileName = csf->defaultProfile(); } else { dbgPigmentCSRegistry << "Unknown color space type : " << csID; } return defaultProfileName; } const KoColorProfile *KoColorSpaceRegistry::Private::profileForCsIdWithFallbackImpl(const QString &csID, const QString &profileName) { const KoColorProfile *profile = 0; // last attempt at getting a profile, sometimes the default profile, like adobe cmyk isn't available. profile = profileStorage.profileByName(profileName); if (!profile) { dbgPigmentCSRegistry << "Profile not found :" << profileName; // first try: default profile = profileStorage.profileByName(defaultProfileForCsIdImpl(csID)); if (!profile) { // second try: first profile in the list QList profiles = profileStorage.profilesFor(colorSpaceFactoryRegistry.value(csID)); if (profiles.isEmpty() || !profiles.first()) { dbgPigmentCSRegistry << "Couldn't fetch a fallback profile:" << profileName; + qWarning() << "profileForCsIdWithFallbackImpl couldn't fetch a fallback profile for " << qUtf8Printable(profileName); return 0; } profile = profiles.first(); } } return profile; } const KoColorSpace *KoColorSpaceRegistry::Private::lazyCreateColorSpaceImpl(const QString &csID, const KoColorProfile *profile) { const KoColorSpace *cs = 0; /* * We need to check again here, a thread requesting the same colorspace could've added it * already, in between the read unlock and write lock. * TODO: We also potentially changed profileName content, which means we maybe are going to * create a colorspace that's actually in the space registry cache, but currently this might * not be an issue because the colorspace should be cached also by the factory, so it won't * create a new instance. That being said, having two caches with the same stuff doesn't make * much sense. */ cs = getCachedColorSpaceImpl(csID, profile->name()); if (!cs) { KoColorSpaceFactory *csf = colorSpaceFactoryRegistry.value(csID); cs = csf->grabColorSpace(profile); if (!cs) { dbgPigmentCSRegistry << "Unable to create color space"; + qWarning() << "lazyCreateColorSpaceImpl was unable to create a color space for " << csID; return 0; } dbgPigmentCSRegistry << "colorspace count: " << csMap.count() << ", adding name: " << idsToCacheName(cs->id(), cs->profile()->name()) << "\n\tcsID" << csID << "\n\tcs->id()" << cs->id() << "\n\tcs->profile()->name()" << cs->profile()->name() << "\n\tprofile->name()" << profile->name(); Q_ASSERT(cs->id() == csID); Q_ASSERT(cs->profile()->name() == profile->name()); csMap[idsToCacheName(cs->id(), cs->profile()->name())] = cs; cs->d->deletability = OwnedByRegistryDoNotDelete; } return cs; } template const KoColorSpace * KoColorSpaceRegistry::Private::colorSpace1(const QString &csID, const QString &pName) { QString profileName = pName; const KoColorSpace *cs = 0; { typename LockPolicy::ReadLocker l(®istrylock); if (profileName.isEmpty()) { profileName = defaultProfileForCsIdImpl(csID); if (profileName.isEmpty()) return 0; } // quick attempt to fetch a cached color space cs = getCachedColorSpaceImpl(csID, profileName); } if (!cs) { // slow attemt to create a color space typename LockPolicy::WriteLocker l(®istrylock); const KoColorProfile *profile = profileForCsIdWithFallbackImpl(csID, profileName); // until kis_asert.h is not available in 3.1, use this combo Q_ASSERT(profile); if (!profile) return 0; cs = lazyCreateColorSpaceImpl(csID, profile); } else { KIS_SAFE_ASSERT_RECOVER_NOOP(cs->id() == csID); KIS_SAFE_ASSERT_RECOVER_NOOP(cs->profile()->name() == profileName); } return cs; } const KoColorSpace * KoColorSpaceRegistry::Private::colorSpace1(const QString &csID, const KoColorProfile *profile) { if (csID.isEmpty()) { return 0; } else if (!profile) { return colorSpace1(csID); } const KoColorSpace *cs = 0; { QReadLocker l(®istrylock); cs = getCachedColorSpaceImpl(csID, profile->name()); } // the profile should have already been added to the registry by createColorProfile() method KIS_SAFE_ASSERT_RECOVER(profileStorage.containsProfile(profile)) { // warning! locking happens inside addProfile! q->addProfile(profile); } if (!cs) { // The profile was not stored and thus not the combination either QWriteLocker l(®istrylock); KoColorSpaceFactory *csf = colorSpaceFactoryRegistry.value(csID); if (!csf) { dbgPigmentCSRegistry << "Unknown color space type :" << csf; return 0; } if (!csf->profileIsCompatible(profile)) { dbgPigmentCSRegistry << "Profile is not compatible:" << csf << profile->name(); return 0; } cs = lazyCreateColorSpaceImpl(csID, profile); } return cs; } const KoColorSpace * KoColorSpaceRegistry::alpha8() { if (!d->alphaCs) { d->alphaCs = d->colorSpace1(KoAlphaColorSpace::colorSpaceId()); } Q_ASSERT(d->alphaCs); return d->alphaCs; } const KoColorSpace * KoColorSpaceRegistry::alpha16() { if (!d->alphaU16Cs) { d->alphaU16Cs = d->colorSpace1(KoAlphaU16ColorSpace::colorSpaceId()); } Q_ASSERT(d->alphaU16Cs); return d->alphaU16Cs; } #ifdef HAVE_OPENEXR const KoColorSpace * KoColorSpaceRegistry::alpha16f() { if (!d->alphaF16Cs) { d->alphaF16Cs = d->colorSpace1(KoAlphaF16ColorSpace::colorSpaceId()); } Q_ASSERT(d->alphaF16Cs); return d->alphaF16Cs; } #endif const KoColorSpace * KoColorSpaceRegistry::alpha32f() { if (!d->alphaF32Cs) { d->alphaF32Cs = d->colorSpace1(KoAlphaF32ColorSpace::colorSpaceId()); } Q_ASSERT(d->alphaF32Cs); return d->alphaF32Cs; } const KoColorSpace * KoColorSpaceRegistry::rgb8(const QString &profileName) { if (profileName.isEmpty()) { if (!d->rgbU8sRGB) { d->rgbU8sRGB = d->colorSpace1(KoRgbU8ColorSpace::colorSpaceId()); } Q_ASSERT(d->rgbU8sRGB); return d->rgbU8sRGB; } return d->colorSpace1(KoRgbU8ColorSpace::colorSpaceId(), profileName); } const KoColorSpace * KoColorSpaceRegistry::rgb8(const KoColorProfile * profile) { if (profile == 0) { if (!d->rgbU8sRGB) { d->rgbU8sRGB = d->colorSpace1(KoRgbU8ColorSpace::colorSpaceId()); } Q_ASSERT(d->rgbU8sRGB); return d->rgbU8sRGB; } return d->colorSpace1(KoRgbU8ColorSpace::colorSpaceId(), profile); } const KoColorSpace * KoColorSpaceRegistry::rgb16(const QString &profileName) { return d->colorSpace1(KoRgbU16ColorSpace::colorSpaceId(), profileName); } const KoColorSpace * KoColorSpaceRegistry::rgb16(const KoColorProfile * profile) { return d->colorSpace1(KoRgbU16ColorSpace::colorSpaceId(), profile); } const KoColorSpace * KoColorSpaceRegistry::lab16(const QString &profileName) { if (profileName.isEmpty()) { if (!d->lab16sLAB) { d->lab16sLAB = d->colorSpace1(KoLabColorSpace::colorSpaceId()); } return d->lab16sLAB; } return d->colorSpace1(KoLabColorSpace::colorSpaceId(), profileName); } const KoColorSpace * KoColorSpaceRegistry::lab16(const KoColorProfile * profile) { if (profile == 0) { if (!d->lab16sLAB) { d->lab16sLAB = d->colorSpace1(KoLabColorSpace::colorSpaceId()); } Q_ASSERT(d->lab16sLAB); return d->lab16sLAB; } return d->colorSpace1(KoLabColorSpace::colorSpaceId(), profile); } QList KoColorSpaceRegistry::colorModelsList(ColorSpaceListVisibility option) const { QReadLocker l(&d->registrylock); QList ids; QList factories = d->colorSpaceFactoryRegistry.values(); Q_FOREACH (KoColorSpaceFactory* factory, factories) { if (!ids.contains(factory->colorModelId()) && (option == AllColorSpaces || factory->userVisible())) { ids << factory->colorModelId(); } } return ids; } QList KoColorSpaceRegistry::colorDepthList(const KoID& colorModelId, ColorSpaceListVisibility option) const { return colorDepthList(colorModelId.id(), option); } QList KoColorSpaceRegistry::colorDepthList(const QString & colorModelId, ColorSpaceListVisibility option) const { QReadLocker l(&d->registrylock); QList ids; QList factories = d->colorSpaceFactoryRegistry.values(); Q_FOREACH (KoColorSpaceFactory* factory, factories) { if (!ids.contains(KoID(factory->colorDepthId())) && factory->colorModelId().id() == colorModelId && (option == AllColorSpaces || factory->userVisible())) { ids << factory->colorDepthId(); } } return ids; } QString KoColorSpaceRegistry::Private::colorSpaceIdImpl(const QString & colorModelId, const QString & colorDepthId) const { QList factories = colorSpaceFactoryRegistry.values(); Q_FOREACH (KoColorSpaceFactory* factory, factories) { if (factory->colorModelId().id() == colorModelId && factory->colorDepthId().id() == colorDepthId) { return factory->id(); } } return ""; } QString KoColorSpaceRegistry::colorSpaceId(const QString & colorModelId, const QString & colorDepthId) const { QReadLocker l(&d->registrylock); return d->colorSpaceIdImpl(colorModelId, colorDepthId); } QString KoColorSpaceRegistry::colorSpaceId(const KoID& colorModelId, const KoID& colorDepthId) const { return colorSpaceId(colorModelId.id(), colorDepthId.id()); } KoID KoColorSpaceRegistry::colorSpaceColorModelId(const QString & _colorSpaceId) const { QReadLocker l(&d->registrylock); KoColorSpaceFactory* factory = d->colorSpaceFactoryRegistry.get(_colorSpaceId); if (factory) { return factory->colorModelId(); } else { return KoID(); } } KoID KoColorSpaceRegistry::colorSpaceColorDepthId(const QString & _colorSpaceId) const { QReadLocker l(&d->registrylock); KoColorSpaceFactory* factory = d->colorSpaceFactoryRegistry.get(_colorSpaceId); if (factory) { return factory->colorDepthId(); } else { return KoID(); } } const KoColorConversionSystem* KoColorSpaceRegistry::colorConversionSystem() const { return d->colorConversionSystem; } KoColorConversionCache* KoColorSpaceRegistry::colorConversionCache() const { return d->colorConversionCache; } const KoColorSpace* KoColorSpaceRegistry::permanentColorspace(const KoColorSpace* _colorSpace) { if (_colorSpace->d->deletability != NotOwnedByRegistry) { return _colorSpace; } else if (*_colorSpace == *d->alphaCs) { return d->alphaCs; } else { const KoColorSpace* cs = d->colorSpace1(_colorSpace->id(), _colorSpace->profile()); Q_ASSERT(cs); Q_ASSERT(*cs == *_colorSpace); return cs; } } QList KoColorSpaceRegistry::listKeys() const { QReadLocker l(&d->registrylock); QList answer; Q_FOREACH (const QString& key, d->colorSpaceFactoryRegistry.keys()) { answer.append(KoID(key, d->colorSpaceFactoryRegistry.get(key)->name())); } return answer; } struct KoColorSpaceRegistry::Private::ProfileRegistrationInterface : public KoColorSpaceFactory::ProfileRegistrationInterface { ProfileRegistrationInterface(KoColorSpaceRegistry::Private *_d) : d(_d) {} const KoColorProfile* profileByName(const QString &profileName) const override { return d->profileStorage.profileByName(profileName); } void registerNewProfile(KoColorProfile *profile) override { d->profileStorage.addProfile(profile); d->colorConversionSystem->insertColorProfile(profile); } KoColorSpaceRegistry::Private *d; }; const KoColorProfile* KoColorSpaceRegistry::createColorProfile(const QString& colorModelId, const QString& colorDepthId, const QByteArray& rawData) { QWriteLocker l(&d->registrylock); KoColorSpaceFactory* factory_ = d->colorSpaceFactoryRegistry.get(d->colorSpaceIdImpl(colorModelId, colorDepthId)); Private::ProfileRegistrationInterface interface(d); return factory_->colorProfile(rawData, &interface); } QList KoColorSpaceRegistry::allColorSpaces(ColorSpaceListVisibility visibility, ColorSpaceListProfilesSelection pSelection) { QList colorSpaces; // TODO: thread-unsafe code: the factories might change right after the lock in released // HINT: used in a unittest only! d->registrylock.lockForRead(); QList factories = d->colorSpaceFactoryRegistry.values(); d->registrylock.unlock(); Q_FOREACH (KoColorSpaceFactory* factory, factories) { // Don't test with ycbcr for now, since we don't have a default profile for it. if (factory->colorModelId().id().startsWith("Y")) continue; if (visibility == AllColorSpaces || factory->userVisible()) { if (pSelection == OnlyDefaultProfile) { const KoColorSpace *cs = d->colorSpace1(factory->id()); if (cs) { colorSpaces.append(cs); } else { warnPigment << "Could not create colorspace for id" << factory->id() << "since there is no working default profile"; } } else { QList profiles = KoColorSpaceRegistry::instance()->profilesFor(factory->id()); Q_FOREACH (const KoColorProfile * profile, profiles) { const KoColorSpace *cs = d->colorSpace1(factory->id(), profile); if (cs) { colorSpaces.append(cs); } else { warnPigment << "Could not create colorspace for id" << factory->id() << "and profile" << profile->name(); } } } } } return colorSpaces; } diff --git a/libs/pigment/KoColorSpaceTraits.h b/libs/pigment/KoColorSpaceTraits.h index 73fec601f6..6f81dbc3bb 100644 --- a/libs/pigment/KoColorSpaceTraits.h +++ b/libs/pigment/KoColorSpaceTraits.h @@ -1,228 +1,235 @@ /* * Copyright (c) 2006-2007 Cyrille Berger * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * 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. */ #ifndef _KO_COLORSPACE_TRAITS_H_ #define _KO_COLORSPACE_TRAITS_H_ #include #include "KoColorSpaceConstants.h" #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. * * - _channels_type_ is the type of the value use for each channel, for example quint8 for 8bits per channel * color spaces, or quint16 for 16bits integer per channel, float for 32bits per channel * floating point color spaces * - _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\ * */ 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 */ inline static quint8 opacityU8(const quint8 * U8_pixel) { if (alpha_pos < 0) return OPACITY_OPAQUE_U8; 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 */ inline static void setOpacity(quint8 * pixels, quint8 alpha, qint32 nPixels) { if (alpha_pos < 0) return; qint32 psize = pixelSize; channels_type valpha = KoColorSpaceMaths::scaleToA(alpha); for (; nPixels > 0; --nPixels, pixels += psize) { nativeArray(pixels)[alpha_pos] = valpha; } } - + inline static void setOpacity(quint8 * pixels, qreal alpha, qint32 nPixels) { if (alpha_pos < 0) return; qint32 psize = pixelSize; channels_type valpha = KoColorSpaceMaths::scaleToA(alpha); for (; nPixels > 0; --nPixels, pixels += psize) { 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); for (uint i = 0; i < channels_nb; i++) { if (i != channelIndex) { dst[i] = 0; } else { dst[i] = src[i]; } } } - + inline static QString channelValueText(const quint8 *pixel, quint32 channelIndex) { if (channelIndex > channels_nb) return QString("Error"); channels_type c = nativeArray(pixel)[channelIndex]; return QString().setNum(c); } inline static QString normalisedChannelValueText(const quint8 *pixel, quint32 channelIndex) { if (channelIndex > channels_nb) return QString("Error"); channels_type c = nativeArray(pixel)[channelIndex]; return QString().setNum(100. *((qreal)c) / KoColorSpaceMathsTraits< channels_type>::unitValue); } inline static void normalisedChannelsValue(const quint8 *pixel, QVector &channels) { Q_ASSERT((int)channels.count() == (int)channels_nb); channels_type c; for (uint i = 0; i < channels_nb; i++) { c = nativeArray(pixel)[i]; channels[i] = ((qreal)c) / KoColorSpaceMathsTraits::unitValue; } } inline static void fromNormalisedChannelsValue(quint8 *pixel, const QVector &values) { Q_ASSERT((int)values.count() == (int)channels_nb); channels_type c; for (uint i = 0; i < channels_nb; i++) { float b = qBound((float)KoColorSpaceMathsTraits::min, (float)KoColorSpaceMathsTraits::unitValue * values[i], (float)KoColorSpaceMathsTraits::max); c = (channels_type)b; nativeArray(pixel)[i] = c; } } inline static void multiplyAlpha(quint8 * pixels, quint8 alpha, qint32 nPixels) { if (alpha_pos < 0) return; channels_type valpha = KoColorSpaceMaths::scaleToA(alpha); for (; nPixels > 0; --nPixels, pixels += pixelSize) { channels_type* alphapixel = nativeArray(pixels) + alpha_pos; *alphapixel = KoColorSpaceMaths::multiply(*alphapixel, valpha); } } inline static void applyAlphaU8Mask(quint8 * pixels, const quint8 * alpha, qint32 nPixels) { if (alpha_pos < 0) return; for (; nPixels > 0; --nPixels, pixels += pixelSize, ++alpha) { channels_type valpha = KoColorSpaceMaths::scaleToA(*alpha); channels_type* alphapixel = nativeArray(pixels) + alpha_pos; *alphapixel = KoColorSpaceMaths::multiply(*alphapixel, valpha); } } inline static void applyInverseAlphaU8Mask(quint8 * pixels, const quint8 * alpha, qint32 nPixels) { if (alpha_pos < 0) return; for (; nPixels > 0; --nPixels, pixels += pixelSize, ++alpha) { channels_type valpha = KoColorSpaceMaths::scaleToA(OPACITY_OPAQUE_U8 - *alpha); channels_type* alphapixel = nativeArray(pixels) + alpha_pos; *alphapixel = KoColorSpaceMaths::multiply(*alphapixel, valpha); } } inline static void applyAlphaNormedFloatMask(quint8 * pixels, const float * alpha, qint32 nPixels) { if (alpha_pos < 0) return; for (; nPixels > 0; --nPixels, pixels += pixelSize, ++alpha) { channels_type valpha = channels_type(KoColorSpaceMathsTraits::unitValue * (*alpha)); channels_type* alphapixel = nativeArray(pixels) + alpha_pos; *alphapixel = KoColorSpaceMaths::multiply(*alphapixel, valpha); } } inline static void applyInverseAlphaNormedFloatMask(quint8 * pixels, const float * alpha, qint32 nPixels) { if (alpha_pos < 0) return; for (; nPixels > 0; --nPixels, pixels += pixelSize, ++alpha) { channels_type valpha = channels_type(KoColorSpaceMathsTraits::unitValue * (1.0f - *alpha)); channels_type* alphapixel = nativeArray(pixels) + alpha_pos; *alphapixel = KoColorSpaceMaths::multiply(*alphapixel, valpha); } } }; #include "KoRgbColorSpaceTraits.h" #include "KoBgrColorSpaceTraits.h" #include "KoGrayColorSpaceTraits.h" #include "KoLabColorSpaceTraits.h" #include "KoXyzColorSpaceTraits.h" #include "KoYcbcrColorSpaceTraits.h" #include "KoCmykColorSpaceTraits.h" #endif diff --git a/libs/ui/KisApplication.cpp b/libs/ui/KisApplication.cpp index 592f4bc9c3..0fef183724 100644 --- a/libs/ui/KisApplication.cpp +++ b/libs/ui/KisApplication.cpp @@ -1,796 +1,803 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999 Torben Weis Copyright (C) 2009 Thomas Zander Copyright (C) 2012 Boudewijn Rempt This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License 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. */ #include "KisApplication.h" #include #ifdef Q_OS_WIN #include #include #endif #ifdef Q_OS_OSX #include "osx.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KoGlobal.h" #include "KoConfig.h" #include #include #include #include "thememanager.h" #include "KisPrintJob.h" #include "KisDocument.h" #include "KisMainWindow.h" #include "KisAutoSaveRecoveryDialog.h" #include "KisPart.h" #include #include "kis_md5_generator.h" #include "kis_splash_screen.h" #include "kis_config.h" #include "flake/kis_shape_selection.h" #include #include #include #include #include #include #include #include "kisexiv2/kis_exiv2.h" #include "KisApplicationArguments.h" #include #include "kis_action_registry.h" #include #include #include #include "kis_image_barrier_locker.h" #include "opengl/kis_opengl.h" #include "kis_spin_box_unit_manager.h" #include "kis_document_aware_spin_box_unit_manager.h" #include "KisViewManager.h" #include "kis_workspace_resource.h" #include namespace { const QTime appStartTime(QTime::currentTime()); } class KisApplicationPrivate { public: KisApplicationPrivate() : splashScreen(0) {} QPointer splashScreen; }; class KisApplication::ResetStarting { public: ResetStarting(KisSplashScreen *splash, int fileCount) : m_splash(splash) , m_fileCount(fileCount) { } ~ResetStarting() { if (m_splash) { KConfigGroup cfg( KSharedConfig::openConfig(), "SplashScreen"); bool hideSplash = cfg.readEntry("HideSplashAfterStartup", false); if (m_fileCount > 0 || hideSplash) { m_splash->hide(); } else { m_splash->setWindowFlags(Qt::Dialog); QRect r(QPoint(), m_splash->size()); m_splash->move(QApplication::desktop()->availableGeometry().center() - r.center()); m_splash->setWindowTitle(qAppName()); m_splash->setParent(0); Q_FOREACH (QObject *o, m_splash->children()) { QWidget *w = qobject_cast(o); if (w && w->isHidden()) { w->setVisible(true); } } m_splash->show(); m_splash->activateWindow(); } } } QPointer m_splash; int m_fileCount; }; KisApplication::KisApplication(const QString &key, int &argc, char **argv) : QtSingleApplication(key, argc, argv) , d(new KisApplicationPrivate) , m_autosaveDialog(0) , m_mainWindow(0) , m_batchRun(false) { #ifdef Q_OS_OSX setMouseCoalescingEnabled(false); #endif QCoreApplication::addLibraryPath(QCoreApplication::applicationDirPath()); setApplicationDisplayName("Krita"); setApplicationName("krita"); // Note: Qt docs suggest we set this, but if we do, we get resource paths of the form of krita/krita, which is weird. // setOrganizationName("krita"); setOrganizationDomain("krita.org"); QString version = KritaVersionWrapper::versionString(true); setApplicationVersion(version); setWindowIcon(KisIconUtils::loadIcon("calligrakrita")); if (qgetenv("KRITA_NO_STYLE_OVERRIDE").isEmpty()) { QStringList styles = QStringList() << "breeze" << "fusion" << "plastique"; if (!styles.contains(style()->objectName().toLower())) { Q_FOREACH (const QString & style, styles) { if (!setStyle(style)) { qDebug() << "No" << style << "available."; } else { qDebug() << "Set style" << style; break; } } } } else { qDebug() << "Style override disabled, using" << style()->objectName(); } KisOpenGL::initialize(); qDebug() << "krita has opengl" << KisOpenGL::hasOpenGL(); } #if defined(Q_OS_WIN) && defined(ENV32BIT) typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL); LPFN_ISWOW64PROCESS fnIsWow64Process; BOOL isWow64() { BOOL bIsWow64 = FALSE; //IsWow64Process is not available on all supported versions of Windows. //Use GetModuleHandle to get a handle to the DLL that contains the function //and GetProcAddress to get a pointer to the function if available. fnIsWow64Process = (LPFN_ISWOW64PROCESS) GetProcAddress( GetModuleHandle(TEXT("kernel32")),"IsWow64Process"); if(0 != fnIsWow64Process) { if (!fnIsWow64Process(GetCurrentProcess(),&bIsWow64)) { //handle error } } return bIsWow64; } #endif void KisApplication::initializeGlobals(const KisApplicationArguments &args) { int dpiX = args.dpiX(); int dpiY = args.dpiY(); if (dpiX > 0 && dpiY > 0) { KoDpi::setDPI(dpiX, dpiY); } } void KisApplication::addResourceTypes() { // All Krita's resource types KoResourcePaths::addResourceType("kis_pics", "data", "/pics/"); KoResourcePaths::addResourceType("kis_images", "data", "/images/"); KoResourcePaths::addResourceType("icc_profiles", "data", "/profiles/"); KoResourcePaths::addResourceType("metadata_schema", "data", "/metadata/schemas/"); KoResourcePaths::addResourceType("kis_brushes", "data", "/brushes/"); KoResourcePaths::addResourceType("kis_taskset", "data", "/taskset/"); KoResourcePaths::addResourceType("kis_taskset", "data", "/taskset/"); KoResourcePaths::addResourceType("gmic_definitions", "data", "/gmic/"); KoResourcePaths::addResourceType("kis_resourcebundles", "data", "/bundles/"); KoResourcePaths::addResourceType("kis_defaultpresets", "data", "/defaultpresets/"); KoResourcePaths::addResourceType("kis_paintoppresets", "data", "/paintoppresets/"); KoResourcePaths::addResourceType("kis_workspaces", "data", "/workspaces/"); KoResourcePaths::addResourceType("psd_layer_style_collections", "data", "/asl"); KoResourcePaths::addResourceType("ko_patterns", "data", "/patterns/", true); KoResourcePaths::addResourceType("ko_gradients", "data", "/gradients/"); KoResourcePaths::addResourceType("ko_gradients", "data", "/gradients/", true); KoResourcePaths::addResourceType("ko_palettes", "data", "/palettes/", true); KoResourcePaths::addResourceType("kis_shortcuts", "data", "/shortcuts/"); KoResourcePaths::addResourceType("kis_actions", "data", "/actions"); KoResourcePaths::addResourceType("icc_profiles", "data", "/color/icc"); KoResourcePaths::addResourceType("ko_effects", "data", "/effects/"); KoResourcePaths::addResourceType("tags", "data", "/tags/"); KoResourcePaths::addResourceType("templates", "data", "/templates"); KoResourcePaths::addResourceType("pythonscripts", "data", "/pykrita"); KoResourcePaths::addResourceType("symbols", "data", "/symbols"); // // Extra directories to look for create resources. (Does anyone actually use that anymore?) // KoResourcePaths::addResourceDir("ko_gradients", "/usr/share/create/gradients/gimp"); // KoResourcePaths::addResourceDir("ko_gradients", QDir::homePath() + QString("/.create/gradients/gimp")); // KoResourcePaths::addResourceDir("ko_patterns", "/usr/share/create/patterns/gimp"); // KoResourcePaths::addResourceDir("ko_patterns", QDir::homePath() + QString("/.create/patterns/gimp")); // KoResourcePaths::addResourceDir("kis_brushes", "/usr/share/create/brushes/gimp"); // KoResourcePaths::addResourceDir("kis_brushes", QDir::homePath() + QString("/.create/brushes/gimp")); // KoResourcePaths::addResourceDir("ko_palettes", "/usr/share/create/swatches"); // KoResourcePaths::addResourceDir("ko_palettes", QDir::homePath() + QString("/.create/swatches")); // Make directories for all resources we can save, and tags QDir d; d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/tags/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/asl/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/bundles/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/gradients/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/paintoppresets/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/palettes/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/patterns/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/taskset/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/workspaces/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/input/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/pykrita/"); d.mkpath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/symbols/"); + + // Indicate that it is now safe for users of KoResourcePaths to load resources + KoResourcePaths::setReady(); } void KisApplication::loadResources() { setSplashScreenLoadingText(i18n("Loading Gradients...")); processEvents(); KoResourceServerProvider::instance()->gradientServer(true); // Load base resources setSplashScreenLoadingText(i18n("Loading Patterns...")); processEvents(); KoResourceServerProvider::instance()->patternServer(true); setSplashScreenLoadingText(i18n("Loading Palettes...")); processEvents(); KoResourceServerProvider::instance()->paletteServer(false); setSplashScreenLoadingText(i18n("Loading Brushes...")); processEvents(); KisBrushServer::instance()->brushServer(true); // load paintop presets setSplashScreenLoadingText(i18n("Loading Paint Operations...")); processEvents(); KisResourceServerProvider::instance()->paintOpPresetServer(true); // load symbols setSplashScreenLoadingText(i18n("Loading SVG Symbol Collections...")); processEvents(); KoResourceServerProvider::instance()->svgSymbolCollectionServer(true); setSplashScreenLoadingText(i18n("Loading Resource Bundles...")); processEvents(); KisResourceServerProvider::instance()->resourceBundleServer(); } void KisApplication::loadPlugins() { KoShapeRegistry* r = KoShapeRegistry::instance(); r->add(new KisShapeSelectionFactory()); KisActionRegistry::instance(); KisFilterRegistry::instance(); KisGeneratorRegistry::instance(); KisPaintOpRegistry::instance(); KoColorSpaceRegistry::instance(); // Load the krita-specific tools setSplashScreenLoadingText(i18n("Loading Plugins for Krita/Tool...")); processEvents(); KoPluginLoader::instance()->load(QString::fromLatin1("Krita/Tool"), QString::fromLatin1("[X-Krita-Version] == 28")); // Load dockers setSplashScreenLoadingText(i18n("Loading Plugins for Krita/Dock...")); processEvents(); KoPluginLoader::instance()->load(QString::fromLatin1("Krita/Dock"), QString::fromLatin1("[X-Krita-Version] == 28")); // XXX_EXIV: make the exiv io backends real plugins setSplashScreenLoadingText(i18n("Loading Plugins Exiv/IO...")); processEvents(); KisExiv2::initialize(); } bool KisApplication::start(const KisApplicationArguments &args) { KisConfig cfg; #if defined(Q_OS_WIN) #ifdef ENV32BIT if (isWow64() && !cfg.readEntry("WarnedAbout32Bits", false)) { QMessageBox::information(0, i18nc("@title:window", "Krita: Warning"), i18n("You are running a 32 bits build on a 64 bits Windows.\n" "This is not recommended.\n" "Please download and install the x64 build instead.")); cfg.writeEntry("WarnedAbout32Bits", true); } #endif #endif QString opengl = cfg.canvasState(); if (opengl == "OPENGL_NOT_TRIED" ) { cfg.setCanvasState("TRY_OPENGL"); } else if (opengl != "OPENGL_SUCCESS") { cfg.setCanvasState("OPENGL_FAILED"); } setSplashScreenLoadingText(i18n("Initializing Globals")); processEvents(); initializeGlobals(args); const bool doTemplate = args.doTemplate(); const bool print = args.print(); const bool exportAs = args.exportAs(); const bool exportAsPdf = args.exportAsPdf(); const QString exportFileName = args.exportFileName(); m_batchRun = (print || exportAs || exportAsPdf || !exportFileName.isEmpty()); // print & exportAsPdf do user interaction ATM const bool needsMainWindow = !exportAs; // only show the mainWindow when no command-line mode option is passed // TODO: fix print & exportAsPdf to work without mainwindow shown const bool showmainWindow = !exportAs; // would be !batchRun; const bool showSplashScreen = !m_batchRun && qEnvironmentVariableIsEmpty("NOSPLASH");// && qgetenv("XDG_CURRENT_DESKTOP") != "GNOME"; if (showSplashScreen && d->splashScreen) { d->splashScreen->show(); d->splashScreen->repaint(); processEvents(); } KoHashGeneratorProvider::instance()->setGenerator("MD5", new KisMD5Generator()); // Initialize all Krita directories etc. KoGlobal::initialize(); KConfigGroup group(KSharedConfig::openConfig(), "theme"); Digikam::ThemeManager themeManager; themeManager.setCurrentTheme(group.readEntry("Theme", "Krita dark")); ResetStarting resetStarting(d->splashScreen, args.filenames().count()); // remove the splash when done Q_UNUSED(resetStarting); // Make sure we can save resources and tags setSplashScreenLoadingText(i18n("Adding resource types")); processEvents(); addResourceTypes(); + // now we're set up, and the LcmsEnginePlugin will have access to resource paths for color management, + // we can finally initialize KoColor. + KoColor::init(); + // Load all resources and tags before the plugins do that loadResources(); // Load the plugins loadPlugins(); if (needsMainWindow) { // show a mainWindow asap, if we want that setSplashScreenLoadingText(i18n("Loading Main Window...")); processEvents(); m_mainWindow = KisPart::instance()->createMainWindow(); if (showmainWindow) { m_mainWindow->initializeGeometry(); if (!args.workspace().isEmpty()) { KoResourceServer * rserver = KisResourceServerProvider::instance()->workspaceServer(); KisWorkspaceResource* workspace = rserver->resourceByName(args.workspace()); if (workspace) { m_mainWindow->restoreWorkspace(workspace->dockerState()); } } if (args.canvasOnly()) { m_mainWindow->viewManager()->switchCanvasOnly(true); } if (args.fullScreen()) { m_mainWindow->showFullScreen(); } else { m_mainWindow->show(); } } } short int numberOfOpenDocuments = 0; // number of documents open // Check for autosave files that can be restored, if we're not running a batchrun (test, print, export to pdf) if (!m_batchRun) { checkAutosaveFiles(); } setSplashScreenLoadingText(QString()); // done loading, so clear out label processEvents(); //configure the unit manager KisSpinBoxUnitManagerFactory::setDefaultUnitManagerBuilder(new KisDocumentAwareSpinBoxUnitManagerBuilder()); connect(this, &KisApplication::aboutToQuit, &KisSpinBoxUnitManagerFactory::clearUnitManagerBuilder); //ensure the builder is destroyed when the application leave. //the new syntax slot syntax allow to connect to a non q_object static method. // Get the command line arguments which we have to parse int argsCount = args.filenames().count(); if (argsCount > 0) { // Loop through arguments short int nPrinted = 0; for (int argNumber = 0; argNumber < argsCount; argNumber++) { QString fileName = args.filenames().at(argNumber); // are we just trying to open a template? if (doTemplate) { // called in mix with batch options? ignore and silently skip if (m_batchRun) { continue; } if (createNewDocFromTemplate(fileName, m_mainWindow)) { ++numberOfOpenDocuments; } // now try to load } else { if (exportAs) { QString outputMimetype = KisMimeDatabase::mimeTypeForFile(exportFileName); if (outputMimetype == "application/octetstream") { dbgKrita << i18n("Mimetype not found, try using the -mimetype option") << endl; return 1; } KisDocument *doc = KisPart::instance()->createDocument(); doc->setFileBatchMode(m_batchRun); doc->openUrl(QUrl::fromLocalFile(fileName)); qApp->processEvents(); // For vector layers to be updated doc->setFileBatchMode(true); if (!doc->exportDocumentSync(QUrl::fromLocalFile(exportFileName), outputMimetype.toLatin1())) { dbgKrita << "Could not export " << fileName << "to" << exportFileName << ":" << doc->errorMessage(); } nPrinted++; QTimer::singleShot(0, this, SLOT(quit())); } else if (m_mainWindow) { KisMainWindow::OpenFlags flags = m_batchRun ? KisMainWindow::BatchMode : KisMainWindow::None; if (m_mainWindow->openDocument(QUrl::fromLocalFile(fileName), flags)) { if (print) { m_mainWindow->slotFilePrint(); nPrinted++; // TODO: trigger closing of app once printing is done } else if (exportAsPdf) { KisPrintJob *job = m_mainWindow->exportToPdf(exportFileName); if (job) connect (job, SIGNAL(destroyed(QObject*)), m_mainWindow, SLOT(slotFileQuit()), Qt::QueuedConnection); nPrinted++; } else { // Normal case, success numberOfOpenDocuments++; } } else { // .... if failed // delete doc; done by openDocument } } } } if (m_batchRun) { return nPrinted > 0; } } // fixes BUG:369308 - Krita crashing on splash screen when loading. // trying to open a file before Krita has loaded can cause it to hang and crash if (d->splashScreen) { d->splashScreen->displayLinks(); d->splashScreen->displayRecentFiles(); } // not calling this before since the program will quit there. return true; } KisApplication::~KisApplication() { delete d; } void KisApplication::setSplashScreen(QWidget *splashScreen) { d->splashScreen = qobject_cast(splashScreen); } void KisApplication::setSplashScreenLoadingText(QString textToLoad) { if (d->splashScreen) { d->splashScreen->loadingLabel->setText(textToLoad); d->splashScreen->repaint(); } } void KisApplication::hideSplashScreen() { if (d->splashScreen) { // hide the splashscreen to see the dialog d->splashScreen->hide(); } } bool KisApplication::notify(QObject *receiver, QEvent *event) { try { return QApplication::notify(receiver, event); } catch (std::exception &e) { qWarning("Error %s sending event %i to object %s", e.what(), event->type(), qPrintable(receiver->objectName())); } catch (...) { qWarning("Error sending event %i to object %s", event->type(), qPrintable(receiver->objectName())); } return false; } void KisApplication::remoteArguments(QByteArray message, QObject *socket) { Q_UNUSED(socket); // check if we have any mainwindow KisMainWindow *mw = qobject_cast(qApp->activeWindow()); if (!mw) { mw = KisPart::instance()->mainWindows().first(); } if (!mw) { return; } KisApplicationArguments args = KisApplicationArguments::deserialize(message); const bool doTemplate = args.doTemplate(); const int argsCount = args.filenames().count(); if (argsCount > 0) { // Loop through arguments for (int argNumber = 0; argNumber < argsCount; ++argNumber) { QString filename = args.filenames().at(argNumber); // are we just trying to open a template? if (doTemplate) { createNewDocFromTemplate(filename, mw); } else if (QFile(filename).exists()) { KisMainWindow::OpenFlags flags = m_batchRun ? KisMainWindow::BatchMode : KisMainWindow::None; mw->openDocument(QUrl::fromLocalFile(filename), flags); } } } } void KisApplication::fileOpenRequested(const QString &url) { KisMainWindow *mainWindow = KisPart::instance()->mainWindows().first(); if (mainWindow) { KisMainWindow::OpenFlags flags = m_batchRun ? KisMainWindow::BatchMode : KisMainWindow::None; mainWindow->openDocument(QUrl::fromLocalFile(url), flags); } } void KisApplication::checkAutosaveFiles() { if (m_batchRun) return; // Check for autosave files from a previous run. There can be several, and // we want to offer a restore for every one. Including a nice thumbnail! QStringList filters; filters << QString(".krita-*-*-autosave.kra"); #ifdef Q_OS_WIN QDir dir = QDir::temp(); #else QDir dir = QDir::home(); #endif // all autosave files for our application QStringList autosaveFiles = dir.entryList(filters, QDir::Files | QDir::Hidden); // Allow the user to make their selection if (autosaveFiles.size() > 0) { if (d->splashScreen) { // hide the splashscreen to see the dialog d->splashScreen->hide(); } m_autosaveDialog = new KisAutoSaveRecoveryDialog(autosaveFiles, activeWindow()); QDialog::DialogCode result = (QDialog::DialogCode) m_autosaveDialog->exec(); if (result == QDialog::Accepted) { QStringList filesToRecover = m_autosaveDialog->recoverableFiles(); Q_FOREACH (const QString &autosaveFile, autosaveFiles) { if (!filesToRecover.contains(autosaveFile)) { QFile::remove(dir.absolutePath() + "/" + autosaveFile); } } autosaveFiles = filesToRecover; } else { autosaveFiles.clear(); } if (autosaveFiles.size() > 0) { QList autosaveUrls; Q_FOREACH (const QString &autoSaveFile, autosaveFiles) { const QUrl url = QUrl::fromLocalFile(dir.absolutePath() + QLatin1Char('/') + autoSaveFile); autosaveUrls << url; } if (m_mainWindow) { Q_FOREACH (const QUrl &url, autosaveUrls) { KisMainWindow::OpenFlags flags = m_batchRun ? KisMainWindow::BatchMode : KisMainWindow::None; m_mainWindow->openDocument(url, flags | KisMainWindow::RecoveryFile); } } } // cleanup delete m_autosaveDialog; m_autosaveDialog = nullptr; } } bool KisApplication::createNewDocFromTemplate(const QString &fileName, KisMainWindow *mainWindow) { QString templatePath; const QUrl templateUrl = QUrl::fromLocalFile(fileName); if (QFile::exists(fileName)) { templatePath = templateUrl.toLocalFile(); dbgUI << "using full path..."; } else { QString desktopName(fileName); const QString templatesResourcePath = QStringLiteral("templates/"); QStringList paths = KoResourcePaths::findAllResources("data", templatesResourcePath + "*/" + desktopName); if (paths.isEmpty()) { paths = KoResourcePaths::findAllResources("data", templatesResourcePath + desktopName); } if (paths.isEmpty()) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("No template found for: %1", desktopName)); } else if (paths.count() > 1) { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Too many templates found for: %1", desktopName)); } else { templatePath = paths.at(0); } } if (!templatePath.isEmpty()) { QUrl templateBase; templateBase.setPath(templatePath); KDesktopFile templateInfo(templatePath); QString templateName = templateInfo.readUrl(); QUrl templateURL; templateURL.setPath(templateBase.adjusted(QUrl::RemoveFilename|QUrl::StripTrailingSlash).path() + '/' + templateName); KisMainWindow::OpenFlags batchFlags = m_batchRun ? KisMainWindow::BatchMode : KisMainWindow::None; if (mainWindow->openDocument(templateURL, KisMainWindow::Import | batchFlags)) { dbgUI << "Template loaded..."; return true; } else { QMessageBox::critical(0, i18nc("@title:window", "Krita"), i18n("Template %1 failed to load.", templateURL.toDisplayString())); } } return false; } void KisApplication::clearConfig() { KIS_ASSERT_RECOVER_RETURN(qApp->thread() == QThread::currentThread()); KSharedConfigPtr config = KSharedConfig::openConfig(); // find user settings file bool createDir = false; QString kritarcPath = KoResourcePaths::locateLocal("config", "kritarc", createDir); QFile configFile(kritarcPath); if (configFile.exists()) { // clear file if (configFile.open(QFile::WriteOnly)) { configFile.close(); } else { QMessageBox::warning(0, i18nc("@title:window", "Krita"), i18n("Failed to clear %1\n\n" "Please make sure no other program is using the file and try again.", kritarcPath), QMessageBox::Ok, QMessageBox::Ok); } } // reload from disk; with the user file settings cleared, // this should load any default configuration files shipping with the program config->reparseConfiguration(); config->sync(); } void KisApplication::askClearConfig() { Qt::KeyboardModifiers mods = QApplication::queryKeyboardModifiers(); bool askClearConfig = (mods & Qt::ControlModifier) && (mods & Qt::ShiftModifier) && (mods & Qt::AltModifier); if (askClearConfig) { bool ok = QMessageBox::question(0, i18nc("@title:window", "Krita"), i18n("Do you want to clear the settings file?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes; if (ok) { clearConfig(); } } } diff --git a/libs/widgetutils/KoResourcePaths.cpp b/libs/widgetutils/KoResourcePaths.cpp index a028c5a559..7a47c722a9 100644 --- a/libs/widgetutils/KoResourcePaths.cpp +++ b/libs/widgetutils/KoResourcePaths.cpp @@ -1,550 +1,562 @@ /* * Copyright (c) 2015 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "KoResourcePaths.h" #include #include #include #include #include #include #include #include #include #include #include "kis_debug.h" #include "WidgetUtilsDebug.h" Q_GLOBAL_STATIC(KoResourcePaths, s_instance); static QString cleanup(const QString &path) { return QDir::cleanPath(path); } static QStringList cleanup(const QStringList &pathList) { QStringList cleanedPathList; Q_FOREACH(const QString &path, pathList) { cleanedPathList << cleanup(path); } return cleanedPathList; } static QString cleanupDirs(const QString &path) { return QDir::cleanPath(path) + QDir::separator(); } static QStringList cleanupDirs(const QStringList &pathList) { QStringList cleanedPathList; Q_FOREACH(const QString &path, pathList) { cleanedPathList << cleanupDirs(path); } return cleanedPathList; } void appendResources(QStringList *dst, const QStringList &src, bool eliminateDuplicates) { Q_FOREACH (const QString &resource, src) { QString realPath = QDir::cleanPath(resource); if (!eliminateDuplicates || !dst->contains(realPath)) { *dst << realPath; } } } #ifdef Q_OS_WIN static const Qt::CaseSensitivity cs = Qt::CaseInsensitive; #else static const Qt::CaseSensitivity cs = Qt::CaseSensitive; #endif #ifdef Q_OS_OSX #include #include #include #endif QString getInstallationPrefix() { #ifdef Q_OS_OSX QString appPath = qApp->applicationDirPath(); debugWidgetUtils << "1" << appPath; appPath.chop(QString("MacOS/").length()); debugWidgetUtils << "2" << appPath; bool makeInstall = QDir(appPath + "/../../../share/kritaplugins").exists(); bool inBundle = QDir(appPath + "/Resources/kritaplugins").exists(); debugWidgetUtils << "3. After make install" << makeInstall; debugWidgetUtils << "4. In Bundle" << inBundle; QString bundlePath; if (inBundle) { bundlePath = appPath + "/"; } else if (makeInstall) { appPath.chop(QString("Contents/").length()); bundlePath = appPath + "/../../"; } else { qFatal("Cannot calculate the bundle path from the app path"); } debugWidgetUtils << ">>>>>>>>>>>" << bundlePath; return bundlePath; #else #ifdef Q_OS_QWIN QDir appdir(qApp->applicationDirPath()); // Corrects for mismatched case errors in path (qtdeclarative fails to load) wchar_t buffer[1024]; QString absolute = appdir.absolutePath(); DWORD rv = ::GetShortPathName((wchar_t*)absolute.utf16(), buffer, 1024); rv = ::GetLongPathName(buffer, buffer, 1024); QString correctedPath((QChar *)buffer); appdir.setPath(correctedPath); appdir.cdUp(); return appdir.canonicalPath(); #else return qApp->applicationDirPath() + "/../"; #endif #endif } class Q_DECL_HIDDEN KoResourcePaths::Private { public: QMap absolutes; // For each resource type, the list of absolute paths, from most local (most priority) to most global QMap relatives; // Same with relative paths QMutex relativesMutex; QMutex absolutesMutex; + bool ready = false; // Paths have been initialized + QStringList aliases(const QString &type) { QStringList r; QStringList a; relativesMutex.lock(); if (relatives.contains(type)) { r += relatives[type]; } relativesMutex.unlock(); debugWidgetUtils << "\trelatives" << r; absolutesMutex.lock(); if (absolutes.contains(type)) { a += absolutes[type]; } debugWidgetUtils << "\tabsolutes" << a; absolutesMutex.unlock(); return r + a; } QStandardPaths::StandardLocation mapTypeToQStandardPaths(const QString &type) { if (type == "tmp") { return QStandardPaths::TempLocation; } else if (type == "appdata") { return QStandardPaths::AppDataLocation; } else if (type == "data") { return QStandardPaths::AppDataLocation; } else if (type == "cache") { return QStandardPaths::CacheLocation; } else if (type == "locale") { return QStandardPaths::AppDataLocation; } else { return QStandardPaths::AppDataLocation; } } }; KoResourcePaths::KoResourcePaths() : d(new Private) { } KoResourcePaths::~KoResourcePaths() { } QString KoResourcePaths::getApplicationRoot() { return getInstallationPrefix(); } void KoResourcePaths::addResourceType(const char *type, const char *basetype, const QString &relativeName, bool priority) { s_instance->addResourceTypeInternal(QString::fromLatin1(type), QString::fromLatin1(basetype), relativeName, priority); } void KoResourcePaths::addResourceDir(const char *type, const QString &dir, bool priority) { s_instance->addResourceDirInternal(QString::fromLatin1(type), dir, priority); } QString KoResourcePaths::findResource(const char *type, const QString &fileName) { return cleanup(s_instance->findResourceInternal(QString::fromLatin1(type), fileName)); } QStringList KoResourcePaths::findDirs(const char *type) { return cleanupDirs(s_instance->findDirsInternal(QString::fromLatin1(type))); } QStringList KoResourcePaths::findAllResources(const char *type, const QString &filter, SearchOptions options) { return cleanup(s_instance->findAllResourcesInternal(QString::fromLatin1(type), filter, options)); } QStringList KoResourcePaths::resourceDirs(const char *type) { return cleanupDirs(s_instance->resourceDirsInternal(QString::fromLatin1(type))); } QString KoResourcePaths::saveLocation(const char *type, const QString &suffix, bool create) { return cleanupDirs(s_instance->saveLocationInternal(QString::fromLatin1(type), suffix, create)); } QString KoResourcePaths::locate(const char *type, const QString &filename) { return cleanup(s_instance->locateInternal(QString::fromLatin1(type), filename)); } QString KoResourcePaths::locateLocal(const char *type, const QString &filename, bool createDir) { return cleanup(s_instance->locateLocalInternal(QString::fromLatin1(type), filename, createDir)); } void KoResourcePaths::addResourceTypeInternal(const QString &type, const QString &basetype, const QString &relativename, bool priority) { Q_UNUSED(basetype); if (relativename.isEmpty()) return; QString copy = relativename; Q_ASSERT(basetype == "data"); if (!copy.endsWith(QLatin1Char('/'))) { copy += QLatin1Char('/'); } d->relativesMutex.lock(); QStringList &rels = d->relatives[type]; // find or insert if (!rels.contains(copy, cs)) { if (priority) { rels.prepend(copy); } else { rels.append(copy); } } d->relativesMutex.unlock(); debugWidgetUtils << "addResourceType: type" << type << "basetype" << basetype << "relativename" << relativename << "priority" << priority << d->relatives[type]; } void KoResourcePaths::addResourceDirInternal(const QString &type, const QString &absdir, bool priority) { if (absdir.isEmpty() || type.isEmpty()) return; // find or insert entry in the map QString copy = absdir; if (copy.at(copy.length() - 1) != QLatin1Char('/')) { copy += QLatin1Char('/'); } d->absolutesMutex.lock(); QStringList &paths = d->absolutes[type]; if (!paths.contains(copy, cs)) { if (priority) { paths.prepend(copy); } else { paths.append(copy); } } d->absolutesMutex.unlock(); debugWidgetUtils << "addResourceDir: type" << type << "absdir" << absdir << "priority" << priority << d->absolutes[type]; } QString KoResourcePaths::findResourceInternal(const QString &type, const QString &fileName) { QStringList aliases = d->aliases(type); debugWidgetUtils << "aliases" << aliases << getApplicationRoot(); QString resource = QStandardPaths::locate(QStandardPaths::AppDataLocation, fileName, QStandardPaths::LocateFile); if (resource.isEmpty()) { Q_FOREACH (const QString &alias, aliases) { resource = QStandardPaths::locate(d->mapTypeToQStandardPaths(type), alias + '/' + fileName, QStandardPaths::LocateFile); debugWidgetUtils << "\t1" << resource; if (QFile::exists(resource)) { continue; } } } if (resource.isEmpty() || !QFile::exists(resource)) { QString approot = getApplicationRoot(); Q_FOREACH (const QString &alias, aliases) { resource = approot + "/share/" + alias + '/' + fileName; debugWidgetUtils << "\t1" << resource; if (QFile::exists(resource)) { continue; } } } if (resource.isEmpty() || !QFile::exists(resource)) { QString approot = getApplicationRoot(); Q_FOREACH (const QString &alias, aliases) { resource = approot + "/share/krita/" + alias + '/' + fileName; debugWidgetUtils << "\t1" << resource; if (QFile::exists(resource)) { continue; } } } debugWidgetUtils << "findResource: type" << type << "filename" << fileName << "resource" << resource; Q_ASSERT(!resource.isEmpty()); return resource; } QStringList KoResourcePaths::findDirsInternal(const QString &type) { QStringList aliases = d->aliases(type); debugWidgetUtils << type << aliases << d->mapTypeToQStandardPaths(type); QStringList dirs; QStringList standardDirs = QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), "", QStandardPaths::LocateDirectory); appendResources(&dirs, standardDirs, true); Q_FOREACH (const QString &alias, aliases) { QStringList aliasDirs = QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), alias + '/', QStandardPaths::LocateDirectory); appendResources(&dirs, aliasDirs, true); #ifdef Q_OS_OSX debugWidgetUtils << "MAC:" << getApplicationRoot(); QStringList bundlePaths; bundlePaths << getApplicationRoot() + "/share/krita/" + alias; bundlePaths << getApplicationRoot() + "/../share/krita/" + alias; debugWidgetUtils << "bundlePaths" << bundlePaths; appendResources(&dirs, bundlePaths, true); Q_ASSERT(!dirs.isEmpty()); #endif QStringList fallbackPaths; fallbackPaths << getApplicationRoot() + "/share/" + alias; fallbackPaths << getApplicationRoot() + "/share/krita/" + alias; appendResources(&dirs, fallbackPaths, true); } debugWidgetUtils << "findDirs: type" << type << "resource" << dirs; return dirs; } QStringList filesInDir(const QString &startdir, const QString & filter, bool recursive) { debugWidgetUtils << "filesInDir: startdir" << startdir << "filter" << filter << "recursive" << recursive; QStringList result; // First the entries in this path QStringList nameFilters; nameFilters << filter; const QStringList fileNames = QDir(startdir).entryList(nameFilters, QDir::Files | QDir::CaseSensitive, QDir::Name); debugWidgetUtils << "\tFound:" << fileNames.size() << ":" << fileNames; Q_FOREACH (const QString &fileName, fileNames) { QString file = startdir + '/' + fileName; result << file; } // And then everything underneath, if recursive is specified if (recursive) { const QStringList entries = QDir(startdir).entryList(QDir::Dirs | QDir::NoDotAndDotDot); Q_FOREACH (const QString &subdir, entries) { debugWidgetUtils << "\tGoing to look in subdir" << subdir << "of" << startdir; result << filesInDir(startdir + '/' + subdir, filter, recursive); } } return result; } QStringList KoResourcePaths::findAllResourcesInternal(const QString &type, const QString &_filter, SearchOptions options) const { debugWidgetUtils << "====================================================="; debugWidgetUtils << type << _filter << QStandardPaths::standardLocations(d->mapTypeToQStandardPaths(type)); bool recursive = options & KoResourcePaths::Recursive; debugWidgetUtils << "findAllResources: type" << type << "filter" << _filter << "recursive" << recursive; QStringList aliases = d->aliases(type); QString filter = _filter; // In cases where the filter is like "color-schemes/*.colors" instead of "*.kpp", used with unregistered resource types if (filter.indexOf('*') > 0) { aliases << filter.split('*').first(); filter = '*' + filter.split('*')[1]; debugWidgetUtils << "Split up alias" << aliases << "filter" << filter; } QStringList resources; if (aliases.isEmpty()) { QStringList standardResources = QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), filter, QStandardPaths::LocateFile); appendResources(&resources, standardResources, true); } debugWidgetUtils << "\tresources from qstandardpaths:" << resources.size(); Q_FOREACH (const QString &alias, aliases) { debugWidgetUtils << "\t\talias:" << alias; QStringList dirs; QFileInfo dirInfo(alias); if (dirInfo.exists() && dirInfo.isDir() && dirInfo.isAbsolute()) { dirs << alias; } else { dirs << QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), alias, QStandardPaths::LocateDirectory) << getInstallationPrefix() + "share/" + alias + "/" << getInstallationPrefix() + "share/krita/" + alias + "/"; } Q_FOREACH (const QString &dir, dirs) { appendResources(&resources, filesInDir(dir, filter, recursive), true); } } debugWidgetUtils << "\tresources also from aliases:" << resources.size(); QFileInfo fi(filter); QStringList prefixResources; prefixResources << filesInDir(getInstallationPrefix() + "share/" + fi.path(), fi.fileName(), false); prefixResources << filesInDir(getInstallationPrefix() + "share/krita/" + fi.path(), fi.fileName(), false); appendResources(&resources, prefixResources, true); debugWidgetUtils << "\tresources from installation:" << resources.size(); debugWidgetUtils << "====================================================="; return resources; } QStringList KoResourcePaths::resourceDirsInternal(const QString &type) { QStringList resourceDirs; QStringList aliases = d->aliases(type); Q_FOREACH (const QString &alias, aliases) { QStringList aliasDirs; aliasDirs << QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), alias, QStandardPaths::LocateDirectory); aliasDirs << getInstallationPrefix() + "share/" + alias + "/" << QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), alias, QStandardPaths::LocateDirectory); aliasDirs << getInstallationPrefix() + "share/krita/" + alias + "/" << QStandardPaths::locateAll(d->mapTypeToQStandardPaths(type), alias, QStandardPaths::LocateDirectory); appendResources(&resourceDirs, aliasDirs, true); } debugWidgetUtils << "resourceDirs: type" << type << resourceDirs; return resourceDirs; } QString KoResourcePaths::saveLocationInternal(const QString &type, const QString &suffix, bool create) { QStringList aliases = d->aliases(type); QString path; if (aliases.size() > 0) { path = QStandardPaths::writableLocation(d->mapTypeToQStandardPaths(type)) + '/' + aliases.first(); } else { path = QStandardPaths::writableLocation(d->mapTypeToQStandardPaths(type)); if (!path.endsWith("krita")) { path += "/krita"; } if (!suffix.isEmpty()) { path += "/" + suffix; } } QDir d(path); if (!d.exists() && create) { d.mkpath(path); } debugWidgetUtils << "saveLocation: type" << type << "suffix" << suffix << "create" << create << "path" << path; return path; } QString KoResourcePaths::locateInternal(const QString &type, const QString &filename) { QStringList aliases = d->aliases(type); QStringList locations; if (aliases.isEmpty()) { locations << QStandardPaths::locate(d->mapTypeToQStandardPaths(type), filename, QStandardPaths::LocateFile); } Q_FOREACH (const QString &alias, aliases) { locations << QStandardPaths::locate(d->mapTypeToQStandardPaths(type), (alias.endsWith('/') ? alias : alias + '/') + filename, QStandardPaths::LocateFile); } debugWidgetUtils << "locate: type" << type << "filename" << filename << "locations" << locations; if (locations.size() > 0) { return locations.first(); } else { return ""; } } QString KoResourcePaths::locateLocalInternal(const QString &type, const QString &filename, bool createDir) { QString path = saveLocationInternal(type, "", createDir); debugWidgetUtils << "locateLocal: type" << type << "filename" << filename << "CreateDir" << createDir << "path" << path; return path + '/' + filename; } + +void KoResourcePaths::setReady() +{ + s_instance->d->ready = true; +} + +void KoResourcePaths::assertReady() +{ + KIS_ASSERT_X(s_instance->d->ready, "KoResourcePaths::assertReady", "Resource paths are not ready yet."); +} diff --git a/libs/widgetutils/KoResourcePaths.h b/libs/widgetutils/KoResourcePaths.h index 5dd831396a..0d9c5bb787 100644 --- a/libs/widgetutils/KoResourcePaths.h +++ b/libs/widgetutils/KoResourcePaths.h @@ -1,251 +1,262 @@ /* * Copyright (c) 2015 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef KORESOURCEPATHS_H #define KORESOURCEPATHS_H #include #include #include #include /** * DEBUGGING KoResourcePaths: * * The usual place to look for resources is Qt's AppDataLocation. * This corresponds to XDG_DATA_DIRS on Linux. To ensure your installation and * path are configured correctly, ensure your files are located in the directories * contained in this variable: * * QStandardPaths::standardLocations(QStandardPaths::AppDataLocation); * * There are many debug lines that can be uncommented for more specific installation * checks. In the future these should be converted to qloggingcategory to enable * convenient enable/disable functionality. */ class KRITAWIDGETUTILS_EXPORT KoResourcePaths { public: KoResourcePaths(); virtual ~KoResourcePaths(); enum SearchOption { NoSearchOptions = 0, Recursive = 1, IgnoreExecBit = 4 }; Q_DECLARE_FLAGS(SearchOptions, SearchOption) static QString getApplicationRoot(); /** * Adds suffixes for types. * * You may add as many as you need, but it is advised that there * is exactly one to make writing definite. * * The later a suffix is added, the higher its priority. Note, that the * suffix should end with / but doesn't have to start with one (as prefixes * should end with one). So adding a suffix for app_pics would look * like KoStandardPaths::addResourceType("app_pics", "data", "app/pics"); * * @param type Specifies a short descriptive string to access * files of this type. * @param basetype Specifies an already known type, or 0 if none * @param relativename Specifies a directory relative to the basetype * @param priority if true, the directory is added before any other, * otherwise after */ static void addResourceType(const char *type, const char *basetype, const QString &relativeName, bool priority = true); /** * Adds absolute path at the beginning of the search path for * particular types (for example in case of icons where * the user specifies extra paths). * * You shouldn't need this function in 99% of all cases besides * adding user-given paths. * * @param type Specifies a short descriptive string to access files * of this type. * @param absdir Points to directory where to look for this specific * type. Non-existent directories may be saved but pruned. * @param priority if true, the directory is added before any other, * otherwise after */ static void addResourceDir(const char *type, const QString &dir, bool priority = true); /** * Tries to find a resource in the following order: * @li All PREFIX/\ paths (most recent first). * @li All absolute paths (most recent first). * * The filename should be a filename relative to the base dir * for resources. So is a way to get the path to libkdecore.la * to findResource("lib", "libkdecore.la"). KStandardDirs will * then look into the subdir lib of all elements of all prefixes * ($KDEDIRS) for a file libkdecore.la and return the path to * the first one it finds (e.g. /opt/kde/lib/libkdecore.la). * * Example: * @code * QString iconfilename = KStandardPaths::findResource("icon",QString("oxygen/22x22/apps/ktip.png")); * @endcode * * @param type The type of the wanted resource * @param filename A relative filename of the resource. * * @return A full path to the filename specified in the second * argument, or QString() if not found. */ static QString findResource(const char *type, const QString &fileName); /** * Tries to find all directories whose names consist of the * specified type and a relative path. So * findDirs("xdgdata-apps", "Settings") would return * @li /home/joe/.local/share/applications/Settings/ * @li /usr/share/applications/Settings/ * * (from the most local to the most global) * * Note that it appends @c / to the end of the directories, * so you can use this right away as directory names. * * @param type The type of the base directory. * @param reldir Relative directory. * * @return A list of matching directories, or an empty * list if the resource specified is not found. */ static QStringList findDirs(const char *type); /** * Tries to find all resources with the specified type. * * The function will look into all specified directories * and return all filenames in these directories. * * The "most local" files are returned before the "more global" files. * * @param type The type of resource to locate directories for. * @param filter Only accept filenames that fit to filter. The filter * may consist of an optional directory and a QRegExp * wildcard expression. E.g. "images\*.jpg". * Use QString() if you do not want a filter. * @param options if the flags passed include Recursive, subdirectories * will also be search. * * @return List of all the files whose filename matches the * specified filter. */ static QStringList findAllResources(const char *type, const QString &filter = QString(), SearchOptions options = NoSearchOptions); /** * @param type The type of resource * @return The list of possible directories for the specified @p type. * The function updates the cache if possible. If the resource * type specified is unknown, it will return an empty list. * Note, that the directories are assured to exist beside the save * location, which may not exist, but is returned anyway. */ static QStringList resourceDirs(const char *type); /** * Finds a location to save files into for the given type * in the user's home directory. * * @param type The type of location to return. * @param suffix A subdirectory name. * Makes it easier for you to create subdirectories. * You can't pass filenames here, you _have_ to pass * directory names only and add possible filename in * that directory yourself. A directory name always has a * trailing slash ('/'). * @param create If set, saveLocation() will create the directories * needed (including those given by @p suffix). * * @return A path where resources of the specified type should be * saved, or QString() if the resource type is unknown. */ static QString saveLocation(const char *type, const QString &suffix = QString(), bool create = true); /** * This function is just for convenience. It simply calls * KoResourcePaths::findResource((type, filename). * * @param type The type of the wanted resource, see KStandardDirs * @param filename A relative filename of the resource * * @return A full path to the filename specified in the second * argument, or QString() if not found **/ static QString locate(const char *type, const QString &filename); /** * This function is much like locate. However it returns a * filename suitable for writing to. No check is made if the * specified @p filename actually exists. Missing directories * are created. If @p filename is only a directory, without a * specific file, @p filename must have a trailing slash. * * @param type The type of the wanted resource, see KStandardDirs * @param filename A relative filename of the resource * * @return A full path to the filename specified in the second * argument, or QString() if not found **/ static QString locateLocal(const char *type, const QString &filename, bool createDir = false); + /** + * Indicate that resource paths have been initialized and users + * of this class may expect to load resources from the proper paths. + */ + static void setReady(); + + /** + * Assert that all resource paths have been initialized. + */ + static void assertReady(); + private: void addResourceTypeInternal(const QString &type, const QString &basetype, const QString &relativeName, bool priority); void addResourceDirInternal(const QString &type, const QString &absdir, bool priority); QString findResourceInternal(const QString &type, const QString &fileName); QStringList findDirsInternal(const QString &type); QStringList findAllResourcesInternal(const QString &type, const QString &filter = QString(), SearchOptions options = NoSearchOptions) const; QStringList resourceDirsInternal(const QString &type); QString saveLocationInternal(const QString &type, const QString &suffix = QString(), bool create = true); QString locateInternal(const QString &type, const QString &filename); QString locateLocalInternal(const QString &type, const QString &filename, bool createDir = false); class Private; QScopedPointer d; }; Q_DECLARE_OPERATORS_FOR_FLAGS(KoResourcePaths::SearchOptions) #endif // KORESOURCEPATHS_H diff --git a/plugins/color/lcms2engine/LcmsEnginePlugin.cpp b/plugins/color/lcms2engine/LcmsEnginePlugin.cpp index 7ac0bbf780..c3d5364939 100644 --- a/plugins/color/lcms2engine/LcmsEnginePlugin.cpp +++ b/plugins/color/lcms2engine/LcmsEnginePlugin.cpp @@ -1,291 +1,297 @@ /* * Copyright (c) 2003 Patrick Julien * Copyright (c) 2004,2010 Cyrille Berger * Copyright (c) 2011 Srikanth Tiyyagura * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * 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. */ #include "LcmsEnginePlugin.h" #include #include #include #include #include #include #include +#include "kis_assert.h" + #include #include #include #include #include "IccColorSpaceEngine.h" #include "colorprofiles/LcmsColorProfileContainer.h" #include "colorspaces/cmyk_u8/CmykU8ColorSpace.h" #include "colorspaces/cmyk_u16/CmykU16ColorSpace.h" #include "colorspaces/cmyk_f32/CmykF32ColorSpace.h" #include "colorspaces/gray_u8/GrayU8ColorSpace.h" #include "colorspaces/gray_u16/GrayU16ColorSpace.h" #include "colorspaces/gray_f32/GrayF32ColorSpace.h" #include "colorspaces/lab_u8/LabU8ColorSpace.h" #include "colorspaces/lab_u16/LabColorSpace.h" #include "colorspaces/lab_f32/LabF32ColorSpace.h" #include "colorspaces/xyz_u8/XyzU8ColorSpace.h" #include "colorspaces/xyz_u16/XyzU16ColorSpace.h" #include "colorspaces/xyz_f32/XyzF32ColorSpace.h" #include "colorspaces/rgb_u8/RgbU8ColorSpace.h" #include "colorspaces/rgb_u16/RgbU16ColorSpace.h" #include "colorspaces/rgb_f32/RgbF32ColorSpace.h" #include "colorspaces/ycbcr_u8/YCbCrU8ColorSpace.h" #include "colorspaces/ycbcr_u16/YCbCrU16ColorSpace.h" #include "colorspaces/ycbcr_f32/YCbCrF32ColorSpace.h" #include #ifdef HAVE_OPENEXR #include #ifdef HAVE_LCMS24 #include "colorspaces/gray_f16/GrayF16ColorSpace.h" #include "colorspaces/xyz_f16/XyzF16ColorSpace.h" #include "colorspaces/rgb_f16/RgbF16ColorSpace.h" #endif #endif void lcms2LogErrorHandlerFunction(cmsContext /*ContextID*/, cmsUInt32Number ErrorCode, const char *Text) { qCritical() << "Lcms2 error: " << ErrorCode << Text; } K_PLUGIN_FACTORY_WITH_JSON(PluginFactory, "kolcmsengine.json", registerPlugin();) LcmsEnginePlugin::LcmsEnginePlugin(QObject *parent, const QVariantList &) : QObject(parent) { + // We need all resource paths to be properly initialized via KisApplication, otherwise we will + // initialize this instance with lacking color profiles which will cause lookup errors later on. + KoResourcePaths::assertReady(); + // Set the lmcs error reporting function cmsSetLogErrorHandler(&lcms2LogErrorHandlerFunction); KoColorSpaceRegistry *registry = KoColorSpaceRegistry::instance(); // Initialise color engine KoColorSpaceEngineRegistry::instance()->add(new IccColorSpaceEngine); QStringList profileFilenames; profileFilenames += KoResourcePaths::findAllResources("icc_profiles", "*.icm", KoResourcePaths::Recursive); profileFilenames += KoResourcePaths::findAllResources("icc_profiles", "*.ICM", KoResourcePaths::Recursive); profileFilenames += KoResourcePaths::findAllResources("icc_profiles", "*.ICC", KoResourcePaths::Recursive); profileFilenames += KoResourcePaths::findAllResources("icc_profiles", "*.icc", KoResourcePaths::Recursive); // Load the profiles if (!profileFilenames.empty()) { KoColorProfile *profile = 0; for (QStringList::Iterator it = profileFilenames.begin(); it != profileFilenames.end(); ++it) { profile = new IccColorProfile(*it); Q_CHECK_PTR(profile); profile->load(); if (profile->valid()) { //qDebug() << "Valid profile : " << profile->fileName() << profile->name(); registry->addProfileToMap(profile); } else { qDebug() << "Invalid profile : " << profile->fileName() << profile->name(); delete profile; } } } // ------------------- LAB --------------------------------- KoColorProfile *labProfile = LcmsColorProfileContainer::createFromLcmsProfile(cmsCreateLab2Profile(0)); registry->addProfile(labProfile); registry->add(new LabU8ColorSpaceFactory()); registry->add(new LabU16ColorSpaceFactory()); registry->add(new LabF32ColorSpaceFactory()); KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory (KoID("LABAU8HISTO", i18n("L*a*b* Histogram")), LABAColorModelID.id(), Integer8BitsColorDepthID.id())); KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory (KoID("LABAU16HISTO", i18n("L*a*b* Histogram")), LABAColorModelID.id(), Integer16BitsColorDepthID.id())); KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory (KoID("LABAF32HISTO", i18n("L*a*b* Histogram")), LABAColorModelID.id(), Float32BitsColorDepthID.id())); // ------------------- RGB --------------------------------- KoColorProfile *rgbProfile = LcmsColorProfileContainer::createFromLcmsProfile(cmsCreate_sRGBProfile()); registry->addProfile(rgbProfile); registry->add(new RgbU8ColorSpaceFactory()); registry->add(new RgbU16ColorSpaceFactory()); #ifdef HAVE_LCMS24 #ifdef HAVE_OPENEXR registry->add(new RgbF16ColorSpaceFactory()); #endif #endif registry->add(new RgbF32ColorSpaceFactory()); KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory (KoID("RGBU8HISTO", i18n("RGB8 Histogram")), RGBAColorModelID.id(), Integer8BitsColorDepthID.id())); KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory (KoID("RGBU16HISTO", i18n("RGB16 Histogram")), RGBAColorModelID.id(), Integer16BitsColorDepthID.id())); #ifdef HAVE_LCMS24 #ifdef HAVE_OPENEXR KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory (KoID("RGBF16HISTO", i18n("RGBF16 Histogram")), RGBAColorModelID.id(), Float16BitsColorDepthID.id())); #endif #endif KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory (KoID("RGF328HISTO", i18n("RGBF32 Histogram")), RGBAColorModelID.id(), Float32BitsColorDepthID.id())); // ------------------- GRAY --------------------------------- cmsToneCurve *Gamma = cmsBuildGamma(0, 2.2); cmsHPROFILE hProfile = cmsCreateGrayProfile(cmsD50_xyY(), Gamma); cmsFreeToneCurve(Gamma); KoColorProfile *defProfile = LcmsColorProfileContainer::createFromLcmsProfile(hProfile); registry->addProfile(defProfile); registry->add(new GrayAU8ColorSpaceFactory()); registry->add(new GrayAU16ColorSpaceFactory()); #ifdef HAVE_LCMS24 #ifdef HAVE_OPENEXR registry->add(new GrayF16ColorSpaceFactory()); #endif #endif registry->add(new GrayF32ColorSpaceFactory()); KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory (KoID("GRAYA8HISTO", i18n("GRAY/Alpha8 Histogram")), GrayAColorModelID.id(), Integer8BitsColorDepthID.id())); KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory (KoID("GRAYA16HISTO", i18n("GRAY/Alpha16 Histogram")), GrayAColorModelID.id(), Integer16BitsColorDepthID.id())); #ifdef HAVE_LCMS24 #ifdef HAVE_OPENEXR KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory (KoID("GRAYF16HISTO", i18n("GRAYF16 Histogram")), GrayAColorModelID.id(), Float16BitsColorDepthID.id())); #endif #endif KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory (KoID("GRAYAF32HISTO", i18n("GRAY/Alpha 32 float Histogram")), GrayAColorModelID.id(), Float32BitsColorDepthID.id())); // ------------------- CMYK --------------------------------- registry->add(new CmykU8ColorSpaceFactory()); registry->add(new CmykU16ColorSpaceFactory()); registry->add(new CmykF32ColorSpaceFactory()); KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory (KoID("CMYK8HISTO", i18n("CMYK8 Histogram")), CMYKAColorModelID.id(), Integer8BitsColorDepthID.id())); KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory (KoID("CMYK16HISTO", i18n("CMYK16 Histogram")), CMYKAColorModelID.id(), Integer16BitsColorDepthID.id())); KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory (KoID("CMYKF32HISTO", i18n("CMYK F32 Histogram")), CMYKAColorModelID.id(), Float32BitsColorDepthID.id())); // ------------------- XYZ --------------------------------- KoColorProfile *xyzProfile = LcmsColorProfileContainer::createFromLcmsProfile(cmsCreateXYZProfile()); registry->addProfile(xyzProfile); registry->add(new XyzU8ColorSpaceFactory()); registry->add(new XyzU16ColorSpaceFactory()); #ifdef HAVE_LCMS24 #ifdef HAVE_OPENEXR registry->add(new XyzF16ColorSpaceFactory()); #endif #endif registry->add(new XyzF32ColorSpaceFactory()); KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory (KoID("XYZ8HISTO", i18n("XYZ8 Histogram")), XYZAColorModelID.id(), Integer8BitsColorDepthID.id())); KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory (KoID("XYZ16HISTO", i18n("XYZ16 Histogram")), XYZAColorModelID.id(), Integer16BitsColorDepthID.id())); #ifdef HAVE_LCMS24 #ifdef HAVE_OPENEXR KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory (KoID("XYZF16HISTO", i18n("XYZF16 Histogram")), XYZAColorModelID.id(), Float16BitsColorDepthID.id())); #endif #endif KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory (KoID("XYZF32HISTO", i18n("XYZF32 Histogram")), XYZAColorModelID.id(), Float32BitsColorDepthID.id())); // ------------------- YCBCR --------------------------------- // KoColorProfile *yCbCrProfile = LcmsColorProfileContainer::createFromLcmsProfile(cmsCreateYCBCRProfile()); // registry->addProfile(yCbCrProfile); registry->add(new YCbCrU8ColorSpaceFactory()); registry->add(new YCbCrU16ColorSpaceFactory()); registry->add(new YCbCrF32ColorSpaceFactory()); KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory (KoID("YCBCR8HISTO", i18n("YCBCR8 Histogram")), YCbCrAColorModelID.id(), Integer8BitsColorDepthID.id())); KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory (KoID("YCBCR16HISTO", i18n("YCBCR16 Histogram")), YCbCrAColorModelID.id(), Integer16BitsColorDepthID.id())); KoHistogramProducerFactoryRegistry::instance()->add( new KoBasicHistogramProducerFactory (KoID("YCBCRF32HISTO", i18n("YCBCRF32 Histogram")), YCbCrAColorModelID.id(), Float32BitsColorDepthID.id())); // Add profile alias for default profile from lcms1 registry->addProfileAlias("sRGB built-in - (lcms internal)", "sRGB built-in"); registry->addProfileAlias("gray built-in - (lcms internal)", "gray built-in"); registry->addProfileAlias("Lab identity built-in - (lcms internal)", "Lab identity built-in"); registry->addProfileAlias("XYZ built-in - (lcms internal)", "XYZ identity built-in"); } #include