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