File indexing completed on 2024-05-12 15:59:31

0001 /*
0002  *  SPDX-FileCopyrightText: 2005 Boudewijn Rempt <boud@valdyas.org>
0003  *  SPDX-FileCopyrightText: 2007 Thomas Zander <zander@kde.org>
0004  *
0005  * SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 #ifndef KOCOLOR_H
0008 #define KOCOLOR_H
0009 
0010 #include <QColor>
0011 #include <QMetaType>
0012 #include <QtGlobal>
0013 #include "kritapigment_export.h"
0014 #include "KoColorConversionTransformation.h"
0015 #include "KoColorSpaceRegistry.h"
0016 #include "KoColorSpaceTraits.h"
0017 #include <boost/operators.hpp>
0018 
0019 
0020 class QDomDocument;
0021 class QDomElement;
0022 
0023 class KoColorProfile;
0024 class KoColorSpace;
0025 
0026 /**
0027  * A KoColor describes a color in a certain colorspace. The color is stored in a buffer
0028  * that can be manipulated by the function of the color space.
0029  */
0030 class KRITAPIGMENT_EXPORT KoColor : public boost::equality_comparable<KoColor>
0031 {
0032 
0033 public:
0034     /// Create an empty KoColor. It will be valid, but also black and transparent
0035     KoColor();
0036 
0037     /// Create a null KoColor. It will be valid, but all channels will be set to 0
0038     explicit KoColor(const KoColorSpace * colorSpace);
0039 
0040     /// Create a KoColor from a QColor. The QColor is immediately converted to native. The QColor
0041     /// is assumed to have the current monitor profile.
0042     KoColor(const QColor & color, const KoColorSpace * colorSpace);
0043 
0044     /// Create a KoColor using a native color strategy. The data is copied.
0045     KoColor(const quint8 * data, const KoColorSpace * colorSpace);
0046 
0047     /// Create a KoColor by converting src into another colorspace
0048     KoColor(const KoColor &src, const KoColorSpace * colorSpace);
0049 
0050     /// Copy constructor -- deep copies the colors.
0051     KoColor(const KoColor &rhs)
0052         : m_colorSpace(rhs.m_colorSpace)
0053         , m_size(rhs.m_size)
0054         , m_metadata(rhs.m_metadata)
0055     {
0056         memcpy(m_data, rhs.m_data, m_size);
0057     }
0058 
0059     /**
0060      * assignment operator to copy the data from the param color into this one.
0061      * @param rhs the color we are going to copy
0062      * @return this color
0063      */
0064     inline KoColor &operator=(const KoColor &rhs) {
0065         if (&rhs == this) {
0066             return *this;
0067         }
0068 
0069         m_colorSpace = rhs.m_colorSpace;
0070         m_metadata = rhs.m_metadata;
0071         m_size = rhs.m_size;
0072         memcpy(m_data, rhs.m_data, m_size);
0073 
0074         assertPermanentColorspace();
0075 
0076         return *this;
0077     }
0078 
0079     bool operator==(const KoColor &other) const;
0080 
0081     /// return the current colorSpace
0082     const KoColorSpace * colorSpace() const {
0083         return m_colorSpace;
0084     }
0085 
0086     /// return the current profile
0087     const KoColorProfile *profile() const;
0088 
0089     /// Convert this KoColor to the specified colorspace. If the specified colorspace is the
0090     /// same as the original colorspace, do nothing
0091     void convertTo(const KoColorSpace * cs,
0092                    KoColorConversionTransformation::Intent renderingIntent,
0093                    KoColorConversionTransformation::ConversionFlags conversionFlags);
0094 
0095     void convertTo(const KoColorSpace * cs);
0096 
0097     /// Copies this color and converts it to the specified colorspace. If the specified colorspace is the
0098     /// same as the original colorspace, just returns a copy
0099     KoColor convertedTo(const KoColorSpace * cs,
0100                         KoColorConversionTransformation::Intent renderingIntent,
0101                         KoColorConversionTransformation::ConversionFlags conversionFlags) const;
0102 
0103     /// Copies this color and converts it to the specified colorspace. If the specified colorspace is the
0104     /// same as the original colorspace, just returns a copy
0105     KoColor convertedTo(const KoColorSpace * cs) const;
0106 
0107 
0108 
0109     /// assign new profile without converting pixel data
0110     void setProfile(const KoColorProfile *profile);
0111 
0112     /// Replace the existing color data, and colorspace with the specified data.
0113     /// The data is copied.
0114     void setColor(const quint8 * data, const KoColorSpace * colorSpace = 0);
0115 
0116     /// Convert the color from src and replace the value of the current color with the converted data.
0117     /// Don't convert the color if src and this have the same colorspace.
0118     void fromKoColor(const KoColor& src);
0119 
0120     /// a convenience method for the above.
0121     void toQColor(QColor *c) const;
0122     /// a convenience method for the above.
0123     QColor toQColor() const;
0124 
0125     /**
0126      * Convenient function to set the opacity of the color.
0127      */
0128     void setOpacity(quint8 alpha);
0129     void setOpacity(qreal alpha);
0130     /**
0131      * Convenient function that return the opacity of the color
0132      */
0133     quint8 opacityU8() const;
0134     qreal opacityF() const;
0135 
0136     /// Convenient function for converting from a QColor
0137     void fromQColor(const QColor& c);
0138 
0139     /**
0140      * @return the buffer associated with this color object to be used with the
0141      *         transformation object created by the color space of this KoColor
0142      *         or to copy to a different buffer from the same color space
0143      */
0144     quint8 * data() {
0145         return m_data;
0146     }
0147 
0148     /**
0149      * @return the buffer associated with this color object to be used with the
0150      *         transformation object created by the color space of this KoColor
0151      *         or to copy to a different buffer from the same color space
0152      */
0153     const quint8 * data() const {
0154         return m_data;
0155     }
0156 
0157 
0158     /**
0159      * Channelwise subtracts \p value from *this and stores the result in *this
0160      *
0161      * Throws a safe assert if the colorspaces of the two colors are different
0162      */
0163     void subtract(const KoColor &value);
0164 
0165     /**
0166      * Channelwise subtracts \p value from a copy of *this and returns the result
0167      *
0168      * Throws a safe assert if the colorspaces of the two colors are different
0169      */
0170     KoColor subtracted(const KoColor &value) const;
0171 
0172     /**
0173      * Channelwise adds \p value to *this and stores the result in *this
0174      *
0175      * Throws a safe assert if the colorspaces of the two colors are different
0176      */
0177     void add(const KoColor &value);
0178 
0179     /**
0180      * Channelwise adds \p value to a copy of *this and returns the result
0181      *
0182      * Throws a safe assert if the colorspaces of the two colors are different
0183      */
0184     KoColor added(const KoColor &value) const;
0185 
0186     /**
0187      * Serialize this color following Create's swatch color specification available
0188      * at https://web.archive.org/web/20110826002520/http://create.freedesktop.org/wiki/Swatches_-_color_file_format/Draft
0189      *
0190      * This function doesn't create the \<color /\> element but rather the \<CMYK /\>,
0191      * \<sRGB /\>, \<RGB /\> ... elements. It is assumed that colorElt is the \<color /\>
0192      * element.
0193      *
0194      * @param colorElt root element for the serialization, it is assumed that this
0195      *                 element is \<color /\>
0196      * @param doc is the document containing colorElt
0197      */
0198     void toXML(QDomDocument& doc, QDomElement& colorElt) const;
0199 
0200     /**
0201      * Unserialize a color following Create's swatch color specification available
0202      * at https://web.archive.org/web/20110826002520/http://create.freedesktop.org/wiki/Swatches_-_color_file_format/Draft
0203      *
0204      * @param elt the element to unserialize (\<CMYK /\>, \<sRGB /\>, \<RGB /\>)
0205      * @param channelDepthId the bit depth is unspecified by the spec, this allow to select
0206      *                   a preferred bit depth for creating the KoColor object (if that
0207      *                   bit depth isn't available, this function will randomly select
0208      *                   an other bit depth)
0209      * @return the unserialize color, or an empty color object if the function failed
0210      *         to unserialize the color
0211      */
0212     static KoColor fromXML(const QDomElement& elt, const QString & channelDepthId);
0213 
0214     /**
0215      * Unserialize a color following Create's swatch color specification available
0216      * at https://web.archive.org/web/20110826002520/http://create.freedesktop.org/wiki/Swatches_-_color_file_format/Draft
0217      *
0218      * @param elt the element to unserialize (\<CMYK /\>, \<sRGB /\>, \<RGB /\>)
0219      * @param channelDepthId the bit depth is unspecified by the spec, this allow to select
0220      *                   a preferred bit depth for creating the KoColor object (if that
0221      *                   bit depth isn't available, this function will randomly select
0222      *                   an other bit depth)
0223      * @param ok If a an error occurs, *ok is set to false; otherwise it's set to true
0224      * @return the unserialize color, or an empty color object if the function failed
0225      *         to unserialize the color
0226      */
0227     static KoColor fromXML(const QDomElement& elt, const QString & channelDepthId, bool* ok);
0228 
0229 
0230     /**
0231      * @brief toXML creates a string with XML that represents the current color. The XML
0232      * is extended with a "channeldepth" attribute so we can restore the color to the same
0233      * channel depth.
0234      * @return a valid XML document in a string
0235      */
0236     QString toXML() const;
0237 
0238     /**
0239      * @brief fromXML restores a KoColor from a string saved with toXML(). If the
0240      * string does not contain the "channeldepth" attribute, 16 bit integer is assumed.
0241      * @param xml a valid XML document
0242      * @return a new KoColor object
0243      */
0244     static KoColor fromXML(const QString &xml);
0245 
0246     /**
0247      * @brief toSVG11
0248      * @param profileList list of profiles, this will map the profile to a name, so it may be embedded.
0249      * @return a color definition string with both a srgb hexcode fallback as well as a icc-color definition.
0250      */
0251     QString toSVG11(QHash<QString, const KoColorProfile *> *profileList) const;
0252 
0253     /**
0254      * @brief fromSVG11
0255      * Parses a color attribute value and returns a KoColor. SVG defines the colorprofiles elsewhere
0256      * in the file, so this function expects you to first figure out the profiles and which colorspaces
0257      * these match to, and it will then use those colorspaces to generate the kocolor. If it cannot find
0258      * the appropriate colorspace, it will return the color fallback. If that doesn't work, an empty KoColor.
0259      * This function ignores url() values. Colors will be F32 unless they are lab and cmyk, in which case they'll
0260      * be U16.
0261      *
0262      * https://www.w3.org/TR/SVG11/types.html#DataTypeColor for hex, rgb() and colornames
0263      * https://www.w3.org/TR/SVG11/types.html#DataTypeICCColor for icc-color()
0264      * https://www.w3.org/TR/SVG11/painting.html#SpecifyingPaint
0265      *
0266      * @param value the content of the svg color value
0267      * @param profileList list of KoColorProfiles that were found inside the svg file, with the reference names.
0268      * @param current the current color.
0269      * @return a KoColor as parsed from the value string.
0270      */
0271     static KoColor fromSVG11(const QString value, QHash<QString, const KoColorProfile*> profileList, KoColor current = KoColor());
0272 
0273     /**
0274      * @brief toQString create a user-visible string of the channel names and the channel values
0275      * @param color the color to create the string from
0276      * @return a string that can be used to display the values of this color to the user.
0277      */
0278     static QString toQString(const KoColor &color);
0279 
0280     /**
0281      * store the given key, value pair in a KoColor.
0282      */
0283     void addMetadata(QString key, QVariant value);
0284 
0285     /**
0286      * Get a map with all the metadata
0287      * The metadata is used by parsing functions to store extra data,
0288      * like for example spot color names, or the original parsing model.
0289      */
0290     QMap<QString, QVariant> metadata() const;
0291 
0292     /**
0293      * @brief clearMetadata
0294      * clear th metadata map inside the KoColor.
0295      */
0296     void clearMetadata();
0297 
0298 #ifndef NODEBUG
0299     /// use qDebug calls to print internal info
0300     void dump() const;
0301 #endif
0302 
0303 private:
0304     inline void assertPermanentColorspace() {
0305 #ifndef NODEBUG
0306         if (m_colorSpace) {
0307             Q_ASSERT(*m_colorSpace == *KoColorSpaceRegistry::instance()->permanentColorspace(m_colorSpace));
0308         }
0309 #endif
0310     }
0311 
0312     const KoColorSpace *m_colorSpace;
0313     quint8 m_data[MAX_PIXEL_SIZE];
0314     quint8 m_size;
0315     QMap<QString, QVariant> m_metadata;
0316 };
0317 
0318 Q_DECLARE_METATYPE(KoColor)
0319 
0320 KRITAPIGMENT_EXPORT QDebug operator<<(QDebug dbg, const KoColor &color);
0321 
0322 
0323 #endif