1/**************************************************************************/
2/* color.cpp */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#include "color.h"
32
33#include "color_names.inc"
34#include "core/math/math_funcs.h"
35#include "core/string/ustring.h"
36#include "core/templates/rb_map.h"
37
38#include "thirdparty/misc/ok_color.h"
39
40uint32_t Color::to_argb32() const {
41 uint32_t c = (uint8_t)Math::round(a * 255.0f);
42 c <<= 8;
43 c |= (uint8_t)Math::round(r * 255.0f);
44 c <<= 8;
45 c |= (uint8_t)Math::round(g * 255.0f);
46 c <<= 8;
47 c |= (uint8_t)Math::round(b * 255.0f);
48
49 return c;
50}
51
52uint32_t Color::to_abgr32() const {
53 uint32_t c = (uint8_t)Math::round(a * 255.0f);
54 c <<= 8;
55 c |= (uint8_t)Math::round(b * 255.0f);
56 c <<= 8;
57 c |= (uint8_t)Math::round(g * 255.0f);
58 c <<= 8;
59 c |= (uint8_t)Math::round(r * 255.0f);
60
61 return c;
62}
63
64uint32_t Color::to_rgba32() const {
65 uint32_t c = (uint8_t)Math::round(r * 255.0f);
66 c <<= 8;
67 c |= (uint8_t)Math::round(g * 255.0f);
68 c <<= 8;
69 c |= (uint8_t)Math::round(b * 255.0f);
70 c <<= 8;
71 c |= (uint8_t)Math::round(a * 255.0f);
72
73 return c;
74}
75
76uint64_t Color::to_abgr64() const {
77 uint64_t c = (uint16_t)Math::round(a * 65535.0f);
78 c <<= 16;
79 c |= (uint16_t)Math::round(b * 65535.0f);
80 c <<= 16;
81 c |= (uint16_t)Math::round(g * 65535.0f);
82 c <<= 16;
83 c |= (uint16_t)Math::round(r * 65535.0f);
84
85 return c;
86}
87
88uint64_t Color::to_argb64() const {
89 uint64_t c = (uint16_t)Math::round(a * 65535.0f);
90 c <<= 16;
91 c |= (uint16_t)Math::round(r * 65535.0f);
92 c <<= 16;
93 c |= (uint16_t)Math::round(g * 65535.0f);
94 c <<= 16;
95 c |= (uint16_t)Math::round(b * 65535.0f);
96
97 return c;
98}
99
100uint64_t Color::to_rgba64() const {
101 uint64_t c = (uint16_t)Math::round(r * 65535.0f);
102 c <<= 16;
103 c |= (uint16_t)Math::round(g * 65535.0f);
104 c <<= 16;
105 c |= (uint16_t)Math::round(b * 65535.0f);
106 c <<= 16;
107 c |= (uint16_t)Math::round(a * 65535.0f);
108
109 return c;
110}
111
112String _to_hex(float p_val) {
113 int v = Math::round(p_val * 255.0f);
114 v = CLAMP(v, 0, 255);
115 String ret;
116
117 for (int i = 0; i < 2; i++) {
118 char32_t c[2] = { 0, 0 };
119 int lv = v & 0xF;
120 if (lv < 10) {
121 c[0] = '0' + lv;
122 } else {
123 c[0] = 'a' + lv - 10;
124 }
125
126 v >>= 4;
127 String cs = (const char32_t *)c;
128 ret = cs + ret;
129 }
130
131 return ret;
132}
133
134String Color::to_html(bool p_alpha) const {
135 String txt;
136 txt += _to_hex(r);
137 txt += _to_hex(g);
138 txt += _to_hex(b);
139 if (p_alpha) {
140 txt += _to_hex(a);
141 }
142 return txt;
143}
144
145float Color::get_h() const {
146 float min = MIN(r, g);
147 min = MIN(min, b);
148 float max = MAX(r, g);
149 max = MAX(max, b);
150
151 float delta = max - min;
152
153 if (delta == 0.0f) {
154 return 0.0f;
155 }
156
157 float h;
158 if (r == max) {
159 h = (g - b) / delta; // between yellow & magenta
160 } else if (g == max) {
161 h = 2 + (b - r) / delta; // between cyan & yellow
162 } else {
163 h = 4 + (r - g) / delta; // between magenta & cyan
164 }
165
166 h /= 6.0f;
167 if (h < 0.0f) {
168 h += 1.0f;
169 }
170
171 return h;
172}
173
174float Color::get_s() const {
175 float min = MIN(r, g);
176 min = MIN(min, b);
177 float max = MAX(r, g);
178 max = MAX(max, b);
179
180 float delta = max - min;
181
182 return (max != 0.0f) ? (delta / max) : 0.0f;
183}
184
185float Color::get_v() const {
186 float max = MAX(r, g);
187 max = MAX(max, b);
188 return max;
189}
190
191void Color::set_hsv(float p_h, float p_s, float p_v, float p_alpha) {
192 int i;
193 float f, p, q, t;
194 a = p_alpha;
195
196 if (p_s == 0.0f) {
197 // Achromatic (gray)
198 r = g = b = p_v;
199 return;
200 }
201
202 p_h *= 6.0f;
203 p_h = Math::fmod(p_h, 6);
204 i = Math::floor(p_h);
205
206 f = p_h - i;
207 p = p_v * (1.0f - p_s);
208 q = p_v * (1.0f - p_s * f);
209 t = p_v * (1.0f - p_s * (1.0f - f));
210
211 switch (i) {
212 case 0: // Red is the dominant color
213 r = p_v;
214 g = t;
215 b = p;
216 break;
217 case 1: // Green is the dominant color
218 r = q;
219 g = p_v;
220 b = p;
221 break;
222 case 2:
223 r = p;
224 g = p_v;
225 b = t;
226 break;
227 case 3: // Blue is the dominant color
228 r = p;
229 g = q;
230 b = p_v;
231 break;
232 case 4:
233 r = t;
234 g = p;
235 b = p_v;
236 break;
237 default: // (5) Red is the dominant color
238 r = p_v;
239 g = p;
240 b = q;
241 break;
242 }
243}
244
245void Color::set_ok_hsl(float p_h, float p_s, float p_l, float p_alpha) {
246 ok_color::HSL hsl;
247 hsl.h = p_h;
248 hsl.s = p_s;
249 hsl.l = p_l;
250 ok_color::RGB rgb = ok_color::okhsl_to_srgb(hsl);
251 Color c = Color(rgb.r, rgb.g, rgb.b, p_alpha).clamp();
252 r = c.r;
253 g = c.g;
254 b = c.b;
255 a = c.a;
256}
257
258bool Color::is_equal_approx(const Color &p_color) const {
259 return Math::is_equal_approx(r, p_color.r) && Math::is_equal_approx(g, p_color.g) && Math::is_equal_approx(b, p_color.b) && Math::is_equal_approx(a, p_color.a);
260}
261
262Color Color::clamp(const Color &p_min, const Color &p_max) const {
263 return Color(
264 CLAMP(r, p_min.r, p_max.r),
265 CLAMP(g, p_min.g, p_max.g),
266 CLAMP(b, p_min.b, p_max.b),
267 CLAMP(a, p_min.a, p_max.a));
268}
269
270void Color::invert() {
271 r = 1.0f - r;
272 g = 1.0f - g;
273 b = 1.0f - b;
274}
275
276Color Color::hex(uint32_t p_hex) {
277 float a = (p_hex & 0xFF) / 255.0f;
278 p_hex >>= 8;
279 float b = (p_hex & 0xFF) / 255.0f;
280 p_hex >>= 8;
281 float g = (p_hex & 0xFF) / 255.0f;
282 p_hex >>= 8;
283 float r = (p_hex & 0xFF) / 255.0f;
284
285 return Color(r, g, b, a);
286}
287
288Color Color::hex64(uint64_t p_hex) {
289 float a = (p_hex & 0xFFFF) / 65535.0f;
290 p_hex >>= 16;
291 float b = (p_hex & 0xFFFF) / 65535.0f;
292 p_hex >>= 16;
293 float g = (p_hex & 0xFFFF) / 65535.0f;
294 p_hex >>= 16;
295 float r = (p_hex & 0xFFFF) / 65535.0f;
296
297 return Color(r, g, b, a);
298}
299
300static int _parse_col4(const String &p_str, int p_ofs) {
301 char character = p_str[p_ofs];
302
303 if (character >= '0' && character <= '9') {
304 return character - '0';
305 } else if (character >= 'a' && character <= 'f') {
306 return character + (10 - 'a');
307 } else if (character >= 'A' && character <= 'F') {
308 return character + (10 - 'A');
309 }
310 return -1;
311}
312
313static int _parse_col8(const String &p_str, int p_ofs) {
314 return _parse_col4(p_str, p_ofs) * 16 + _parse_col4(p_str, p_ofs + 1);
315}
316
317Color Color::inverted() const {
318 Color c = *this;
319 c.invert();
320 return c;
321}
322
323Color Color::html(const String &p_rgba) {
324 String color = p_rgba;
325 if (color.length() == 0) {
326 return Color();
327 }
328 if (color[0] == '#') {
329 color = color.substr(1);
330 }
331
332 // If enabled, use 1 hex digit per channel instead of 2.
333 // Other sizes aren't in the HTML/CSS spec but we could add them if desired.
334 bool is_shorthand = color.length() < 5;
335 bool alpha = false;
336
337 if (color.length() == 8) {
338 alpha = true;
339 } else if (color.length() == 6) {
340 alpha = false;
341 } else if (color.length() == 4) {
342 alpha = true;
343 } else if (color.length() == 3) {
344 alpha = false;
345 } else {
346 ERR_FAIL_V_MSG(Color(), "Invalid color code: " + p_rgba + ".");
347 }
348
349 float r, g, b, a = 1.0f;
350 if (is_shorthand) {
351 r = _parse_col4(color, 0) / 15.0f;
352 g = _parse_col4(color, 1) / 15.0f;
353 b = _parse_col4(color, 2) / 15.0f;
354 if (alpha) {
355 a = _parse_col4(color, 3) / 15.0f;
356 }
357 } else {
358 r = _parse_col8(color, 0) / 255.0f;
359 g = _parse_col8(color, 2) / 255.0f;
360 b = _parse_col8(color, 4) / 255.0f;
361 if (alpha) {
362 a = _parse_col8(color, 6) / 255.0f;
363 }
364 }
365 ERR_FAIL_COND_V_MSG(r < 0.0f, Color(), "Invalid color code: " + p_rgba + ".");
366 ERR_FAIL_COND_V_MSG(g < 0.0f, Color(), "Invalid color code: " + p_rgba + ".");
367 ERR_FAIL_COND_V_MSG(b < 0.0f, Color(), "Invalid color code: " + p_rgba + ".");
368 ERR_FAIL_COND_V_MSG(a < 0.0f, Color(), "Invalid color code: " + p_rgba + ".");
369
370 return Color(r, g, b, a);
371}
372
373bool Color::html_is_valid(const String &p_color) {
374 String color = p_color;
375
376 if (color.length() == 0) {
377 return false;
378 }
379 if (color[0] == '#') {
380 color = color.substr(1);
381 }
382
383 // Check if the amount of hex digits is valid.
384 int len = color.length();
385 if (!(len == 3 || len == 4 || len == 6 || len == 8)) {
386 return false;
387 }
388
389 // Check if each hex digit is valid.
390 for (int i = 0; i < len; i++) {
391 if (_parse_col4(color, i) == -1) {
392 return false;
393 }
394 }
395
396 return true;
397}
398
399Color Color::named(const String &p_name) {
400 int idx = find_named_color(p_name);
401 if (idx == -1) {
402 ERR_FAIL_V_MSG(Color(), "Invalid color name: " + p_name + ".");
403 }
404 return named_colors[idx].color;
405}
406
407Color Color::named(const String &p_name, const Color &p_default) {
408 int idx = find_named_color(p_name);
409 if (idx == -1) {
410 return p_default;
411 }
412 return named_colors[idx].color;
413}
414
415int Color::find_named_color(const String &p_name) {
416 String name = p_name;
417 // Normalize name
418 name = name.replace(" ", "");
419 name = name.replace("-", "");
420 name = name.replace("_", "");
421 name = name.replace("'", "");
422 name = name.replace(".", "");
423 name = name.to_upper();
424
425 int idx = 0;
426 while (named_colors[idx].name != nullptr) {
427 if (name == String(named_colors[idx].name).replace("_", "")) {
428 return idx;
429 }
430 idx++;
431 }
432
433 return -1;
434}
435
436int Color::get_named_color_count() {
437 int idx = 0;
438 while (named_colors[idx].name != nullptr) {
439 idx++;
440 }
441 return idx;
442}
443
444String Color::get_named_color_name(int p_idx) {
445 ERR_FAIL_INDEX_V(p_idx, get_named_color_count(), "");
446 return named_colors[p_idx].name;
447}
448
449Color Color::get_named_color(int p_idx) {
450 ERR_FAIL_INDEX_V(p_idx, get_named_color_count(), Color());
451 return named_colors[p_idx].color;
452}
453
454// For a version that errors on invalid values instead of returning
455// a default color, use the Color(String) constructor instead.
456Color Color::from_string(const String &p_string, const Color &p_default) {
457 if (html_is_valid(p_string)) {
458 return html(p_string);
459 } else {
460 return named(p_string, p_default);
461 }
462}
463
464Color Color::from_hsv(float p_h, float p_s, float p_v, float p_alpha) {
465 Color c;
466 c.set_hsv(p_h, p_s, p_v, p_alpha);
467 return c;
468}
469
470Color Color::from_rgbe9995(uint32_t p_rgbe) {
471 float r = p_rgbe & 0x1ff;
472 float g = (p_rgbe >> 9) & 0x1ff;
473 float b = (p_rgbe >> 18) & 0x1ff;
474 float e = (p_rgbe >> 27);
475 float m = Math::pow(2.0f, e - 15.0f - 9.0f);
476
477 float rd = r * m;
478 float gd = g * m;
479 float bd = b * m;
480
481 return Color(rd, gd, bd, 1.0f);
482}
483
484Color::operator String() const {
485 return "(" + String::num(r, 4) + ", " + String::num(g, 4) + ", " + String::num(b, 4) + ", " + String::num(a, 4) + ")";
486}
487
488Color Color::operator+(const Color &p_color) const {
489 return Color(
490 r + p_color.r,
491 g + p_color.g,
492 b + p_color.b,
493 a + p_color.a);
494}
495
496void Color::operator+=(const Color &p_color) {
497 r = r + p_color.r;
498 g = g + p_color.g;
499 b = b + p_color.b;
500 a = a + p_color.a;
501}
502
503Color Color::operator-(const Color &p_color) const {
504 return Color(
505 r - p_color.r,
506 g - p_color.g,
507 b - p_color.b,
508 a - p_color.a);
509}
510
511void Color::operator-=(const Color &p_color) {
512 r = r - p_color.r;
513 g = g - p_color.g;
514 b = b - p_color.b;
515 a = a - p_color.a;
516}
517
518Color Color::operator*(const Color &p_color) const {
519 return Color(
520 r * p_color.r,
521 g * p_color.g,
522 b * p_color.b,
523 a * p_color.a);
524}
525
526Color Color::operator*(float p_scalar) const {
527 return Color(
528 r * p_scalar,
529 g * p_scalar,
530 b * p_scalar,
531 a * p_scalar);
532}
533
534void Color::operator*=(const Color &p_color) {
535 r = r * p_color.r;
536 g = g * p_color.g;
537 b = b * p_color.b;
538 a = a * p_color.a;
539}
540
541void Color::operator*=(float p_scalar) {
542 r = r * p_scalar;
543 g = g * p_scalar;
544 b = b * p_scalar;
545 a = a * p_scalar;
546}
547
548Color Color::operator/(const Color &p_color) const {
549 return Color(
550 r / p_color.r,
551 g / p_color.g,
552 b / p_color.b,
553 a / p_color.a);
554}
555
556Color Color::operator/(float p_scalar) const {
557 return Color(
558 r / p_scalar,
559 g / p_scalar,
560 b / p_scalar,
561 a / p_scalar);
562}
563
564void Color::operator/=(const Color &p_color) {
565 r = r / p_color.r;
566 g = g / p_color.g;
567 b = b / p_color.b;
568 a = a / p_color.a;
569}
570
571void Color::operator/=(float p_scalar) {
572 r = r / p_scalar;
573 g = g / p_scalar;
574 b = b / p_scalar;
575 a = a / p_scalar;
576}
577
578Color Color::operator-() const {
579 return Color(
580 1.0f - r,
581 1.0f - g,
582 1.0f - b,
583 1.0f - a);
584}
585
586Color Color::from_ok_hsl(float p_h, float p_s, float p_l, float p_alpha) {
587 Color c;
588 c.set_ok_hsl(p_h, p_s, p_l, p_alpha);
589 return c;
590}
591
592float Color::get_ok_hsl_h() const {
593 ok_color::RGB rgb;
594 rgb.r = r;
595 rgb.g = g;
596 rgb.b = b;
597 ok_color::HSL ok_hsl = ok_color::srgb_to_okhsl(rgb);
598 if (Math::is_nan(ok_hsl.h)) {
599 return 0.0f;
600 }
601 return CLAMP(ok_hsl.h, 0.0f, 1.0f);
602}
603
604float Color::get_ok_hsl_s() const {
605 ok_color::RGB rgb;
606 rgb.r = r;
607 rgb.g = g;
608 rgb.b = b;
609 ok_color::HSL ok_hsl = ok_color::srgb_to_okhsl(rgb);
610 if (Math::is_nan(ok_hsl.s)) {
611 return 0.0f;
612 }
613 return CLAMP(ok_hsl.s, 0.0f, 1.0f);
614}
615
616float Color::get_ok_hsl_l() const {
617 ok_color::RGB rgb;
618 rgb.r = r;
619 rgb.g = g;
620 rgb.b = b;
621 ok_color::HSL ok_hsl = ok_color::srgb_to_okhsl(rgb);
622 if (Math::is_nan(ok_hsl.l)) {
623 return 0.0f;
624 }
625 return CLAMP(ok_hsl.l, 0.0f, 1.0f);
626}
627