FTXUI  5.0.0
C++ functional terminal UI.
color.cpp
Go to the documentation of this file.
1// Copyright 2020 Arthur Sonzogni. All rights reserved.
2// Use of this source code is governed by the MIT license that can be found in
3// the LICENSE file.
5
6#include <array> // for array
7#include <cmath>
8#include <cstdint>
9#include <string_view> // for literals
10
11#include "ftxui/screen/color_info.hpp" // for GetColorInfo, ColorInfo
12#include "ftxui/screen/terminal.hpp" // for ColorSupport, Color, Palette256, TrueColor
13
14namespace ftxui {
15
16using namespace std::literals;
17
18namespace {
19const std::array<const char*, 33> palette16code = {
20 "30", "40", //
21 "31", "41", //
22 "32", "42", //
23 "33", "43", //
24 "34", "44", //
25 "35", "45", //
26 "36", "46", //
27 "37", "47", //
28 "90", "100", //
29 "91", "101", //
30 "92", "102", //
31 "93", "103", //
32 "94", "104", //
33 "95", "105", //
34 "96", "106", //
35 "97", "107", //
36};
37
38} // namespace
39
40bool Color::operator==(const Color& rhs) const {
41 return red_ == rhs.red_ && green_ == rhs.green_ && blue_ == rhs.blue_ &&
42 type_ == rhs.type_;
43}
44
45bool Color::operator!=(const Color& rhs) const {
46 return !operator==(rhs);
47}
48
49std::string Color::Print(bool is_background_color) const {
50 switch (type_) {
51 case ColorType::Palette1:
52 return is_background_color ? "49"s : "39"s;
53
54 case ColorType::Palette16:
55 return palette16code[2 * red_ + is_background_color]; // NOLINT;
56
57 case ColorType::Palette256:
58 return (is_background_color ? "48;5;"s : "38;5;"s) + std::to_string(red_);
59
60 case ColorType::TrueColor:
61 default:
62 return (is_background_color ? "48;2;"s : "38;2;"s) //
63 + std::to_string(red_) + ";" //
64 + std::to_string(green_) + ";" //
65 + std::to_string(blue_); //
66 }
67}
68
69/// @brief Build a transparent color.
70/// @ingroup screen
71Color::Color() = default;
72
73/// @brief Build a transparent color.
74/// @ingroup screen
75Color::Color(Palette1 /*value*/) : Color() {}
76
77/// @brief Build a color using the Palette16 colors.
78/// @ingroup screen
80 : type_(ColorType::Palette16),
81 red_(index),
82 green_(0),
83 blue_(0),
84 alpha_(255) {}
85
86/// @brief Build a color using Palette256 colors.
87/// @ingroup screen
89 : type_(ColorType::Palette256),
90 red_(index),
91 blue_(0),
92 green_(0),
93 alpha_(255) {
95 return;
96 }
97 type_ = ColorType::Palette16;
99}
100
101/// @brief Build a Color from its RGB representation.
102/// https://en.wikipedia.org/wiki/RGB_color_model
103///
104/// @param red The quantity of red [0,255]
105/// @param green The quantity of green [0,255]
106/// @param blue The quantity of blue [0,255]
107/// @param alpha The quantity of alpha [0,255]
108/// @ingroup screen
109Color::Color(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha)
110 : type_(ColorType::TrueColor),
111 red_(red),
112 green_(green),
113 blue_(blue),
114 alpha_(alpha) {
116 return;
117 }
118
119 // Find the closest Color from the database:
120 const int max_distance = 256 * 256 * 3;
121 int closest = max_distance;
122 int best = 0;
123 const int database_begin = 16;
124 const int database_end = 256;
125 for (int i = database_begin; i < database_end; ++i) {
126 const ColorInfo color_info = GetColorInfo(Color::Palette256(i));
127 const int dr = color_info.red - red;
128 const int dg = color_info.green - green;
129 const int db = color_info.blue - blue;
130 const int dist = dr * dr + dg * dg + db * db;
131 if (closest > dist) {
132 closest = dist;
133 best = i;
134 }
135 }
136
138 type_ = ColorType::Palette256;
139 red_ = best;
140 } else {
141 type_ = ColorType::Palette16;
143 }
144}
145
146/// @brief Build a Color from its RGB representation.
147/// https://en.wikipedia.org/wiki/RGB_color_model
148///
149/// @param red The quantity of red [0,255]
150/// @param green The quantity of green [0,255]
151/// @param blue The quantity of blue [0,255]
152/// @ingroup screen
153// static
154Color Color::RGB(uint8_t red, uint8_t green, uint8_t blue) {
155 return RGBA(red, green, blue, 255);
156}
157
158/// @brief Build a Color from its RGBA representation.
159/// https://en.wikipedia.org/wiki/RGB_color_model
160/// @param red The quantity of red [0,255]
161/// @param green The quantity of green [0,255]
162/// @param blue The quantity of blue [0,255]
163/// @param alpha The quantity of alpha [0,255]
164/// @ingroup screen
165/// @see Color::RGB
166// static
167Color Color::RGBA(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha) {
168 return {red, green, blue, alpha};
169}
170
171/// @brief Build a Color from its HSV representation.
172/// https://en.wikipedia.org/wiki/HSL_and_HSV
173///
174/// @param h The hue of the color [0,255]
175/// @param s The "colorfulness" [0,255].
176/// @param v The "Lightness" [0,255]
177/// @param alpha The quantity of alpha [0,255]
178/// @ingroup screen
179// static
180Color Color::HSVA(uint8_t h, uint8_t s, uint8_t v, uint8_t alpha) {
181 if (s == 0) {
182 return {0, 0, 0, alpha};
183 }
184
185 uint8_t region = h / 43; // NOLINT
186 uint8_t remainder = (h - (region * 43)) * 6; // NOLINT
187 uint8_t p = (v * (255 - s)) >> 8; // NOLINT
188 uint8_t q = (v * (255 - ((s * remainder) >> 8))) >> 8; // NOLINT
189 uint8_t t = (v * (255 - ((s * (255 - remainder)) >> 8))) >> 8; // NOLINT
190
191 // clang-format off
192 switch (region) { // NOLINT
193 case 0: return Color(v,t,p, alpha); // NOLINT
194 case 1: return Color(q,v,p, alpha); // NOLINT
195 case 2: return Color(p,v,t, alpha); // NOLINT
196 case 3: return Color(p,q,v, alpha); // NOLINT
197 case 4: return Color(t,p,v, alpha); // NOLINT
198 case 5: return Color(v,p,q, alpha); // NOLINT
199 } // NOLINT
200 // clang-format on
201 return {0, 0, 0, alpha};
202}
203
204/// @brief Build a Color from its HSV representation.
205/// https://en.wikipedia.org/wiki/HSL_and_HSV
206///
207/// @param h The hue of the color [0,255]
208/// @param s The "colorfulness" [0,255].
209/// @param v The "Lightness" [0,255]
210/// @ingroup screen
211// static
212Color Color::HSV(uint8_t h, uint8_t s, uint8_t v) {
213 return HSVA(h, s, v, 255);
214}
215
216// static
217Color Color::Interpolate(float t, const Color& a, const Color& b) {
218 if (a.type_ == ColorType::Palette1 || //
219 b.type_ == ColorType::Palette1) {
220 if (t < 0.5F) { // NOLINT
221 return a;
222 } else {
223 return b;
224 }
225 }
226
227 auto get_color = [](const Color& color, //
228 uint8_t* red, uint8_t* green, uint8_t* blue) {
229 switch (color.type_) {
230 case ColorType::Palette1: {
231 return;
232 }
233
234 case ColorType::Palette16: {
235 const ColorInfo info = GetColorInfo(Color::Palette16(color.red_));
236 *red = info.red;
237 *green = info.green;
238 *blue = info.blue;
239 return;
240 }
241
242 case ColorType::Palette256: {
243 const ColorInfo info = GetColorInfo(Color::Palette256(color.red_));
244 *red = info.red;
245 *green = info.green;
246 *blue = info.blue;
247 return;
248 }
249
250 case ColorType::TrueColor:
251 default: {
252 *red = color.red_;
253 *green = color.green_;
254 *blue = color.blue_;
255 return;
256 }
257 }
258 };
259
260 uint8_t a_r = 0;
261 uint8_t a_g = 0;
262 uint8_t a_b = 0;
263 uint8_t b_r = 0;
264 uint8_t b_g = 0;
265 uint8_t b_b = 0;
266 get_color(a, &a_r, &a_g, &a_b);
267 get_color(b, &b_r, &b_g, &b_b);
268
269 // Gamma correction:
270 // https://en.wikipedia.org/wiki/Gamma_correction
271 auto interp = [t](uint8_t a_u, uint8_t b_u) {
272 constexpr float gamma = 2.2F;
273 const float a_f = powf(a_u, gamma);
274 const float b_f = powf(b_u, gamma);
275 const float c_f = a_f * (1.0F - t) + //
276 b_f * t;
277 return static_cast<uint8_t>(powf(c_f, 1.F / gamma));
278 };
279 return Color::RGB(interp(a_r, b_r), //
280 interp(a_g, b_g), //
281 interp(a_b, b_b)); //
282}
283
284
285/// @brief Blend two colors together using the alpha channel.
286//static
287Color Color::Blend(const Color& lhs, const Color& rhs) {
288 Color out = Interpolate(rhs.alpha_ / 255.F, lhs, rhs);
289 out.alpha_ = lhs.alpha_ + rhs.alpha_ - lhs.alpha_ * rhs.alpha_ / 255;
290 return out;
291}
292
293inline namespace literals {
294
295Color operator""_rgb(unsigned long long int combined) {
296 // assert(combined <= 0xffffffU);
297 auto const red = static_cast<uint8_t>(combined >> 16U);
298 auto const green = static_cast<uint8_t>(combined >> 8U);
299 auto const blue = static_cast<uint8_t>(combined);
300 return {red, green, blue};
301}
302
303} // namespace literals
304
305} // namespace ftxui
A class representing terminal colors.
Definition: color.hpp:21
Color()
Build a transparent color.
static Color HSV(uint8_t hue, uint8_t saturation, uint8_t value)
Build a Color from its HSV representation. https://en.wikipedia.org/wiki/HSL_and_HSV.
Definition: color.cpp:212
static Color RGBA(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha)
Build a Color from its RGBA representation. https://en.wikipedia.org/wiki/RGB_color_model.
Definition: color.cpp:167
static Color Blend(const Color &lhs, const Color &rhs)
Blend two colors together using the alpha channel.
Definition: color.cpp:287
bool operator!=(const Color &rhs) const
Definition: color.cpp:45
bool operator==(const Color &rhs) const
Definition: color.cpp:40
static Color RGB(uint8_t red, uint8_t green, uint8_t blue)
Build a Color from its RGB representation. https://en.wikipedia.org/wiki/RGB_color_model.
Definition: color.cpp:154
std::string Print(bool is_background_color) const
Definition: color.cpp:49
static Color Interpolate(float t, const Color &a, const Color &b)
Definition: color.cpp:217
static Color HSVA(uint8_t hue, uint8_t saturation, uint8_t value, uint8_t alpha)
Build a Color from its HSV representation. https://en.wikipedia.org/wiki/HSL_and_HSV.
Definition: color.cpp:180
Color ColorSupport()
Get the color support of the terminal.
Definition: terminal.cpp:130
std::string to_string(const std::wstring &s)
Convert a UTF8 std::string into a std::wstring.
Definition: string.cpp:1565
ColorInfo GetColorInfo(Color::Palette256 index)
Definition: color_info.cpp:272
Decorator color(Color)
Decorate using a foreground color.
Definition: color.cpp:110