FTXUI 7.1.0
C++ functional terminal UI.
Loading...
Searching...
No Matches
screen/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 <cstdio> // for snprintf
10#include <string>
11
12#include "ftxui/screen/color_info.hpp" // for GetColorInfo, ColorInfo
13#include "ftxui/screen/terminal.hpp" // for ColorSupport, Color, Palette256, TrueColor
14
15namespace ftxui {
16namespace {
17const std::array<const char*, 32> palette16code = {
18 "30", "40", //
19 "31", "41", //
20 "32", "42", //
21 "33", "43", //
22 "34", "44", //
23 "35", "45", //
24 "36", "46", //
25 "37", "47", //
26 "90", "100", //
27 "91", "101", //
28 "92", "102", //
29 "93", "103", //
30 "94", "104", //
31 "95", "105", //
32 "96", "106", //
33 "97", "107", //
34};
35
36void AppendNumber(std::string& out, uint8_t n) {
37 if (n >= 100) {
38 out += static_cast<char>('0' + n / 100);
39 n %= 100;
40 out += static_cast<char>('0' + n / 10);
41 n %= 10;
42 out += static_cast<char>('0' + n);
43 } else if (n >= 10) {
44 out += static_cast<char>('0' + n / 10);
45 n %= 10;
46 out += static_cast<char>('0' + n);
47 } else {
48 out += static_cast<char>('0' + n);
49 }
50}
51
52} // namespace
53
54bool Color::operator==(const Color& rhs) const {
55 return red_ == rhs.red_ && green_ == rhs.green_ && blue_ == rhs.blue_ &&
56 type_ == rhs.type_;
57}
58
59bool Color::operator!=(const Color& rhs) const {
60 return !operator==(rhs);
61}
62
63std::string Color::Print(bool is_background_color) const {
64 std::string out;
65 PrintTo(out, is_background_color);
66 return out;
67}
68
69/// @brief Append the ANSI color code to a string (zero-allocation fast path).
70/// @param out The string to append to.
71/// @param is_background_color Whether this is a background color code.
72void Color::PrintTo(std::string& out, bool is_background_color) const {
73 switch (type_) {
74 case ColorType::Palette1:
75 out.append(is_background_color ? "49" : "39", 2);
76 return;
77 case ColorType::Palette16:
78 out.append(palette16code[2 * red_ + (is_background_color ? 1 : 0)]);
79 return;
80 case ColorType::Palette256:
81 out.append(is_background_color ? "48;5;" : "38;5;", 5);
82 AppendNumber(out, red_);
83 return;
84 case ColorType::TrueColor:
85 out.append(is_background_color ? "48;2;" : "38;2;", 5);
86 AppendNumber(out, red_);
87 out += ';';
88 AppendNumber(out, green_);
89 out += ';';
90 AppendNumber(out, blue_);
91 return;
92 }
93}
94
95/// @brief Build a transparent color.
96Color::Color() = default;
97
98/// @brief Build a transparent color.
99Color::Color(Palette1 /*value*/) : Color() {}
100
101/// @brief Build a color using the Palette16 colors.
102Color::Color(Palette16 index)
103 : type_(ColorType::Palette16), red_(index), alpha_(255) {
104 if (Terminal::ColorSupport() == Terminal::Color::Palette1) {
105 type_ = ColorType::Palette1;
106 }
107}
108
109/// @brief Build a color using Palette256 colors.
110Color::Color(Palette256 index)
111 : type_(ColorType::Palette256), red_(index), alpha_(255) {
112 if (Terminal::ColorSupport() >= Terminal::Color::Palette256) {
113 return;
114 }
115 if (Terminal::ColorSupport() == Terminal::Color::Palette1) {
116 type_ = ColorType::Palette1;
117 return;
118 }
119 type_ = ColorType::Palette16;
120 red_ = GetColorInfo(Color::Palette256(red_)).index_16;
121}
122
123/// @brief Build a Color from its RGB representation.
124/// https://en.wikipedia.org/wiki/RGB_color_model
125///
126/// @param red The quantity of red [0,255]
127/// @param green The quantity of green [0,255]
128/// @param blue The quantity of blue [0,255]
129/// @param alpha The quantity of alpha [0,255]
130Color::Color(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha)
131 : type_(ColorType::TrueColor),
132 red_(red),
133 green_(green),
134 blue_(blue),
135 alpha_(alpha) {
136 if (Terminal::ColorSupport() == Terminal::Color::TrueColor) {
137 return;
138 }
139 if (Terminal::ColorSupport() == Terminal::Color::Palette1) {
140 type_ = ColorType::Palette1;
141 return;
142 }
143
144 // Find the closest Color from the database:
145 const int max_distance = 256 * 256 * 3;
146 int closest = max_distance;
147 int best = 0;
148 const int database_begin = 16;
149 const int database_end = 256;
150 for (int i = database_begin; i < database_end; ++i) {
151 const ColorInfo color_info = GetColorInfo(Color::Palette256(i));
152 const int dr = color_info.red - red;
153 const int dg = color_info.green - green;
154 const int db = color_info.blue - blue;
155 const int dist = dr * dr + dg * dg + db * db;
156 if (closest > dist) {
157 closest = dist;
158 best = i;
159 }
160 }
161
162 if (Terminal::ColorSupport() == Terminal::Color::Palette256) {
163 type_ = ColorType::Palette256;
164 red_ = best;
165 } else {
166 type_ = ColorType::Palette16;
167 red_ = GetColorInfo(Color::Palette256(best)).index_16;
168 }
169}
170
171/// @brief Build a Color from its RGB representation.
172/// https://en.wikipedia.org/wiki/RGB_color_model
173///
174/// @param red The quantity of red [0,255]
175/// @param green The quantity of green [0,255]
176/// @param blue The quantity of blue [0,255]
177// static
178Color Color::RGB(uint8_t red, uint8_t green, uint8_t blue) {
179 return RGBA(red, green, blue, 255);
180}
181
182/// @brief Build a Color from its RGBA representation.
183/// https://en.wikipedia.org/wiki/RGB_color_model
184/// @param red The quantity of red [0,255]
185/// @param green The quantity of green [0,255]
186/// @param blue The quantity of blue [0,255]
187/// @param alpha The quantity of alpha [0,255]
188/// @see Color::RGB
189// static
190Color Color::RGBA(uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha) {
191 return {red, green, blue, alpha};
192}
193
194/// @brief Build a Color from its HSV representation.
195/// https://en.wikipedia.org/wiki/HSL_and_HSV
196///
197/// @param h The hue of the color [0,255]
198/// @param s The "colorfulness" [0,255].
199/// @param v The "Lightness" [0,255]
200/// @param alpha The quantity of alpha [0,255]
201// static
202Color Color::HSVA(uint8_t h, uint8_t s, uint8_t v, uint8_t alpha) {
203 uint8_t region = h / 43; // NOLINT
204 uint8_t remainder = (h - (region * 43)) * 6; // NOLINT
205 uint8_t p = (v * (255 - s)) >> 8; // NOLINT
206 uint8_t q = (v * (255 - ((s * remainder) >> 8))) >> 8; // NOLINT
207 uint8_t t = (v * (255 - ((s * (255 - remainder)) >> 8))) >> 8; // NOLINT
208
209 // clang-format off
210 switch (region) { // NOLINT
211 case 0: return Color(v,t,p, alpha); // NOLINT
212 case 1: return Color(q,v,p, alpha); // NOLINT
213 case 2: return Color(p,v,t, alpha); // NOLINT
214 case 3: return Color(p,q,v, alpha); // NOLINT
215 case 4: return Color(t,p,v, alpha); // NOLINT
216 case 5: return Color(v,p,q, alpha); // NOLINT
217 } // NOLINT
218 // clang-format on
219 return {0, 0, 0, alpha};
220}
221
222/// @brief Build a Color from its HSV representation.
223/// https://en.wikipedia.org/wiki/HSL_and_HSV
224///
225/// @param h The hue of the color [0,255]
226/// @param s The "colorfulness" [0,255].
227/// @param v The "Lightness" [0,255]
228// static
229Color Color::HSV(uint8_t h, uint8_t s, uint8_t v) {
230 return HSVA(h, s, v, 255);
231}
232
233// static
234Color Color::Interpolate(float t, const Color& a, const Color& b) {
235 if (a.type_ == ColorType::Palette1 || //
236 b.type_ == ColorType::Palette1) {
237 if (t < 0.5F) { // NOLINT
238 return a;
239 } else {
240 return b;
241 }
242 }
243
244 // Gamma correction:
245 // https://en.wikipedia.org/wiki/Gamma_correction
246 auto interp = [t](uint8_t a_u, uint8_t b_u) {
247 constexpr float gamma = 2.2F;
248 const float a_f = std::pow(a_u, gamma);
249 const float b_f = std::pow(b_u, gamma);
250 const float c_f = a_f * (1.0F - t) + //
251 b_f * t;
252 return static_cast<uint8_t>(std::pow(c_f, 1.F / gamma));
253 };
254 return Color::RGB(interp(a.GetRed(), b.GetRed()), //
255 interp(a.GetGreen(), b.GetGreen()), //
256 interp(a.GetBlue(), b.GetBlue())); //
257}
258
259/// @brief The red component of the color [0,255].
260/// Palette colors are resolved to their RGB approximation. Returns 0 for the
261/// default (transparent) color.
262uint8_t Color::GetRed() const {
263 switch (type_) {
264 case ColorType::Palette1:
265 return 0;
266 case ColorType::Palette16:
267 case ColorType::Palette256:
268 return GetColorInfo(Palette256(red_)).red;
269 case ColorType::TrueColor:
270 return red_;
271 }
272 return 0;
273}
274
275/// @brief The green component of the color [0,255].
276/// Palette colors are resolved to their RGB approximation. Returns 0 for the
277/// default (transparent) color.
278uint8_t Color::GetGreen() const {
279 switch (type_) {
280 case ColorType::Palette1:
281 return 0;
282 case ColorType::Palette16:
283 case ColorType::Palette256:
284 return GetColorInfo(Palette256(red_)).green;
285 case ColorType::TrueColor:
286 return green_;
287 }
288 return 0;
289}
290
291/// @brief The blue component of the color [0,255].
292/// Palette colors are resolved to their RGB approximation. Returns 0 for the
293/// default (transparent) color.
294uint8_t Color::GetBlue() const {
295 switch (type_) {
296 case ColorType::Palette1:
297 return 0;
298 case ColorType::Palette16:
299 case ColorType::Palette256:
300 return GetColorInfo(Palette256(red_)).blue;
301 case ColorType::TrueColor:
302 return blue_;
303 }
304 return 0;
305}
306
307/// @brief The alpha component of the color [0,255]. 0 is fully transparent.
308uint8_t Color::GetAlpha() const {
309 return alpha_;
310}
311
312/// @brief Blend two colors together using the alpha channel.
313// static
314Color Color::Blend(const Color& lhs, const Color& rhs) {
315 Color out = Interpolate(float(rhs.alpha_) / 255.F, lhs, rhs);
316 out.alpha_ = lhs.alpha_ + rhs.alpha_ - lhs.alpha_ * rhs.alpha_ / 255;
317 return out;
318}
319
320inline namespace literals {
321
322Color operator""_rgb(unsigned long long int combined) {
323 // assert(combined <= 0xffffffU);
324 auto const red = static_cast<uint8_t>(combined >> 16U);
325 auto const green = static_cast<uint8_t>(combined >> 8U);
326 auto const blue = static_cast<uint8_t>(combined);
327 return {red, green, blue};
328}
329
330} // namespace literals
331
332} // namespace ftxui
The FTXUI ftxui:: namespace.
Definition animation.hpp:11
The FTXUI ftxui::literals:: namespace.