1 | // Aseprite |
2 | // Copyright (C) 2020-2022 Igara Studio S.A. |
3 | // Copyright (C) 2001-2018 David Capello |
4 | // |
5 | // This program is distributed under the terms of |
6 | // the End-User License Agreement for Aseprite. |
7 | |
8 | #ifdef HAVE_CONFIG_H |
9 | #include "config.h" |
10 | #endif |
11 | |
12 | #include "app/color.h" |
13 | |
14 | #include "app/color_utils.h" |
15 | #include "app/modules/palettes.h" |
16 | #include "base/debug.h" |
17 | #include "doc/image.h" |
18 | #include "doc/palette.h" |
19 | #include "doc/primitives.h" |
20 | #include "doc/tile.h" |
21 | #include "gfx/hsl.h" |
22 | #include "gfx/hsv.h" |
23 | #include "gfx/rgb.h" |
24 | |
25 | #include <cmath> |
26 | #include <cstdlib> |
27 | #include <iomanip> |
28 | #include <sstream> |
29 | #include <string> |
30 | |
31 | namespace app { |
32 | |
33 | using namespace gfx; |
34 | |
35 | // static |
36 | Color Color::fromMask() |
37 | { |
38 | return Color(Color::MaskType); |
39 | } |
40 | |
41 | // static |
42 | Color Color::fromRgb(int r, int g, int b, int a) |
43 | { |
44 | Color color(Color::RgbType); |
45 | color.m_value.rgb.r = r; |
46 | color.m_value.rgb.g = g; |
47 | color.m_value.rgb.b = b; |
48 | color.m_value.rgb.a = a; |
49 | return color; |
50 | } |
51 | |
52 | // static |
53 | Color Color::fromHsv(double h, double s, double v, int a) |
54 | { |
55 | Color color(Color::HsvType); |
56 | color.m_value.hsv.h = h; |
57 | color.m_value.hsv.s = s; |
58 | color.m_value.hsv.v = v; |
59 | color.m_value.hsv.a = a; |
60 | return color; |
61 | } |
62 | |
63 | // static |
64 | Color Color::fromHsl(double h, double s, double l, int a) |
65 | { |
66 | Color color(Color::HslType); |
67 | color.m_value.hsl.h = h; |
68 | color.m_value.hsl.s = s; |
69 | color.m_value.hsl.l = l; |
70 | color.m_value.hsl.a = a; |
71 | return color; |
72 | } |
73 | |
74 | // static |
75 | Color Color::fromGray(int g, int a) |
76 | { |
77 | Color color(Color::GrayType); |
78 | color.m_value.gray.g = g; |
79 | color.m_value.gray.a = a; |
80 | return color; |
81 | } |
82 | |
83 | // static |
84 | Color Color::fromIndex(int index) |
85 | { |
86 | ASSERT(index >= 0 || index == doc::notile); |
87 | |
88 | Color color(Color::IndexType); |
89 | color.m_value.index = index; |
90 | return color; |
91 | } |
92 | |
93 | // static |
94 | Color Color::fromImage(PixelFormat pixelFormat, color_t c) |
95 | { |
96 | Color color = app::Color::fromMask(); |
97 | |
98 | switch (pixelFormat) { |
99 | |
100 | case IMAGE_RGB: |
101 | if (rgba_geta(c) > 0) { |
102 | color = Color::fromRgb(rgba_getr(c), |
103 | rgba_getg(c), |
104 | rgba_getb(c), |
105 | rgba_geta(c)); |
106 | } |
107 | break; |
108 | |
109 | case IMAGE_GRAYSCALE: |
110 | if (graya_geta(c) > 0) { |
111 | color = Color::fromGray(graya_getv(c), |
112 | graya_geta(c)); |
113 | } |
114 | break; |
115 | |
116 | case IMAGE_INDEXED: |
117 | case IMAGE_TILEMAP: |
118 | color = Color::fromIndex(c); |
119 | break; |
120 | } |
121 | |
122 | return color; |
123 | } |
124 | |
125 | // static |
126 | Color Color::fromImageGetPixel(Image *image, int x, int y) |
127 | { |
128 | if ((x >= 0) && (y >= 0) && (x < image->width()) && (y < image->height())) |
129 | return Color::fromImage(image->pixelFormat(), doc::get_pixel(image, x, y)); |
130 | else |
131 | return Color::fromMask(); |
132 | } |
133 | |
134 | // static |
135 | Color Color::fromString(const std::string& str) |
136 | { |
137 | Color color = app::Color::fromMask(); |
138 | |
139 | if (str != "mask" ) { |
140 | if (str.find("rgb{" ) == 0 || |
141 | str.find("hsv{" ) == 0 || |
142 | str.find("hsl{" ) == 0 || |
143 | str.find("gray{" ) == 0) { |
144 | int c = 0; |
145 | double table[4] = { 0.0, 0.0, 0.0, 255.0 }; |
146 | std::string::size_type i = str.find_first_of('{')+1, j; |
147 | |
148 | while ((j = str.find_first_of(",}" , i)) != std::string::npos) { |
149 | std::string element = str.substr(i, j - i); |
150 | if (c < 4) |
151 | table[c++] = std::strtod(element.c_str(), NULL); |
152 | if (c >= 4) |
153 | break; |
154 | i = j+1; |
155 | } |
156 | |
157 | if (str[0] == 'r') |
158 | color = Color::fromRgb(int(table[0]), int(table[1]), int(table[2]), int(table[3])); |
159 | else if (str[0] == 'h' && str[1] == 's' && str[2] == 'v') |
160 | color = Color::fromHsv(table[0], |
161 | table[1] / 100.0, |
162 | table[2] / 100.0, |
163 | int(table[3])); |
164 | else if (str[0] == 'h' && str[1] == 's' && str[2] == 'l') |
165 | color = Color::fromHsl(table[0], |
166 | table[1] / 100.0, |
167 | table[2] / 100.0, |
168 | int(table[3])); |
169 | else if (str[0] == 'g') |
170 | color = Color::fromGray(int(table[0]), (c >= 2 ? int(table[1]): 255)); |
171 | } |
172 | else if (str.find("index{" ) == 0) { |
173 | color = Color::fromIndex(std::strtol(str.c_str()+6, NULL, 10)); |
174 | } |
175 | } |
176 | |
177 | return color; |
178 | } |
179 | |
180 | Color Color::toRgb() const |
181 | { |
182 | return Color::fromRgb(getRed(), getGreen(), getBlue(), getAlpha()); |
183 | } |
184 | |
185 | std::string Color::toString() const |
186 | { |
187 | std::stringstream result; |
188 | |
189 | switch (getType()) { |
190 | |
191 | case Color::MaskType: |
192 | result << "mask" ; |
193 | break; |
194 | |
195 | case Color::RgbType: |
196 | result << "rgb{" |
197 | << m_value.rgb.r << "," |
198 | << m_value.rgb.g << "," |
199 | << m_value.rgb.b << "," |
200 | << m_value.rgb.a << "}" ; |
201 | break; |
202 | |
203 | case Color::HsvType: |
204 | result << "hsv{" |
205 | << std::setprecision(2) |
206 | << std::fixed |
207 | << m_value.hsv.h << "," |
208 | << std::clamp(m_value.hsv.s*100.0, 0.0, 100.0) << "," |
209 | << std::clamp(m_value.hsv.v*100.0, 0.0, 100.0) << "," |
210 | << m_value.hsv.a << "}" ; |
211 | break; |
212 | |
213 | case Color::HslType: |
214 | result << "hsl{" |
215 | << std::setprecision(2) |
216 | << std::fixed |
217 | << m_value.hsl.h << "," |
218 | << std::clamp(m_value.hsl.s*100.0, 0.0, 100.0) << "," |
219 | << std::clamp(m_value.hsl.l*100.0, 0.0, 100.0) << "," |
220 | << m_value.hsl.a << "}" ; |
221 | break; |
222 | |
223 | case Color::GrayType: |
224 | result << "gray{" |
225 | << m_value.gray.g << "," |
226 | << m_value.gray.a << "}" ; |
227 | break; |
228 | |
229 | case Color::IndexType: |
230 | result << "index{" << m_value.index << "}" ; |
231 | break; |
232 | |
233 | } |
234 | |
235 | return result.str(); |
236 | } |
237 | |
238 | std::string Color::toHumanReadableString(PixelFormat pixelFormat, HumanReadableString humanReadable) const |
239 | { |
240 | std::stringstream result; |
241 | |
242 | if (humanReadable == LongHumanReadableString) { |
243 | switch (getType()) { |
244 | |
245 | case Color::MaskType: |
246 | result << "Mask" ; |
247 | break; |
248 | |
249 | case Color::RgbType: |
250 | if (pixelFormat == IMAGE_GRAYSCALE) { |
251 | result << "Gray " << getGray(); |
252 | } |
253 | else { |
254 | result << "RGB " |
255 | << m_value.rgb.r << " " |
256 | << m_value.rgb.g << " " |
257 | << m_value.rgb.b; |
258 | |
259 | if (pixelFormat == IMAGE_INDEXED) |
260 | result << " Index " |
261 | << color_utils::color_for_image(*this, IMAGE_INDEXED); |
262 | } |
263 | break; |
264 | |
265 | case Color::HsvType: |
266 | if (pixelFormat == IMAGE_GRAYSCALE) { |
267 | result << "Gray " << getGray(); |
268 | } |
269 | else { |
270 | result << "HSV " |
271 | << int(m_value.hsv.h) << "\xc2\xb0 " |
272 | << std::clamp(int(m_value.hsv.s*100.0), 0, 100) << "% " |
273 | << std::clamp(int(m_value.hsv.v*100.0), 0, 100) << "%" ; |
274 | |
275 | if (pixelFormat == IMAGE_INDEXED) |
276 | result << " Index " << color_utils::color_for_image(*this, IMAGE_INDEXED); |
277 | |
278 | result << " (RGB " |
279 | << getRed() << " " |
280 | << getGreen() << " " |
281 | << getBlue() << ")" ; |
282 | } |
283 | break; |
284 | |
285 | case Color::HslType: |
286 | if (pixelFormat == IMAGE_GRAYSCALE) { |
287 | result << "Gray " << getGray(); |
288 | } |
289 | else { |
290 | result << "HSL " |
291 | << int(m_value.hsl.h) << "\xc2\xb0 " |
292 | << std::clamp(int(m_value.hsl.s*100.0), 0, 100) << "% " |
293 | << std::clamp(int(m_value.hsl.l*100.0), 0, 100) << "%" ; |
294 | |
295 | if (pixelFormat == IMAGE_INDEXED) |
296 | result << " Index " << color_utils::color_for_image(*this, IMAGE_INDEXED); |
297 | |
298 | result << " (RGB " |
299 | << getRed() << " " |
300 | << getGreen() << " " |
301 | << getBlue() << ")" ; |
302 | } |
303 | break; |
304 | |
305 | case Color::GrayType: |
306 | result << "Gray " << m_value.gray.g; |
307 | break; |
308 | |
309 | case Color::IndexType: { |
310 | int i = m_value.index; |
311 | if (i >= 0 && i < (int)get_current_palette()->size()) { |
312 | uint32_t _c = get_current_palette()->getEntry(i); |
313 | result << "Index " << i |
314 | << " (RGB " |
315 | << (int)rgba_getr(_c) << " " |
316 | << (int)rgba_getg(_c) << " " |
317 | << (int)rgba_getb(_c) << ")" ; |
318 | } |
319 | else { |
320 | result << "Index " |
321 | << i |
322 | << " (out of range)" ; |
323 | } |
324 | break; |
325 | } |
326 | |
327 | default: |
328 | ASSERT(false); |
329 | break; |
330 | } |
331 | |
332 | result << " #" << std::hex << std::setfill('0') |
333 | << std::setw(2) << getRed() |
334 | << std::setw(2) << getGreen() |
335 | << std::setw(2) << getBlue(); |
336 | } |
337 | else if (humanReadable == ShortHumanReadableString) { |
338 | switch (getType()) { |
339 | |
340 | case Color::MaskType: |
341 | result << "Mask" ; |
342 | break; |
343 | |
344 | case Color::RgbType: |
345 | if (pixelFormat == IMAGE_GRAYSCALE) { |
346 | result << "Gry-" << getGray(); |
347 | } |
348 | else { |
349 | result << "#" << std::hex << std::setfill('0') |
350 | << std::setw(2) << m_value.rgb.r |
351 | << std::setw(2) << m_value.rgb.g |
352 | << std::setw(2) << m_value.rgb.b; |
353 | } |
354 | break; |
355 | |
356 | case Color::HsvType: |
357 | if (pixelFormat == IMAGE_GRAYSCALE) { |
358 | result << "Gry-" << getGray(); |
359 | } |
360 | else { |
361 | result << int(m_value.hsv.h) << "\xc2\xb0" |
362 | << std::clamp(int(m_value.hsv.s*100.0), 0, 100) << "," |
363 | << std::clamp(int(m_value.hsv.v*100.0), 0, 100); |
364 | } |
365 | break; |
366 | |
367 | case Color::HslType: |
368 | if (pixelFormat == IMAGE_GRAYSCALE) { |
369 | result << "Gry-" << getGray(); |
370 | } |
371 | else { |
372 | result << int(m_value.hsl.h) << "\xc2\xb0" |
373 | << std::clamp(int(m_value.hsl.s*100.0), 0, 100) << "," |
374 | << std::clamp(int(m_value.hsl.l*100.0), 0, 100); |
375 | } |
376 | break; |
377 | |
378 | case Color::GrayType: |
379 | result << "Gry-" << m_value.gray.g; |
380 | break; |
381 | |
382 | case Color::IndexType: |
383 | result << "Idx-" << m_value.index; |
384 | break; |
385 | |
386 | default: |
387 | ASSERT(false); |
388 | break; |
389 | } |
390 | } |
391 | |
392 | return result.str(); |
393 | } |
394 | |
395 | bool Color::operator==(const Color& other) const |
396 | { |
397 | if (getType() != other.getType()) |
398 | return false; |
399 | |
400 | switch (getType()) { |
401 | |
402 | case Color::MaskType: |
403 | return true; |
404 | |
405 | case Color::RgbType: |
406 | return |
407 | m_value.rgb.r == other.m_value.rgb.r && |
408 | m_value.rgb.g == other.m_value.rgb.g && |
409 | m_value.rgb.b == other.m_value.rgb.b && |
410 | m_value.rgb.a == other.m_value.rgb.a; |
411 | |
412 | case Color::HsvType: |
413 | return |
414 | (std::fabs(m_value.hsv.h - other.m_value.hsv.h) < 0.001) && |
415 | (std::fabs(m_value.hsv.s - other.m_value.hsv.s) < 0.00001) && |
416 | (std::fabs(m_value.hsv.v - other.m_value.hsv.v) < 0.00001) && |
417 | (m_value.hsv.a == other.m_value.hsv.a); |
418 | |
419 | case Color::HslType: |
420 | return |
421 | (std::fabs(m_value.hsl.h - other.m_value.hsl.h) < 0.001) && |
422 | (std::fabs(m_value.hsl.s - other.m_value.hsl.s) < 0.00001) && |
423 | (std::fabs(m_value.hsl.l - other.m_value.hsl.l) < 0.00001) && |
424 | (m_value.hsl.a == other.m_value.hsl.a); |
425 | |
426 | case Color::GrayType: |
427 | return |
428 | m_value.gray.g == other.m_value.gray.g && |
429 | m_value.gray.a == other.m_value.gray.a; |
430 | |
431 | case Color::IndexType: |
432 | return m_value.index == other.m_value.index; |
433 | |
434 | default: |
435 | ASSERT(false); |
436 | return false; |
437 | } |
438 | } |
439 | |
440 | // Returns false only if the color is a index and it is outside the |
441 | // valid range (outside the maximum number of colors in the current |
442 | // palette) |
443 | bool Color::isValid() const |
444 | { |
445 | switch (getType()) { |
446 | |
447 | case Color::IndexType: { |
448 | int i = m_value.index; |
449 | return (i >= 0 && i < get_current_palette()->size()); |
450 | } |
451 | |
452 | } |
453 | return true; |
454 | } |
455 | |
456 | int Color::getRed() const |
457 | { |
458 | switch (getType()) { |
459 | |
460 | case Color::MaskType: |
461 | return 0; |
462 | |
463 | case Color::RgbType: |
464 | return m_value.rgb.r; |
465 | |
466 | case Color::HsvType: |
467 | return Rgb(Hsv(m_value.hsv.h, |
468 | m_value.hsv.s, |
469 | m_value.hsv.v)).red(); |
470 | |
471 | case Color::HslType: |
472 | return Rgb(Hsl(m_value.hsl.h, |
473 | m_value.hsl.s, |
474 | m_value.hsl.l)).red(); |
475 | |
476 | case Color::GrayType: |
477 | return m_value.gray.g; |
478 | |
479 | case Color::IndexType: { |
480 | int i = m_value.index; |
481 | if (i >= 0 && i < get_current_palette()->size()) |
482 | return rgba_getr(get_current_palette()->getEntry(i)); |
483 | else |
484 | return 0; |
485 | } |
486 | |
487 | } |
488 | |
489 | ASSERT(false); |
490 | return -1; |
491 | } |
492 | |
493 | int Color::getGreen() const |
494 | { |
495 | switch (getType()) { |
496 | |
497 | case Color::MaskType: |
498 | return 0; |
499 | |
500 | case Color::RgbType: |
501 | return m_value.rgb.g; |
502 | |
503 | case Color::HsvType: |
504 | return Rgb(Hsv(m_value.hsv.h, |
505 | m_value.hsv.s, |
506 | m_value.hsv.v)).green(); |
507 | |
508 | case Color::HslType: |
509 | return Rgb(Hsl(m_value.hsl.h, |
510 | m_value.hsl.s, |
511 | m_value.hsl.l)).green(); |
512 | |
513 | case Color::GrayType: |
514 | return m_value.gray.g; |
515 | |
516 | case Color::IndexType: { |
517 | int i = m_value.index; |
518 | if (i >= 0 && i < get_current_palette()->size()) |
519 | return rgba_getg(get_current_palette()->getEntry(i)); |
520 | else |
521 | return 0; |
522 | } |
523 | |
524 | } |
525 | |
526 | ASSERT(false); |
527 | return -1; |
528 | } |
529 | |
530 | int Color::getBlue() const |
531 | { |
532 | switch (getType()) { |
533 | |
534 | case Color::MaskType: |
535 | return 0; |
536 | |
537 | case Color::RgbType: |
538 | return m_value.rgb.b; |
539 | |
540 | case Color::HsvType: |
541 | return Rgb(Hsv(m_value.hsv.h, |
542 | m_value.hsv.s, |
543 | m_value.hsv.v)).blue(); |
544 | |
545 | case Color::HslType: |
546 | return Rgb(Hsl(m_value.hsl.h, |
547 | m_value.hsl.s, |
548 | m_value.hsl.l)).blue(); |
549 | |
550 | case Color::GrayType: |
551 | return m_value.gray.g; |
552 | |
553 | case Color::IndexType: { |
554 | int i = m_value.index; |
555 | if (i >= 0 && i < get_current_palette()->size()) |
556 | return rgba_getb(get_current_palette()->getEntry(i)); |
557 | else |
558 | return 0; |
559 | } |
560 | |
561 | } |
562 | |
563 | ASSERT(false); |
564 | return -1; |
565 | } |
566 | |
567 | double Color::getHsvHue() const |
568 | { |
569 | switch (getType()) { |
570 | |
571 | case Color::MaskType: |
572 | return 0.0; |
573 | |
574 | case Color::RgbType: |
575 | return Hsv(Rgb(m_value.rgb.r, |
576 | m_value.rgb.g, |
577 | m_value.rgb.b)).hue(); |
578 | |
579 | case Color::HsvType: |
580 | return m_value.hsv.h; |
581 | |
582 | case Color::HslType: |
583 | return m_value.hsl.h; |
584 | |
585 | case Color::GrayType: |
586 | return 0.0; |
587 | |
588 | case Color::IndexType: { |
589 | int i = m_value.index; |
590 | if (i >= 0 && i < get_current_palette()->size()) { |
591 | uint32_t c = get_current_palette()->getEntry(i); |
592 | return Hsv(Rgb(rgba_getr(c), |
593 | rgba_getg(c), |
594 | rgba_getb(c))).hue(); |
595 | } |
596 | else |
597 | return 0.0; |
598 | } |
599 | |
600 | } |
601 | |
602 | ASSERT(false); |
603 | return -1.0; |
604 | } |
605 | |
606 | double Color::getHsvSaturation() const |
607 | { |
608 | switch (getType()) { |
609 | |
610 | case Color::MaskType: |
611 | return 0; |
612 | |
613 | case Color::RgbType: |
614 | return Hsv(Rgb(m_value.rgb.r, |
615 | m_value.rgb.g, |
616 | m_value.rgb.b)).saturation(); |
617 | |
618 | case Color::HsvType: |
619 | return m_value.hsv.s; |
620 | |
621 | case Color::HslType: |
622 | return Hsv(Rgb(getRed(), |
623 | getGreen(), |
624 | getBlue())).saturation(); |
625 | |
626 | case Color::GrayType: |
627 | return 0; |
628 | |
629 | case Color::IndexType: { |
630 | int i = m_value.index; |
631 | if (i >= 0 && i < get_current_palette()->size()) { |
632 | uint32_t c = get_current_palette()->getEntry(i); |
633 | return Hsv(Rgb(rgba_getr(c), |
634 | rgba_getg(c), |
635 | rgba_getb(c))).saturation(); |
636 | } |
637 | else |
638 | return 0.0; |
639 | } |
640 | |
641 | } |
642 | |
643 | ASSERT(false); |
644 | return -1.0; |
645 | } |
646 | |
647 | double Color::getHsvValue() const |
648 | { |
649 | switch (getType()) { |
650 | |
651 | case Color::MaskType: |
652 | return 0.0; |
653 | |
654 | case Color::RgbType: |
655 | return Hsv(Rgb(m_value.rgb.r, |
656 | m_value.rgb.g, |
657 | m_value.rgb.b)).value(); |
658 | |
659 | case Color::HsvType: |
660 | return m_value.hsv.v; |
661 | |
662 | case Color::HslType: |
663 | return Hsv(Rgb(getRed(), |
664 | getGreen(), |
665 | getBlue())).value(); |
666 | |
667 | case Color::GrayType: |
668 | return m_value.gray.g / 255.0; |
669 | |
670 | case Color::IndexType: { |
671 | int i = m_value.index; |
672 | if (i >= 0 && i < get_current_palette()->size()) { |
673 | uint32_t c = get_current_palette()->getEntry(i); |
674 | return Hsv(Rgb(rgba_getr(c), |
675 | rgba_getg(c), |
676 | rgba_getb(c))).value(); |
677 | } |
678 | else |
679 | return 0.0; |
680 | } |
681 | |
682 | } |
683 | |
684 | ASSERT(false); |
685 | return -1.0; |
686 | } |
687 | |
688 | double Color::getHslHue() const |
689 | { |
690 | switch (getType()) { |
691 | |
692 | case Color::MaskType: |
693 | return 0.0; |
694 | |
695 | case Color::RgbType: |
696 | return Hsl(Rgb(m_value.rgb.r, |
697 | m_value.rgb.g, |
698 | m_value.rgb.b)).hue(); |
699 | |
700 | case Color::HsvType: |
701 | return m_value.hsv.h; |
702 | |
703 | case Color::HslType: |
704 | return m_value.hsl.h; |
705 | |
706 | case Color::GrayType: |
707 | return 0.0; |
708 | |
709 | case Color::IndexType: { |
710 | int i = m_value.index; |
711 | if (i >= 0 && i < get_current_palette()->size()) { |
712 | uint32_t c = get_current_palette()->getEntry(i); |
713 | return Hsl(Rgb(rgba_getr(c), |
714 | rgba_getg(c), |
715 | rgba_getb(c))).hue(); |
716 | } |
717 | else |
718 | return 0.0; |
719 | } |
720 | |
721 | } |
722 | |
723 | ASSERT(false); |
724 | return -1.0; |
725 | } |
726 | |
727 | double Color::getHslSaturation() const |
728 | { |
729 | switch (getType()) { |
730 | |
731 | case Color::MaskType: |
732 | return 0; |
733 | |
734 | case Color::RgbType: |
735 | return Hsl(Rgb(m_value.rgb.r, |
736 | m_value.rgb.g, |
737 | m_value.rgb.b)).saturation(); |
738 | |
739 | case Color::HsvType: |
740 | return Hsl(Rgb(getRed(), |
741 | getGreen(), |
742 | getBlue())).saturation(); |
743 | |
744 | case Color::HslType: |
745 | return m_value.hsl.s; |
746 | |
747 | case Color::GrayType: |
748 | return 0; |
749 | |
750 | case Color::IndexType: { |
751 | int i = m_value.index; |
752 | if (i >= 0 && i < get_current_palette()->size()) { |
753 | uint32_t c = get_current_palette()->getEntry(i); |
754 | return Hsl(Rgb(rgba_getr(c), |
755 | rgba_getg(c), |
756 | rgba_getb(c))).saturation(); |
757 | } |
758 | else |
759 | return 0.0; |
760 | } |
761 | |
762 | } |
763 | |
764 | ASSERT(false); |
765 | return -1.0; |
766 | } |
767 | |
768 | double Color::getHslLightness() const |
769 | { |
770 | switch (getType()) { |
771 | |
772 | case Color::MaskType: |
773 | return 0.0; |
774 | |
775 | case Color::RgbType: |
776 | return Hsl(Rgb(m_value.rgb.r, |
777 | m_value.rgb.g, |
778 | m_value.rgb.b)).lightness(); |
779 | |
780 | case Color::HsvType: |
781 | return Hsl(Rgb(getRed(), |
782 | getGreen(), |
783 | getBlue())).lightness(); |
784 | |
785 | case Color::HslType: |
786 | return m_value.hsl.l; |
787 | |
788 | case Color::GrayType: |
789 | return m_value.gray.g / 255.0; |
790 | |
791 | case Color::IndexType: { |
792 | int i = m_value.index; |
793 | if (i >= 0 && i < get_current_palette()->size()) { |
794 | uint32_t c = get_current_palette()->getEntry(i); |
795 | return Hsl(Rgb(rgba_getr(c), |
796 | rgba_getg(c), |
797 | rgba_getb(c))).lightness(); |
798 | } |
799 | else |
800 | return 0.0; |
801 | } |
802 | |
803 | } |
804 | |
805 | ASSERT(false); |
806 | return -1.0; |
807 | } |
808 | |
809 | int Color::getGray() const |
810 | { |
811 | switch (getType()) { |
812 | |
813 | case Color::MaskType: |
814 | return 0; |
815 | |
816 | case Color::RgbType: |
817 | return int(255.0 * Hsl(Rgb(m_value.rgb.r, |
818 | m_value.rgb.g, |
819 | m_value.rgb.b)).lightness()); |
820 | |
821 | case Color::HsvType: |
822 | return int(255.0 * Hsl(Rgb(getRed(), |
823 | getGreen(), |
824 | getBlue())).lightness()); |
825 | |
826 | case Color::HslType: |
827 | return int(255.0 * m_value.hsl.l); |
828 | |
829 | case Color::GrayType: |
830 | return m_value.gray.g; |
831 | |
832 | case Color::IndexType: { |
833 | int i = m_value.index; |
834 | if (i >= 0 && i < get_current_palette()->size()) { |
835 | uint32_t c = get_current_palette()->getEntry(i); |
836 | return int(255.0 * Hsl(Rgb(rgba_getr(c), |
837 | rgba_getg(c), |
838 | rgba_getb(c))).lightness()); |
839 | } |
840 | else |
841 | return 0; |
842 | } |
843 | |
844 | } |
845 | |
846 | ASSERT(false); |
847 | return -1; |
848 | } |
849 | |
850 | int Color::getIndex() const |
851 | { |
852 | switch (getType()) { |
853 | |
854 | case Color::MaskType: |
855 | return 0; |
856 | |
857 | case Color::RgbType: |
858 | case Color::HsvType: |
859 | case Color::HslType: |
860 | case Color::GrayType: { |
861 | int i = get_current_palette()->findExactMatch(getRed(), getGreen(), getBlue(), getAlpha(), -1); |
862 | if (i >= 0) |
863 | return i; |
864 | else |
865 | return get_current_palette()->findBestfit(getRed(), getGreen(), getBlue(), getAlpha(), 0); |
866 | } |
867 | |
868 | case Color::IndexType: |
869 | return m_value.index; |
870 | |
871 | } |
872 | |
873 | ASSERT(false); |
874 | return -1; |
875 | } |
876 | |
877 | int Color::getAlpha() const |
878 | { |
879 | switch (getType()) { |
880 | |
881 | case Color::MaskType: |
882 | return 0; |
883 | |
884 | case Color::RgbType: |
885 | return m_value.rgb.a; |
886 | |
887 | case Color::HsvType: |
888 | return m_value.hsv.a; |
889 | |
890 | case Color::HslType: |
891 | return m_value.hsl.a; |
892 | |
893 | case Color::GrayType: |
894 | return m_value.gray.a; |
895 | |
896 | case Color::IndexType: { |
897 | int i = m_value.index; |
898 | if (i >= 0 && i < get_current_palette()->size()) |
899 | return rgba_geta(get_current_palette()->getEntry(i)); |
900 | else |
901 | return 0; |
902 | } |
903 | |
904 | } |
905 | |
906 | ASSERT(false); |
907 | return -1; |
908 | } |
909 | |
910 | void Color::setAlpha(int alpha) |
911 | { |
912 | alpha = std::clamp(alpha, 0, 255); |
913 | |
914 | switch (getType()) { |
915 | |
916 | case Color::MaskType: |
917 | break; |
918 | |
919 | case Color::RgbType: |
920 | m_value.rgb.a = alpha; |
921 | break; |
922 | |
923 | case Color::HsvType: |
924 | m_value.hsv.a = alpha; |
925 | break; |
926 | |
927 | case Color::HslType: |
928 | m_value.hsl.a = alpha; |
929 | break; |
930 | |
931 | case Color::GrayType: |
932 | m_value.gray.a = alpha; |
933 | break; |
934 | |
935 | case Color::IndexType: |
936 | *this = Color::fromRgb(getRed(), |
937 | getGreen(), |
938 | getBlue(), alpha); |
939 | break; |
940 | } |
941 | } |
942 | |
943 | } // namespace app |
944 | |