Serenity Operating System
1/*
2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright notice, this
9 * list of conditions and the following disclaimer.
10 *
11 * 2. Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#pragma once
28
29#include <AK/Forward.h>
30#include <AK/StdLibExtras.h>
31
32namespace Gfx {
33
34enum class ColorRole;
35typedef u32 RGBA32;
36
37inline constexpr u32 make_rgb(u8 r, u8 g, u8 b)
38{
39 return ((r << 16) | (g << 8) | b);
40}
41
42struct HSV {
43 double hue { 0 };
44 double saturation { 0 };
45 double value { 0 };
46};
47
48class Color {
49public:
50 enum NamedColor {
51 Black,
52 White,
53 Red,
54 Green,
55 Cyan,
56 Blue,
57 Yellow,
58 Magenta,
59 DarkGray,
60 MidGray,
61 LightGray,
62 WarmGray,
63 DarkCyan,
64 DarkGreen,
65 DarkBlue,
66 DarkRed,
67 MidCyan,
68 MidGreen,
69 MidRed,
70 MidBlue,
71 MidMagenta,
72 };
73
74 Color() {}
75 Color(NamedColor);
76 Color(u8 r, u8 g, u8 b)
77 : m_value(0xff000000 | (r << 16) | (g << 8) | b)
78 {
79 }
80 Color(u8 r, u8 g, u8 b, u8 a)
81 : m_value((a << 24) | (r << 16) | (g << 8) | b)
82 {
83 }
84
85 static Color from_rgb(unsigned rgb) { return Color(rgb | 0xff000000); }
86 static Color from_rgba(unsigned rgba) { return Color(rgba); }
87
88 u8 red() const { return (m_value >> 16) & 0xff; }
89 u8 green() const { return (m_value >> 8) & 0xff; }
90 u8 blue() const { return m_value & 0xff; }
91 u8 alpha() const { return (m_value >> 24) & 0xff; }
92
93 void set_alpha(u8 value)
94 {
95 m_value &= 0x00ffffff;
96 m_value |= value << 24;
97 }
98
99 void set_red(u8 value)
100 {
101 m_value &= 0xff00ffff;
102 m_value |= value << 16;
103 }
104
105 void set_green(u8 value)
106 {
107 m_value &= 0xffff00ff;
108 m_value |= value << 8;
109 }
110
111 void set_blue(u8 value)
112 {
113 m_value &= 0xffffff00;
114 m_value |= value;
115 }
116
117 Color with_alpha(u8 alpha)
118 {
119 return Color((m_value & 0x00ffffff) | alpha << 24);
120 }
121
122 Color blend(Color source) const
123 {
124 if (!alpha() || source.alpha() == 255)
125 return source;
126
127 if (!source.alpha())
128 return *this;
129
130 int d = 255 * (alpha() + source.alpha()) - alpha() * source.alpha();
131 u8 r = (red() * alpha() * (255 - source.alpha()) + 255 * source.alpha() * source.red()) / d;
132 u8 g = (green() * alpha() * (255 - source.alpha()) + 255 * source.alpha() * source.green()) / d;
133 u8 b = (blue() * alpha() * (255 - source.alpha()) + 255 * source.alpha() * source.blue()) / d;
134 u8 a = d / 255;
135 return Color(r, g, b, a);
136 }
137
138 Color to_grayscale() const
139 {
140 int gray = (red() + green() + blue()) / 3;
141 return Color(gray, gray, gray, alpha());
142 }
143
144 Color darkened(float amount = 0.5f) const
145 {
146 return Color(red() * amount, green() * amount, blue() * amount, alpha());
147 }
148
149 Color lightened(float amount = 1.2f) const
150 {
151 return Color(min(255, (int)((float)red() * amount)), min(255, (int)((float)green() * amount)), min(255, (int)((float)blue() * amount)), alpha());
152 }
153
154 Color inverted() const
155 {
156 return Color(~red(), ~green(), ~blue());
157 }
158
159 RGBA32 value() const { return m_value; }
160
161 bool operator==(const Color& other) const
162 {
163 return m_value == other.m_value;
164 }
165
166 bool operator!=(const Color& other) const
167 {
168 return m_value != other.m_value;
169 }
170
171 String to_string() const;
172 static Optional<Color> from_string(const StringView&);
173
174 HSV to_hsv() const
175 {
176 HSV hsv;
177 double r = static_cast<double>(red()) / 255.0;
178 double g = static_cast<double>(green()) / 255.0;
179 double b = static_cast<double>(blue()) / 255.0;
180 double max = AK::max(AK::max(r, g), b);
181 double min = AK::min(AK::min(r, g), b);
182 double chroma = max - min;
183
184 if (!chroma)
185 hsv.hue = 0.0;
186 else if (max == r)
187 hsv.hue = (60.0 * ((g - b) / chroma)) + 360.0;
188 else if (max == g)
189 hsv.hue = (60.0 * ((b - r) / chroma)) + 120.0;
190 else
191 hsv.hue = (60.0 * ((r - g) / chroma)) + 240.0;
192
193 if (hsv.hue >= 360.0)
194 hsv.hue -= 360.0;
195
196 hsv.hue /= 360.0;
197
198 if (!max)
199 hsv.saturation = 0;
200 else
201 hsv.saturation = chroma / max;
202
203 hsv.value = max;
204 return hsv;
205 }
206
207 static Color from_hsv(double hue, double saturation, double value)
208 {
209 return from_hsv({ hue, saturation, value });
210 }
211
212 static Color from_hsv(const HSV& hsv)
213 {
214 double hue = hsv.hue * 2.0;
215 double saturation = hsv.saturation / 255.0;
216 double value = hsv.value / 255.0;
217
218 int high = static_cast<int>(hue / 60.0) % 6;
219 double f = (hue / 60.0) - high;
220 double c1 = value * (1.0 - saturation);
221 double c2 = value * (1.0 - saturation * f);
222 double c3 = value * (1.0 - saturation * (1.0 - f));
223
224 double r = 0;
225 double g = 0;
226 double b = 0;
227
228 switch (high) {
229 case 0:
230 r = value;
231 g = c3;
232 b = c1;
233 break;
234 case 1:
235 r = c2;
236 g = value;
237 b = c1;
238 break;
239 case 2:
240 r = c1;
241 g = value;
242 b = c3;
243 break;
244 case 3:
245 r = c1;
246 g = c2;
247 b = value;
248 break;
249 case 4:
250 r = c3;
251 g = c1;
252 b = value;
253 break;
254 case 5:
255 r = value;
256 g = c1;
257 b = c2;
258 break;
259 }
260
261 u8 out_r = (u8)(r * 255);
262 u8 out_g = (u8)(g * 255);
263 u8 out_b = (u8)(b * 255);
264 return Color(out_r, out_g, out_b);
265 }
266
267private:
268 explicit Color(RGBA32 rgba)
269 : m_value(rgba)
270 {
271 }
272
273 RGBA32 m_value { 0 };
274};
275
276const LogStream& operator<<(const LogStream&, Color);
277
278}
279
280using Gfx::Color;
281
282namespace IPC {
283bool decode(BufferStream&, Gfx::Color&);
284}