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