1 /*
2  * Xournal++
3  *
4  * Color utility, does color conversions
5  *
6  * @author Xournal++ Team
7  * https://github.com/xournalpp/xournalpp
8  *
9  * @license GNU GPLv2 or later
10  */
11 
12 #pragma once
13 
14 #include <cassert>
15 #include <cstdint>
16 #include <limits>
17 
18 #include <gtk/gtk.h>
19 
20 #ifndef XOURNAL_ENFORCE_COLOR
21 using Color = uint32_t;
22 #else
23 #include <iostream>
24 #include <limits>
25 #include <type_traits>
26 struct Color {
27     constexpr Color() = default;
28 
29     template <typename T, std::enable_if_t<std::is_same_v<T, uint32_t>, int> = 0>
ColorColor30     constexpr Color(T t): val(uint32_t(t)) {}
31     template <typename T, std::enable_if_t<std::is_unsigned_v<T> && !std::is_same_v<T, uint32_t>, int> = 0>
ColorColor32     constexpr explicit Color(T t): val(uint32_t(t)) {}
33     template <typename T, std::enable_if_t<std::is_signed_v<T>, int> = 0>
ColorColor34     constexpr explicit Color(T t): val(uint32_t(t)) {}
35 
36     constexpr explicit operator uint32_t&() { return val; }
37 
38     template <typename T, std::enable_if_t<std::is_same_v<T, uint32_t>, int> = 0>
TColor39     constexpr explicit operator T() const {
40         return val;
41     }
42     template <typename T, std::enable_if_t<std::is_unsigned_v<T> && !std::is_same_v<T, uint32_t>, int> = 0>
TColor43     constexpr explicit operator T() const {
44         return val;
45     }
46     template <typename T, std::enable_if_t<std::is_signed_v<T>, int> = 0>
TColor47     constexpr explicit operator T() const {
48         return val;
49     }
50 
51     constexpr auto operator=(Color const& rhs) -> Color& = default;
52     constexpr auto operator=(uint32_t rhs) -> Color& { return *this = Color(rhs); }
53 
54     constexpr friend auto operator&(Color lhs, uint32_t rhs) -> uint32_t { return lhs.val & rhs; }
55     constexpr friend auto operator|(Color lhs, uint32_t rhs) -> uint32_t { return lhs.val | rhs; }
56     constexpr friend auto operator^(Color lhs, uint32_t rhs) -> uint32_t { return lhs.val ^ rhs; }
57     constexpr friend auto operator&(uint32_t lhs, Color rhs) -> uint32_t { return lhs & rhs.val; }
58     constexpr friend auto operator|(uint32_t lhs, Color rhs) -> uint32_t { return lhs | rhs.val; }
59     constexpr friend auto operator^(uint32_t lhs, Color rhs) -> uint32_t { return lhs ^ rhs.val; }
60     constexpr friend auto operator>>(Color lhs, uint32_t rhs) -> uint32_t { return lhs.val << rhs; }
61     constexpr friend auto operator<<(Color lhs, uint32_t rhs) -> uint32_t { return lhs.val >> rhs; }
62 
63     constexpr friend bool operator==(Color lhs, Color rhs) { return lhs.val == rhs.val; }
64     constexpr friend bool operator!=(Color lhs, Color rhs) { return lhs.val != rhs.val; }
65     constexpr friend bool operator<(Color lhs, Color rhs) { return lhs.val < rhs.val; }
66     constexpr friend bool operator<=(Color lhs, Color rhs) { return lhs.val <= rhs.val; }
67     constexpr friend bool operator>(Color lhs, Color rhs) { return lhs.val > rhs.val; }
68     constexpr friend bool operator>=(Color lhs, Color rhs) { return lhs.val >= rhs.val; }
69 
70     inline friend std::ostream& operator<<(std::ostream& os, Color rhs) { return os << rhs.val; }
71 
72     uint32_t val{};
73 };
74 
75 namespace std {
76 template <>
77 struct hash<Color> {
78     size_t operator()(Color c) const noexcept { return c.val; }
79 };
80 
81 
82 }  // namespace std
83 
84 #endif
85 
86 struct ColorU16 {
87     uint16_t red{};
88     uint16_t green{};
89     uint16_t blue{};
90     uint16_t alpha{};
91 };
92 
93 namespace Util {
94 
95 constexpr auto rgb_to_GdkRGBA(Color color) -> GdkRGBA;
96 constexpr auto argb_to_GdkRGBA(Color color) -> GdkRGBA;
97 constexpr auto argb_to_GdkRGBA(Color color, double alpha) -> GdkRGBA;
98 constexpr auto GdkRGBA_to_argb(const GdkRGBA& color) -> Color;
99 constexpr auto GdkRGBA_to_rgb(const GdkRGBA& color) -> Color;
100 
101 constexpr auto GdkRGBA_to_ColorU16(const GdkRGBA& color) -> ColorU16;
102 
103 
104 void cairo_set_source_rgbi(cairo_t* cr, Color color);
105 void cairo_set_source_rgbi(cairo_t* cr, Color color, double alpha);
106 
107 constexpr auto floatToUIntColor(double color) -> uint32_t;
108 
109 }  // namespace Util
110 
111 constexpr auto Util::rgb_to_GdkRGBA(const Color color) -> GdkRGBA {  //
112     return Util::argb_to_GdkRGBA(Color{0xFF000000U | color});
113 }
114 
115 constexpr auto Util::argb_to_GdkRGBA(const Color color) -> GdkRGBA {
116     return {((color >> 16U) & 0xFFU) / 255.0,  //
117             ((color >> 8U) & 0xFFU) / 255.0,   //
118             ((color >> 0U) & 0xFFU) / 255.0,   //
119             ((color >> 24U) & 0xFFU) / 255.0};
120 }
121 
122 constexpr auto Util::argb_to_GdkRGBA(Color color, double alpha) -> GdkRGBA {
123     return {((color >> 16U) & 0xFFU) / 255.0,  //
124             ((color >> 8U) & 0xFFU) / 255.0,   //
125             ((color >> 0U) & 0xFFU) / 255.0,   //
126             alpha};
127 }
128 
129 constexpr auto Util::GdkRGBA_to_argb(const GdkRGBA& color) -> Color {
130     return floatToUIntColor(color.alpha) << 24U |  //
131            GdkRGBA_to_rgb(color);                  //
132 }
133 
134 constexpr auto Util::GdkRGBA_to_rgb(const GdkRGBA& color) -> Color {
135     return floatToUIntColor(color.red) << 16U |   //
136            floatToUIntColor(color.green) << 8U |  //
137            floatToUIntColor(color.blue);
138 }
139 
140 constexpr auto Util::floatToUIntColor(const double color) -> uint32_t {  //
141     // Splits the double into a equal sized distribution between [0,256[ and rounding down
142     // inspired by, which isn't completely correct:
143     // https://stackoverflow.com/questions/1914115/converting-color-value-from-float-0-1-to-byte-0-255
144     constexpr double MAX_COLOR = 256.0 - std::numeric_limits<double>::epsilon() * 128;
145     static_assert(MAX_COLOR < 256.0, "MAX_COLOR isn't smaller than 256");
146     return static_cast<uint32_t>(color * MAX_COLOR);
147 }
148 
149 constexpr auto Util::GdkRGBA_to_ColorU16(const GdkRGBA& color) -> ColorU16 {
150     auto floatToColorU16 = [](double color) {
151         constexpr double MAX_COLOR = 65536.0 - std::numeric_limits<double>::epsilon() * (65536.0 / 2.0);
152         static_assert(MAX_COLOR < 65536.0, "MAX_COLOR isn't smaller than 65536");
153         return static_cast<uint16_t>(color * MAX_COLOR);
154     };
155 
156     return {floatToColorU16(color.red),    //
157             floatToColorU16(color.green),  //
158             floatToColorU16(color.blue),   //
159             floatToColorU16(color.alpha)};
160 }
161