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

0001 /*
0002  *  SPDX-FileCopyrightText: 2005 Boudewijn Rempt <boud@valdyas.org>
0003  *  SPDX-FileCopyrightText: 2006-2007 Cyrille Berger <cberger@cberger.net>
0004  *  SPDX-FileCopyrightText: 2021 L. E. Segovia <amy@amyspark.me>
0005  *
0006  * SPDX-License-Identifier: LGPL-2.1-or-later
0007  */
0008 #ifndef KOCOLORSPACE_H
0009 #define KOCOLORSPACE_H
0010 
0011 #include <climits>
0012 
0013 #include <QImage>
0014 #include <QHash>
0015 #include <QVector>
0016 #include <QList>
0017 
0018 #include <boost/operators.hpp>
0019 #include "KoColorSpaceConstants.h"
0020 #include "KoColorConversionTransformation.h"
0021 #include "KoColorProofingConversionTransformation.h"
0022 #include "KoCompositeOp.h"
0023 #include "KisDitherOp.h"
0024 #include <KoID.h>
0025 #include "kritapigment_export.h"
0026 
0027 class QDomDocument;
0028 class QDomElement;
0029 class KoChannelInfo;
0030 class KoColorProfile;
0031 class KoColorTransformation;
0032 class QBitArray;
0033 
0034 enum Deletability {
0035     OwnedByRegistryDoNotDelete,
0036     OwnedByRegistryRegistryDeletes,
0037     NotOwnedByRegistry
0038 };
0039 
0040 enum ColorSpaceIndependence {
0041     FULLY_INDEPENDENT,
0042     TO_LAB16,
0043     TO_RGBA8,
0044     TO_RGBA16
0045 };
0046 
0047 class KoMixColorsOp;
0048 class KoConvolutionOp;
0049 
0050 /**
0051  * A KoColorSpace is the definition of a certain color space.
0052  *
0053  * A color model and a color space are two related concepts. A color
0054  * model is more general in that it describes the channels involved and
0055  * how they in broad terms combine to describe a color. Examples are
0056  * RGB, HSV, CMYK.
0057  *
0058  * A color space is more specific in that it also describes exactly how
0059  * the channels are combined. So for each color model there can be a
0060  * number of specific color spaces. So RGB is the model and sRGB,
0061  * adobeRGB, etc are colorspaces.
0062  *
0063  * In Pigment KoColorSpace acts as both a color model and a color space.
0064  * You can think of the class definition as the color model, but the
0065  * instance of the class as representing a colorspace.
0066  *
0067  * A third concept is the profile represented by KoColorProfile. It
0068  * represents the info needed to specialize a color model into a color
0069  * space.
0070  *
0071  * KoColorSpace is an abstract class serving as an interface.
0072  *
0073  * Subclasses implement actual color spaces
0074  * Some subclasses implement only some parts and are named Traits
0075  *
0076  */
0077 class KRITAPIGMENT_EXPORT KoColorSpace : public boost::equality_comparable<KoColorSpace>
0078 {
0079     friend class KoColorSpaceRegistry;
0080     friend class KoColorSpaceFactory;
0081 protected:
0082     /// Only for use by classes that serve as baseclass for real color spaces
0083     KoColorSpace();
0084 
0085 public:
0086 
0087     /// Should be called by real color spaces
0088     KoColorSpace(const QString &id, const QString &name, KoMixColorsOp *mixColorsOp, KoConvolutionOp *convolutionOp);
0089 
0090     virtual bool operator==(const KoColorSpace& rhs) const;
0091 protected:
0092     virtual ~KoColorSpace();
0093 
0094 public:
0095     //========== Gamut and other basic info ===================================//
0096     /*
0097      * @returns QPolygonF with 5*channel samples converted to xyY.
0098      * maybe convert to 3d space in future?
0099      */
0100     QPolygonF gamutXYY() const;
0101 
0102     /*
0103      * @returns a polygon with 5 samples per channel converted to xyY, but unlike
0104      * gamutxyY it focuses on the luminance. This then can be used to visualise
0105      * the approximate trc of a given colorspace.
0106      */
0107     QPolygonF estimatedTRCXYY() const;
0108 
0109     QVector <qreal> lumaCoefficients() const;
0110 
0111     //========== Channels =====================================================//
0112 
0113     /// Return a list describing all the channels this color model has. The order
0114     /// of the channels in the list is the order of channels in the pixel. To find
0115     /// out the preferred display position, use KoChannelInfo::displayPosition.
0116     QList<KoChannelInfo *> channels() const;
0117 
0118     /**
0119      * The total number of channels for a single pixel in this color model
0120      */
0121     virtual quint32 channelCount() const = 0;
0122 
0123     /**
0124      * Position of the alpha channel in a pixel
0125      */
0126     virtual quint32 alphaPos() const = 0;
0127 
0128     /**
0129      * The total number of color channels (excludes alpha) for a single
0130      * pixel in this color model.
0131      */
0132     virtual quint32 colorChannelCount() const = 0;
0133 
0134     /**
0135      * returns a QBitArray that contains true for the specified
0136      * channel types:
0137      *
0138      * @param color if true, set all color channels to true
0139      * @param alpha if true, set all alpha channels to true
0140      *
0141      * The order of channels is the colorspace descriptive order,
0142      * not the pixel order.
0143      */
0144     QBitArray channelFlags(bool color = true, bool alpha = false) const;
0145 
0146     /**
0147      * The size in bytes of a single pixel in this color model
0148      */
0149     virtual quint32 pixelSize() const = 0;
0150 
0151     /**
0152      * Return a string with the channel's value suitable for display in the gui.
0153      */
0154     virtual QString channelValueText(const quint8 *pixel, quint32 channelIndex) const = 0;
0155 
0156     /**
0157      * Return a string with the channel's value with integer
0158      * channels normalised to the floating point range 0 to 1, if
0159      * appropriate.
0160      */
0161     virtual QString normalisedChannelValueText(const quint8 *pixel, quint32 channelIndex) const = 0;
0162 
0163     /**
0164      * Return a QVector of floats with channels' values normalized
0165      * to floating point range 0 to 1.
0166      */
0167     virtual void normalisedChannelsValue(const quint8 *pixel, QVector<float> &channels) const = 0;
0168 
0169     /**
0170      * Write in the pixel the value from the normalized vector.
0171      */
0172     virtual void fromNormalisedChannelsValue(quint8 *pixel, const QVector<float> &values) const = 0;
0173 
0174     /**
0175      * Convert the value of the channel at the specified position into
0176      * an 8-bit value. The position is not the number of bytes, but
0177      * the position of the channel as defined in the channel info list.
0178      */
0179     virtual quint8 scaleToU8(const quint8 * srcPixel, qint32 channelPos) const = 0;
0180 
0181     /**
0182      * Set dstPixel to the pixel containing only the given channel of srcPixel. The remaining channels
0183      * should be set to whatever makes sense for 'empty' channels of this color space,
0184      * with the intent being that the pixel should look like it only has the given channel.
0185      */
0186     virtual void singleChannelPixel(quint8 *dstPixel, const quint8 *srcPixel, quint32 channelIndex) const = 0;
0187 
0188     //========== Identification ===============================================//
0189 
0190     /**
0191      * ID for use in files and internally: unchanging name. As the id must be unique
0192      * it is usually the concatenation of the id of the color model and of the color
0193      * depth, for instance "RGBA8" or "CMYKA16" or "XYZA32f".
0194      */
0195     QString id() const;
0196 
0197     /**
0198      * User visible name which contains the name of the color model and of the color depth.
0199      * For instance "RGBA (8-bits)" or "CMYKA (16-bits)".
0200      */
0201     QString name() const;
0202 
0203     /**
0204      * @return a string that identify the color model (for instance "RGB" or "CMYK" ...)
0205      * @see KoColorModelStandardIds.h
0206      */
0207     virtual KoID colorModelId() const = 0;
0208     /**
0209      * @return a string that identify the bit depth (for instance "U8" or "F16" ...)
0210      * @see KoColorModelStandardIds.h
0211      */
0212     virtual KoID colorDepthId() const = 0;
0213 
0214     /**
0215      * @return true if the profile given in argument can be used by this color space
0216      */
0217     virtual bool profileIsCompatible(const KoColorProfile* profile) const = 0;
0218 
0219     /**
0220      * If false, images in this colorspace will degrade considerably by
0221      * functions, tools and filters that have the given measure of colorspace
0222      * independence.
0223      *
0224      * @param independence the measure to which this colorspace will suffer
0225      *                     from the manipulations of the tool or filter asking
0226      * @return false if no degradation will take place, true if degradation will
0227      *         take place
0228      */
0229     virtual bool willDegrade(ColorSpaceIndependence independence) const = 0;
0230 
0231     //========== Capabilities =================================================//
0232 
0233     /**
0234      * Tests if the colorspace offers the specific composite op.
0235      * @param srcSpace optional source color space. Some color spaces prefer blitting in source
0236      *        color space. If already known, additional composite ops may be available.
0237      */
0238     virtual bool hasCompositeOp(const QString & id, const KoColorSpace *srcSpace = nullptr) const;
0239 
0240     /**
0241      * Returns the list of user-visible composite ops supported by this colorspace.
0242      */
0243     virtual QList<KoCompositeOp*> compositeOps() const;
0244 
0245     /**
0246      * Retrieve a single composite op from the ones this colorspace offers.
0247      * If the requeste composite op does not exist, COMPOSITE_OVER is returned.
0248      * @param srcSpace optional source color space. Some color spaces prefer blitting in source
0249      *        color space. If already known, additional composite ops may be available.
0250      *        _Note_: if given, the returned op is only safe to use with this exact source color space!
0251      */
0252     const KoCompositeOp * compositeOp(const QString & id, const KoColorSpace *srcSpace = nullptr) const;
0253 
0254     /**
0255      * add a composite op to this colorspace.
0256      */
0257     virtual void addCompositeOp(const KoCompositeOp * op);
0258 
0259     /**
0260      * Returns true if the colorspace supports channel values outside the
0261      * (normalised) range 0 to 1.
0262      */
0263     virtual bool hasHighDynamicRange() const = 0;
0264 
0265 //========== Display profiles =============================================//
0266 
0267     /**
0268      * Return the profile of this color space.
0269      */
0270     virtual const KoColorProfile * profile() const = 0;
0271 
0272 
0273 //================= Conversion functions ==================================//
0274 
0275 
0276     /**
0277      * The fromQColor methods take a given color defined as an RGB QColor
0278      * and fills a byte array with the corresponding color in the
0279      * the colorspace managed by this strategy.
0280      *
0281      * @param color the QColor that will be used to fill dst
0282      * @param dst a pointer to a pixel
0283      * @param profile the optional profile that describes the color values of QColor
0284      */
0285     virtual void fromQColor(const QColor& color, quint8 *dst, const KoColorProfile * profile = 0) const = 0;
0286 
0287     /**
0288      * The toQColor methods take a byte array that is at least pixelSize() long
0289      * and converts the contents to a QColor, using the given profile as a source
0290      * profile and the optional profile as a destination profile.
0291      *
0292      * @param src a pointer to the source pixel
0293      * @param c the QColor that will be filled with the color at src
0294      * @param profile the optional profile that describes the color in c, for instance the monitor profile
0295      */
0296     virtual void toQColor(const quint8 *src, QColor *c, const KoColorProfile * profile = 0) const = 0;
0297 
0298     /**
0299      * The toQColor16 methods take a byte array that is at least pixelSize() long
0300      * and converts the contents to a 16 bit QColor, using the given profile as a source
0301      * profile and the optional profile as a destination profile.
0302      *
0303      * @param src a pointer to the source pixel
0304      * @param c the QColor that will be filled with the color at src
0305      * @param profile the optional profile that describes the color in c, for instance the monitor profile
0306      */
0307     virtual void toQColor16(const quint8 *src, QColor *c, const KoColorProfile * profile = 0) const = 0;
0308 
0309     /**
0310      * Convert the pixels in data to (8-bit BGRA) QImage using the specified profiles.
0311      *
0312      * @param data A pointer to a contiguous memory region containing width * height pixels
0313      * @param width in pixels
0314      * @param height in pixels
0315      * @param dstProfile destination profile
0316      * @param renderingIntent the rendering intent
0317      * @param conversionFlags conversion flags
0318      */
0319     virtual QImage convertToQImage(const quint8 *data, qint32 width, qint32 height,
0320                                    const KoColorProfile *  dstProfile,
0321                                    KoColorConversionTransformation::Intent renderingIntent,
0322                                    KoColorConversionTransformation::ConversionFlags conversionFlags) const;
0323 
0324     /**
0325      * Convert the specified data to Lab (D50). All colorspaces are guaranteed to support this
0326      *
0327      * @param src the source data
0328      * @param dst the destination data
0329      * @param nPixels the number of source pixels
0330      */
0331     virtual void toLabA16(const quint8 * src, quint8 * dst, quint32 nPixels) const;
0332 
0333     /**
0334      * Convert the specified data from Lab (D50). to this colorspace. All colorspaces are
0335      * guaranteed to support this.
0336      *
0337      * @param src the pixels in 16 bit lab format
0338      * @param dst the destination data
0339      * @param nPixels the number of pixels in the array
0340      */
0341     virtual void fromLabA16(const quint8 * src, quint8 * dst, quint32 nPixels) const;
0342 
0343     /**
0344      * Convert the specified data to sRGB 16 bits. All colorspaces are guaranteed to support this
0345      *
0346      * @param src the source data
0347      * @param dst the destination data
0348      * @param nPixels the number of source pixels
0349      */
0350     virtual void toRgbA16(const quint8 * src, quint8 * dst, quint32 nPixels) const;
0351 
0352     /**
0353      * Convert the specified data from sRGB 16 bits. to this colorspace. All colorspaces are
0354      * guaranteed to support this.
0355      *
0356      * @param src the pixels in 16 bit rgb format
0357      * @param dst the destination data
0358      * @param nPixels the number of pixels in the array
0359      */
0360     virtual void fromRgbA16(const quint8 * src, quint8 * dst, quint32 nPixels) const;
0361 
0362     /**
0363      * Create a color conversion transformation.
0364      */
0365     virtual KoColorConversionTransformation* createColorConverter(const KoColorSpace * dstColorSpace,
0366                                                                   KoColorConversionTransformation::Intent renderingIntent,
0367                                                                   KoColorConversionTransformation::ConversionFlags conversionFlags) const;
0368 
0369     /**
0370      * Retrieve the elevate-to-normalized floating point dithering op.
0371      */
0372     virtual const KisDitherOp *ditherOp(const QString &depth, DitherType type) const;
0373 
0374     virtual void addDitherOp(KisDitherOp *op);
0375 
0376     /**
0377      * Convert a byte array of srcLen pixels *src to the specified color space
0378      * and put the converted bytes into the prepared byte array *dst.
0379      *
0380      * Returns false if the conversion failed, true if it succeeded
0381      *
0382      * This function is not thread-safe. If you want to apply multiple conversion
0383      * in different threads at the same time, you need to create one color converter
0384      * per-thread using createColorConverter.
0385      */
0386     virtual bool convertPixelsTo(const quint8 * src,
0387                                  quint8 * dst, const KoColorSpace * dstColorSpace,
0388                                  quint32 numPixels,
0389                                  KoColorConversionTransformation::Intent renderingIntent,
0390                                  KoColorConversionTransformation::ConversionFlags conversionFlags) const;
0391 
0392     virtual KoColorConversionTransformation *createProofingTransform(const KoColorSpace * dstColorSpace,
0393                                                              const KoColorSpace * proofingSpace,
0394                                                              KoColorConversionTransformation::Intent renderingIntent,
0395                                                              KoColorConversionTransformation::Intent proofingIntent,
0396                                                              KoColorConversionTransformation::ConversionFlags conversionFlags,
0397                                                              quint8 *gamutWarning, double adaptationState) const;
0398     /**
0399      * @brief proofPixelsTo
0400      * @param src source
0401      * @param dst destination
0402      * @param numPixels the amount of pixels.
0403      * @param proofingTransform the intent used for proofing.
0404      * @return
0405      */
0406     virtual bool proofPixelsTo(const quint8 * src,
0407                                quint8 * dst,
0408                                quint32 numPixels,
0409                                KoColorConversionTransformation *proofingTransform) const;
0410 
0411     /**
0412      * Convert @p nPixels pixels in @p src into their human-visible
0413      * visual representation. The channel is shown as grayscale.
0414      *
0415      * Both buffers are in the same color space.
0416      *
0417      * @param src source buffer in (*this) color space
0418      * @param dst destination buffer in the same color space as @p src
0419      * @param nPixels length of the buffers in number of pixels
0420      * @param pixelSize stride of each pixel in the destination buffer
0421      * @param selectedChannelIndex Index of the selected channel.
0422      */
0423     virtual void convertChannelToVisualRepresentation(const quint8 *src, quint8 *dst, quint32 nPixels, const qint32 selectedChannelIndex) const = 0;
0424 
0425     /**
0426      * Convert @p nPixels pixels in @p src into their human-visible
0427      * visual representation. The channels are shown as if other channels were null (or, if Lab, L = 1.0, *a = *b = 0.0).
0428      *
0429      * Both buffers are in the same color space.
0430      *
0431      * @param src source buffer in (*this) color space
0432      * @param dst destination buffer in the same color space as @p src
0433      * @param nPixels length of the buffers in number of pixels
0434      * @param pixelSize stride of each pixel in the destination buffer
0435      * @param selectedChannels Bitmap of selected channels
0436      */
0437     virtual void convertChannelToVisualRepresentation(const quint8 *src, quint8 *dst, quint32 nPixels, const QBitArray selectedChannels) const = 0;
0438 
0439 //============================== Manipulation functions ==========================//
0440 
0441 
0442 //
0443 // The manipulation functions have default implementations that _convert_ the pixel
0444 // to a QColor and back. Reimplement these methods in your color strategy!
0445 //
0446 
0447     /**
0448      * Get the alpha value of the given pixel, downscaled to an 8-bit value.
0449      */
0450     virtual quint8 opacityU8(const quint8 * pixel) const = 0;
0451     virtual qreal opacityF(const quint8 * pixel) const = 0;
0452 
0453     /**
0454      * Set the alpha channel of the given run of pixels to the given value.
0455      *
0456      * pixels -- a pointer to the pixels that will have their alpha set to this value
0457      * alpha --  a downscaled 8-bit value for opacity
0458      * nPixels -- the number of pixels
0459      *
0460      */
0461     virtual void setOpacity(quint8 * pixels, quint8 alpha, qint32 nPixels) const = 0;
0462     virtual void setOpacity(quint8 * pixels, qreal alpha, qint32 nPixels) const = 0;
0463 
0464 
0465     /**
0466     * Copy the opacity of a run of pixels.
0467     *
0468     * src -- pixels to get opacity from.  This is in the source colorspace.
0469     * dst -- pixels in Alpha8 colorspace to be assigned the opacity of src pixel
0470     * nPixels -- the number of pixels
0471     *
0472     */
0473     virtual void copyOpacityU8(quint8* src, quint8* dst, qint32 nPixels) const = 0;
0474 
0475     /**
0476      * Multiply the alpha channel of the given run of pixels by the given value.
0477      *
0478      * pixels -- a pointer to the pixels that will have their alpha set to this value
0479      * alpha --  a downscaled 8-bit value for opacity
0480      * nPixels -- the number of pixels
0481      *
0482      */
0483     virtual void multiplyAlpha(quint8 * pixels, quint8 alpha, qint32 nPixels) const = 0;
0484 
0485     /**
0486      * Applies the specified 8-bit alpha mask to the pixels. We assume that there are just
0487      * as many alpha values as pixels but we do not check this; the alpha values
0488      * are assumed to be 8-bits.
0489      */
0490     virtual void applyAlphaU8Mask(quint8 * pixels, const quint8 * alpha, qint32 nPixels) const = 0;
0491 
0492     /**
0493      * Applies the inverted 8-bit alpha mask to the pixels. We assume that there are just
0494      * as many alpha values as pixels but we do not check this; the alpha values
0495      * are assumed to be 8-bits.
0496      */
0497     virtual void applyInverseAlphaU8Mask(quint8 * pixels, const quint8 * alpha, qint32 nPixels) const = 0;
0498 
0499     /**
0500      * Applies the specified float alpha mask to the pixels. We assume that there are just
0501      * as many alpha values as pixels but we do not check this; alpha values have to be between 0.0 and 1.0
0502      */
0503     virtual void applyAlphaNormedFloatMask(quint8 * pixels, const float * alpha, qint32 nPixels) const = 0;
0504 
0505     /**
0506      * Applies the inverted specified float alpha mask to the pixels. We assume that there are just
0507      * as many alpha values as pixels but we do not check this; alpha values have to be between 0.0 and 1.0
0508      */
0509     virtual void applyInverseNormedFloatMask(quint8 * pixels, const float * alpha, qint32 nPixels) const = 0;
0510 
0511     /**
0512      * Fills \p pixels with specified \p brushColor and then applies inverted brush
0513      * mask specified in \p alpha.
0514      */
0515     virtual void fillInverseAlphaNormedFloatMaskWithColor(quint8 * pixels, const float * alpha, const quint8 *brushColor, qint32 nPixels) const = 0;
0516 
0517     /**
0518      * Fills \p dst with specified \p brushColor and then applies inverted brush
0519      * mask specified in \p brush. Premultiplied red channel of the brush is
0520      * used as an alpha channel for destination pixels.
0521      *
0522      * The equation is:
0523      *
0524      *     dstC = colorC;
0525      *     dstA = qAlpha(brush) * (255 - qRed(brush)) / 255;
0526      */
0527     virtual void fillGrayBrushWithColor(quint8 *dst, const QRgb *brush, quint8 *brushColor, qint32 nPixels) const = 0;
0528 
0529     /**
0530      * Fills \p dst with specified \p brushColor and then applies inverted brush
0531      * mask specified in \p brush. Inverted red channel of the brush is used
0532      * as lightness of the destination. Alpha channel of the brush is used as
0533      * alpha of the destination.
0534      *
0535      * The equation is:
0536      *
0537      *     dstL_hsl = preserveLightness(colorL_hsl, lightFactor);
0538      *     dstA = qAlpha(brush);
0539      *
0540      * For details on preserveLightness() formula,
0541      * see KoColorSpacePreserveLightnessUtils.h
0542      */
0543     virtual void fillGrayBrushWithColorAndLightnessOverlay(quint8 *dst, const QRgb *brush, quint8 *brushColor, qint32 nPixels) const;
0544     // Same as above, but with contrast adjusted by strength.  Strength == 1 -> full contrast.  Allows softer lightness adjustments.
0545     virtual void fillGrayBrushWithColorAndLightnessWithStrength(quint8* dst, const QRgb* brush, quint8* brushColor, qreal strength, qint32 nPixels) const;
0546     // Same as above, but applies lightness adjustment to \p dst in-place
0547     virtual void modulateLightnessByGrayBrush(quint8* dst, const QRgb *brush, qreal strength, qint32 nPixels) const;
0548 
0549     /**
0550      * Create an adjustment object for adjusting the brightness and contrast
0551      * transferValues is a 256 bins array with values from 0 to 0xFFFF
0552      * This function is thread-safe, but you need to create one KoColorTransformation per thread.
0553      */
0554     virtual KoColorTransformation *createBrightnessContrastAdjustment(const quint16 *transferValues) const = 0;
0555 
0556     /**
0557      * Create an adjustment object for adjusting individual channels
0558      * transferValues is an array of colorChannelCount number of 256 bins array with values from 0 to 0xFFFF
0559      * This function is thread-safe, but you need to create one KoColorTransformation per thread.
0560      *
0561      * The layout of the channels must be the following:
0562      *
0563      * 0..N-2 - color channels of the pixel;
0564      * N-1 - alpha channel of the pixel (if exists)
0565      */
0566     virtual KoColorTransformation *createPerChannelAdjustment(const quint16 * const* transferValues) const = 0;
0567 
0568     /**
0569      * Darken all color channels with the given amount. If compensate is true,
0570      * the compensation factor will be used to limit the darkening.
0571      *
0572      */
0573     virtual KoColorTransformation *createDarkenAdjustment(qint32 shade, bool compensate, qreal compensation) const = 0;
0574 
0575     /**
0576      * Invert color channels of the given pixels
0577      * This function is thread-safe, but you need to create one KoColorTransformation per thread.
0578      */
0579     virtual KoColorTransformation *createInvertTransformation() const = 0;
0580 
0581     /**
0582      * Get the difference between 2 colors, normalized in the range (0,255). Only completely
0583      * opaque and completely transparent are taken into account when computing the difference;
0584      * other transparency levels are not regarded when finding the difference.
0585      *
0586      * Completely transparent pixels are treated as if they are completely
0587      * different from any non-transparent pixels.
0588      */
0589     virtual quint8 difference(const quint8* src1, const quint8* src2) const = 0;
0590 
0591     /**
0592      * Get the difference between 2 colors, normalized in the range (0,255). This function
0593      * takes the Alpha channel of the pixel into account. Alpha channel has the same
0594      * weight as Lightness channel.
0595      *
0596      * Completely transparent pixels are treated as if their color channels are
0597      * the same as ones of the other pixel. In other words, only alpha channel
0598      * difference is compared.
0599      */
0600     virtual quint8 differenceA(const quint8* src1, const quint8* src2) const = 0;
0601 
0602     /**
0603      * @return the mix color operation of this colorspace (do not delete it locally, it's deleted by the colorspace).
0604      */
0605     virtual KoMixColorsOp* mixColorsOp() const;
0606 
0607     /**
0608      * @return the convolution operation of this colorspace (do not delete it locally, it's deleted by the colorspace).
0609      */
0610     virtual KoConvolutionOp* convolutionOp() const;
0611 
0612     /**
0613      * Calculate the intensity of the given pixel, scaled down to the range 0-255. XXX: Maybe this should be more flexible
0614      */
0615     virtual quint8 intensity8(const quint8 * src) const = 0;
0616 
0617     /**
0618      * Calculate the intensity of the given pixel, scaled down to the range 0-1
0619      */
0620     virtual qreal intensityF(const quint8 * src) const = 0;
0621 
0622     /*
0623      *increase luminosity by step
0624      */
0625     virtual void increaseLuminosity(quint8 * pixel, qreal step) const;
0626     virtual void decreaseLuminosity(quint8 * pixel, qreal step) const;
0627     virtual void increaseSaturation(quint8 * pixel, qreal step) const;
0628     virtual void decreaseSaturation(quint8 * pixel, qreal step) const;
0629     virtual void increaseHue(quint8 * pixel, qreal step) const;
0630     virtual void decreaseHue(quint8 * pixel, qreal step) const;
0631     virtual void increaseRed(quint8 * pixel, qreal step) const;
0632     virtual void increaseGreen(quint8 * pixel, qreal step) const;
0633     virtual void increaseBlue(quint8 * pixel, qreal step) const;
0634     virtual void increaseYellow(quint8 * pixel, qreal step) const;
0635     virtual void toHSY(const QVector<double> &channelValues, qreal *hue, qreal *sat, qreal *luma) const = 0;
0636     virtual QVector <double> fromHSY(qreal *hue, qreal *sat, qreal *luma) const = 0;
0637     virtual void toYUV(const QVector<double> &channelValues, qreal *y, qreal *u, qreal *v) const = 0;
0638     virtual QVector <double> fromYUV(qreal *y, qreal *u, qreal *v) const = 0;
0639     /**
0640      * Compose two arrays of pixels together. If source and target
0641      * are not the same color model, the source pixels will be
0642      * converted to the target model. We're "dst" -- "dst" pixels are always in _this_
0643      * colorspace.
0644      *
0645      * @param srcSpace the colorspace of the source pixels that will be composited onto "us"
0646      * @param params the information needed for blitting e.g. the source and destination pixel data,
0647      *        the opacity and flow, ...
0648      * @param op the composition operator instance to use, e.g. COPY_OVER.
0649      *        This operator must belong to this (dst) color space, UNLESS preferCompositionInSourceColorSpace()
0650      *        returns true. In this case, the operator should be from the source color space. Besides avoiding
0651      *        recurring lookups via ::compositeOp(), this is necessary if this color space does not implement
0652      *        the desired composite op, but srcPace does.
0653      *        Use ::compositeOp() with valid srcSpace to get the appropriate op.
0654      *
0655      * @param renderingIntent the rendering intent
0656      * @param conversionFlags the conversion flags.
0657      *
0658      */
0659     virtual void bitBlt(const KoColorSpace* srcSpace, const KoCompositeOp::ParameterInfo& params, const KoCompositeOp* op,
0660                         KoColorConversionTransformation::Intent renderingIntent,
0661                         KoColorConversionTransformation::ConversionFlags conversionFlags) const;
0662 
0663     /**
0664      * Serialize this color following Create's swatch color specification available
0665      * at https://web.archive.org/web/20110826002520/http://create.freedesktop.org/wiki/Swatches_-_color_file_format/Draft
0666      *
0667      * This function doesn't create the \<color /\> element but rather the \<CMYK /\>,
0668      * \<sRGB /\>, \<RGB /\> ... elements. It is assumed that colorElt is the \<color /\>
0669      * element.
0670      *
0671      * @param pixel buffer to serialized
0672      * @param colorElt root element for the serialization, it is assumed that this
0673      *                 element is \<color /\>
0674      * @param doc is the document containing colorElt
0675      */
0676     virtual void colorToXML(const quint8* pixel, QDomDocument& doc, QDomElement& colorElt) const = 0;
0677 
0678     /**
0679      * Unserialize a color following Create's swatch color specification available
0680      * at https://web.archive.org/web/20110826002520/http://create.freedesktop.org/wiki/Swatches_-_color_file_format/Draft
0681      *
0682      * @param pixel buffer where the color will be unserialized
0683      * @param elt the element to unserialize (\<CMYK /\>, \<sRGB /\>, \<RGB /\>)
0684      * @return the unserialize color, or an empty color object if the function failed
0685      *         to unserialize the color
0686      */
0687     virtual void colorFromXML(quint8* pixel, const QDomElement& elt) const = 0;
0688 
0689     KoColorTransformation* createColorTransformation(const QString & id, const QHash<QString, QVariant> & parameters) const;
0690 
0691 protected:
0692 
0693     /**
0694      * Use this function in the constructor of your colorspace to add the information about a channel.
0695      * @param ci a pointer to the information about a channel
0696      */
0697     virtual void addChannel(KoChannelInfo * ci);
0698     const KoColorConversionTransformation* toLabA16Converter() const;
0699     const KoColorConversionTransformation* fromLabA16Converter() const;
0700     const KoColorConversionTransformation* toRgbA16Converter() const;
0701     const KoColorConversionTransformation* fromRgbA16Converter() const;
0702 
0703     /**
0704      * Returns the thread-local conversion cache. If it doesn't exist
0705      * yet, it is created. If it is currently too small, it is resized.
0706      */
0707     QVector<quint8> * threadLocalConversionCache(quint32 size) const;
0708 
0709     /**
0710      * This function defines the behavior of the bitBlt function
0711      * when the composition of pixels in different colorspaces is
0712      * requested, that is in case:
0713      *
0714      * srcCS == any
0715      * dstCS == this
0716      *
0717      * 1) preferCompositionInSourceColorSpace() == false,
0718      *
0719      *    the source pixels are first converted to *this color space
0720      *    and then composition is performed.
0721      *
0722      * 2)  preferCompositionInSourceColorSpace() == true,
0723      *
0724      *    the destination pixels are first converted into *srcCS color
0725      *    space, then the composition is done, and the result is finally
0726      *    converted into *this colorspace.
0727      *
0728      *    This is used by alpha8() color space mostly, because it has
0729      *    weaker representation of the color, so the composition
0730      *    should be done in CS with richer functionality.
0731      */
0732     virtual bool preferCompositionInSourceColorSpace() const;
0733 
0734 
0735     struct Private;
0736     Private * const d;
0737 
0738 };
0739 
0740 inline QDebug operator<<(QDebug dbg, const KoColorSpace *cs)
0741 {
0742     if (cs) {
0743         dbg.nospace() << cs->name() << " (" << cs->colorModelId().id() << "," << cs->colorDepthId().id() << " )";
0744     } else {
0745         dbg.nospace() << "0x0";
0746     }
0747 
0748     return dbg.space();
0749 }
0750 
0751 
0752 #endif // KOCOLORSPACE_H