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
31namespace app {
32
33using namespace gfx;
34
35// static
36Color Color::fromMask()
37{
38 return Color(Color::MaskType);
39}
40
41// static
42Color 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
53Color 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
64Color 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
75Color 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
84Color 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
94Color 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
126Color 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
135Color 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
180Color Color::toRgb() const
181{
182 return Color::fromRgb(getRed(), getGreen(), getBlue(), getAlpha());
183}
184
185std::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
238std::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
395bool 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)
443bool 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
456int 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
493int 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
530int 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
567double 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
606double 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
647double 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
688double 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
727double 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
768double 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
809int 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
850int 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
877int 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
910void 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