File indexing completed on 2024-05-12 04:44:33

0001 // SPDX-FileCopyrightText: Lukas Sommer <sommerluk@gmail.com>
0002 // SPDX-License-Identifier: BSD-2-Clause OR MIT
0003 
0004 #ifndef GRADIENTIMAGEPARAMETERS_H
0005 #define GRADIENTIMAGEPARAMETERS_H
0006 
0007 #include "lchadouble.h"
0008 #include <qglobal.h>
0009 #include <qimage.h>
0010 #include <qmetatype.h>
0011 #include <qsharedpointer.h>
0012 #include <qvariant.h>
0013 
0014 namespace PerceptualColor
0015 {
0016 class AsyncImageRenderCallback;
0017 class RgbColorSpace;
0018 
0019 /** @internal
0020  *
0021  *  @brief Parameters for image of a gradient.
0022  *
0023  * For usage with @ref AsyncImageProvider.
0024  *
0025  * As the hue is a circular property, there exists two ways to go one hue to
0026  * another (clockwise or counter-clockwise). This gradient takes always the
0027  * shortest way.
0028  *
0029  * The image has properties that can be accessed by the corresponding setters
0030  * and getters or directly. You should explicitly set all values
0031  * <em>before</em> calling the first time @ref render().
0032  *
0033  * This class supports HiDPI via its @ref setDevicePixelRatioF function.
0034  *
0035  * @todo Instead of providing an image that has actually the size that
0036  * has been requested, we could provide just a tile. The user would have
0037  * to tile the surface. In many cases, we could get away with much smaller
0038  * images. Attention: Test if this approach works fine when the screen scale
0039  * factor is not an integer! */
0040 struct GradientImageParameters final {
0041 public:
0042     explicit GradientImageParameters();
0043     [[nodiscard]] bool operator==(const GradientImageParameters &other) const;
0044     [[nodiscard]] bool operator!=(const GradientImageParameters &other) const;
0045 
0046     /** @brief Pointer to @ref RgbColorSpace object */
0047     QSharedPointer<PerceptualColor::RgbColorSpace> rgbColorSpace = nullptr;
0048 
0049     [[nodiscard]] LchaDouble colorFromValue(qreal value) const;
0050     static void render(const QVariant &variantParameters, AsyncImageRenderCallback &callbackObject);
0051     void setDevicePixelRatioF(const qreal newDevicePixelRatioF);
0052     void setFirstColor(const LchaDouble &newFirstColor);
0053     void setGradientLength(const int newGradientLength);
0054     void setGradientThickness(const int newGradientThickness);
0055     void setSecondColor(const LchaDouble &newFirstColor);
0056 
0057 private:
0058     /** @internal @brief Only for unit tests. */
0059     friend class TestGradientImageParameters;
0060 
0061     // Methods
0062     [[nodiscard]] static LchaDouble completlyNormalizedAndBounded(const LchaDouble &color);
0063     void updateSecondColor();
0064 
0065     // Data members
0066     /** @brief Internal storage of the device pixel ratio as floating point.
0067      *
0068      * @sa @ref setDevicePixelRatioF() */
0069     qreal m_devicePixelRatioF = 1;
0070     /** @brief Internal storage of the first color.
0071      *
0072      * The color is normalized and bound to the LCH color space.
0073      * @sa @ref completlyNormalizedAndBounded() */
0074     LchaDouble m_firstColorCorrected;
0075     /** @brief Internal storage for the gradient length, measured in
0076      * physical pixels.
0077      *
0078      * @sa @ref setGradientLength() */
0079     int m_gradientLength = 0;
0080     /** @brief Internal storage for the gradient thickness, measured in
0081      * physical pixels.
0082      *
0083      * @sa @ref setGradientThickness() */
0084     int m_gradientThickness = 0;
0085     /** @brief Internal storage of the image (cache).
0086      *
0087      * - If <tt>m_image.isNull()</tt> than either no cache is available
0088      *   or @ref m_gradientLength or @ref m_gradientThickness is <tt>0</tt>.
0089      *   Before using it, a new image has to be rendered. (If
0090      *   @ref m_gradientLength or @ref m_gradientThickness is
0091      *   <tt>0</tt>, this will be extremly fast.)
0092      * - If <tt>m_image.isNull()</tt> is <tt>false</tt>, than the cache
0093      *   is valid and can be used directly. */
0094     QImage m_image;
0095     /** @brief Internal storage of the second color (corrected and altered
0096      * value).
0097      *
0098      * The color is normalized and bound to the LCH color space. In an
0099      * additional step, it has been altered (by increasing or decreasing the
0100      * hue component in steps of 360°) to minimize the distance in hue
0101      * from this color to @ref m_firstColorCorrected. This is necessary to
0102      * easily allow to calculate the intermediate colors of the gradient, so
0103      * that they take the shortest way through the color space.
0104      * @sa @ref setFirstColor()
0105      * @sa @ref setSecondColor()
0106      * @sa @ref completlyNormalizedAndBounded()
0107      * @sa @ref updateSecondColor() */
0108     LchaDouble m_secondColorCorrectedAndAltered;
0109 };
0110 
0111 } // namespace PerceptualColor
0112 
0113 Q_DECLARE_METATYPE(PerceptualColor::GradientImageParameters)
0114 
0115 #endif // GRADIENTIMAGEPARAMETERS_H