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

0001 // SPDX-FileCopyrightText: Lukas Sommer <sommerluk@gmail.com>
0002 // SPDX-License-Identifier: BSD-2-Clause OR MIT
0003 
0004 #ifndef RGBCOLOR_H
0005 #define RGBCOLOR_H
0006 
0007 #include "genericcolor.h"
0008 #include <optional>
0009 #include <qcolor.h>
0010 #include <qdebug.h>
0011 
0012 namespace PerceptualColor
0013 {
0014 
0015 /** @internal
0016  *
0017  * @brief An RGB color stored in multiple different RGB transformations.
0018  *
0019  * Unlike <tt>QColor</tt> (which is essentially a C++ <tt>union</tt> of
0020  * different color formats, so only one of them is actually saved),
0021  * @ref RgbColor <em>actually</em> stores <em>all</em> available color
0022  * transformations.
0023  *
0024  * This data type is just an (ugly) implementation detail of @ref ColorDialog.
0025  * For simplicity, data members are accessible directly, without write
0026  * protection. Usage: Create instances of this class with one of the static
0027  * factory functions, and assign them to <tt>const</tt> variables. The factory
0028  * functions guarantee that all data members have correct values representing
0029  * the <em>same</em> color.
0030  *
0031  * Changes to some values of some color formats under certain
0032  * circumstances do not change the color:
0033  * - HSL saturation: When the color is either black (L = 0%) or
0034  *   white (L = 100%).
0035  * - HSV/HSB saturation: When the color is black (V/B = 0%).
0036  *
0037  * The color conversion of this class provides meaningful and predictable
0038  * <em>HSL-saturation</em> and <em>HSV-saturation</em> values.
0039  *
0040  * This data type can be passed to QDebug thanks to
0041  * @ref operator<<(QDebug dbg, const PerceptualColor::RgbColor &value)
0042  *
0043  * @sa @ref AbsoluteColor */
0044 class RgbColor final
0045 {
0046 public:
0047     [[nodiscard]] static RgbColor fromHsl(const GenericColor &color);
0048     [[nodiscard]] static RgbColor fromHsv(const GenericColor &color);
0049     [[nodiscard]] static RgbColor fromHwb(const GenericColor &color);
0050     [[nodiscard]] static RgbColor fromRgb255(const GenericColor &color, std::optional<double> hue = std::optional<double>());
0051     [[nodiscard]] static RgbColor fromRgbQColor(const QColor &color);
0052 
0053     /** @brief Constructor for an uninitialized object.
0054      *
0055      * This constructor is quite useless except for declaring variables
0056      * of this type. Use the static functions to get an actual color object.
0057      *
0058      * @warning As the data members are uninitialized, this implies that the
0059      * count of <tt>QList</tt> items is not correct! */
0060     RgbColor();
0061     /** @brief Default copy constructor
0062      * @param other the object to copy */
0063     RgbColor(const RgbColor &other) = default;
0064     /** @brief Default copy assignment operator
0065      * @param other the object to copy
0066      * @returns The default implementation’s return value. */
0067     RgbColor &operator=(const RgbColor &other) = default;
0068     // NOTE About move constructor and move assignment operator:
0069     // Declaring them with “= default” will create a compiler-generated
0070     // implementation that, apparently, does not copy correctly
0071     // the data members of type QList. Using “=delete” however
0072     // will create a move constructor that it forbidden to be used.
0073     // When some code needs a move constructor, the overload resolution
0074     // will choose this move constructor, and the compilation fails
0075     // because it is forbidden to be used. For details on this behaviour,
0076     // see https://blog.knatten.org/2021/10/15/ Therefore, we do not declare
0077     // the move constructor at all: Because the copy constructor exists, the
0078     // move constructor will not be generated implicitly at all. When some
0079     // code needs a move constructor, the overload resolution does not find
0080     // one and falls back to the copy constructor (which is the default
0081     // implementation, but apparently works correctly also for QList members).
0082     // RgbColor &operator=(RgbColor &&other) noexcept = default;
0083     // RgbColor(RgbColor &&other) noexcept = default;
0084 
0085     [[nodiscard]] bool operator==(const RgbColor &other) const;
0086 
0087     /** @brief HWB representation.
0088      *
0089      * Range: [0, 360], [0, 100], [0, 100] */
0090     GenericColor hwb;
0091     /** @brief HSL representation.
0092      *
0093      * Range: [0, 360], [0, 100], [0, 100] */
0094     GenericColor hsl;
0095     /** @brief HSV representation.
0096      *
0097      * Range: [0, 360], [0, 100], [0, 100] */
0098     GenericColor hsv;
0099     /** @brief RGB representation.
0100      *
0101      * Range: [0, 255] */
0102     GenericColor rgb255;
0103     /** @brief QColor representation.
0104      *
0105      * <tt>QColor::spec()</tt> is <tt>QColor::Rgb</tt>. */
0106     QColor rgbQColor;
0107 
0108 private:
0109     void fillAll(QColor color, std::optional<double> hue);
0110 };
0111 
0112 QDebug operator<<(QDebug dbg, const PerceptualColor::RgbColor &value);
0113 
0114 } // namespace PerceptualColor
0115 
0116 #endif // RGBCOLOR_H