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

0001 // SPDX-FileCopyrightText: Lukas Sommer <sommerluk@gmail.com>
0002 // SPDX-License-Identifier: BSD-2-Clause OR MIT
0003 
0004 #ifndef CHROMALIGHTNESSIMAGEPARAMETERS_H
0005 #define CHROMALIGHTNESSIMAGEPARAMETERS_H
0006 
0007 #include <qglobal.h>
0008 #include <qmetatype.h>
0009 #include <qsharedpointer.h>
0010 #include <qsize.h>
0011 #include <qvariant.h>
0012 
0013 namespace PerceptualColor
0014 {
0015 
0016 class AsyncImageRenderCallback;
0017 class RgbColorSpace;
0018 
0019 /** @internal
0020  *
0021  *  @brief An image of a chroma-lightness plane.
0022  *
0023  * This is a cut through the gamut body at a given hue.
0024  *
0025  * For the y axis, its height covers the lightness range [0, 100].
0026  * Coordinate point <tt>(0)</tt> corresponds to value 100.
0027  * Coordinate point <tt>height</tt> corresponds to value 0.
0028  * Its x axis uses always the same scale as the y axis. So if the size
0029  * is a square, both x range and y range are from 0 to 100. If the
0030  * width is larger than the height, the x range goes beyond 100. The
0031  * image paints all the LCH values that are within the gamut and x/y range.
0032  * Each pixel show the color of the coordinate point at its center. So
0033  * the pixel at pixel position <tt>(2, 3)</tt> shows the color corresponding
0034  * to coordinate point <tt>(2.5, 3.5)</tt>.
0035  *
0036  * @todo Solve the problem with nearestNeighborSearch to respond immediately,
0037  * without waiting for the rendering to complete, to avoid using things like
0038  * <a href="https://api.kde.org/frameworks/kwidgetsaddons/html/classKBusyIndicatorWidget.html">
0039  * KBusyIndicatorWidget</a>.
0040  *
0041  * @note Intentionally there is no anti-aliasing because this would be much
0042  * slower: As there is no mathematical description of the shape of the color
0043  * solid, the only easy way to get anti-aliasing would be to render at a
0044  * higher resolution (say two times higher, which would yet mean four times
0045  * more data), and then downscale it to the final resolution. This would be
0046  * too slow. */
0047 class ChromaLightnessImageParameters final
0048 {
0049 public:
0050     [[nodiscard]] bool operator==(const ChromaLightnessImageParameters &other) const;
0051     [[nodiscard]] bool operator!=(const ChromaLightnessImageParameters &other) const;
0052     static void render(const QVariant &variantParameters, AsyncImageRenderCallback &callbackObject);
0053 
0054     /** @brief The LCH-hue.
0055      *
0056      * Valid range: 0° ≤ value < 360° */
0057     qreal hue = 0;
0058     /** @brief Image size, measured in physical pixels. */
0059     QSize imageSizePhysical;
0060     /** @brief Pointer to @ref RgbColorSpace object */
0061     QSharedPointer<PerceptualColor::RgbColorSpace> rgbColorSpace;
0062 
0063 private:
0064     /** @internal @brief Only for unit tests. */
0065     friend class TestChromaLightnessImageParameters;
0066 
0067     /** @brief Calculate one-dimensional index for given <tt>x</tt> and
0068      * <tt>y</tt> coordinates.
0069      *
0070      * @param x The <tt>x</tt> coordinate
0071      * @param y The <tt>y</tt> coordinate
0072      * @param imageSizePhysical The image size
0073      * @returns The corresponding index, assuming a one-dimensional array
0074      * that contains one element for each pixel, starting with the elements
0075      * <tt>(0, 0)</tt>, than <tt>(0, 1)</tt> and so on, line by line. */
0076     [[nodiscard]] static constexpr int maskIndex(const int x, const int y, const QSize imageSizePhysical)
0077     {
0078         return x + y * imageSizePhysical.width();
0079     }
0080 };
0081 
0082 } // namespace PerceptualColor
0083 
0084 Q_DECLARE_METATYPE(PerceptualColor::ChromaLightnessImageParameters)
0085 
0086 #endif // CHROMALIGHTNESSIMAGEPARAMETERS_H