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#include <LibIPC/Forward.h>
32
33namespace Gfx {
34
35enum class ColorRole;
36typedef u32 RGBA32;
37
38inline constexpr u32 make_rgb(u8 r, u8 g, u8 b)
39{
40 return ((r << 16) | (g << 8) | b);
41}
42
43struct HSV {
44 double hue { 0 };
45 double saturation { 0 };
46 double value { 0 };
47};
48
49class Color {
50public:
51 enum NamedColor {
52 Black,
53 White,
54 Red,
55 Green,
56 Cyan,
57 Blue,
58 Yellow,
59 Magenta,
60 DarkGray,
61 MidGray,
62 LightGray,
63 WarmGray,
64 DarkCyan,
65 DarkGreen,
66 DarkBlue,
67 DarkRed,
68 MidCyan,
69 MidGreen,
70 MidRed,
71 MidBlue,
72 MidMagenta,
73 };
74
75 Color() {}
76 Color(NamedColor);
77 Color(u8 r, u8 g, u8 b)
78 : m_value(0xff000000 | (r << 16) | (g << 8) | b)
79 {
80 }
81 Color(u8 r, u8 g, u8 b, u8 a)
82 : m_value((a << 24) | (r << 16) | (g << 8) | b)
83 {
84 }
85
86 static Color from_rgb(unsigned rgb) { return Color(rgb | 0xff000000); }
87 static Color from_rgba(unsigned rgba) { return Color(rgba); }
88
89 u8 red() const { return (m_value >> 16) & 0xff; }
90 u8 green() const { return (m_value >> 8) & 0xff; }
91 u8 blue() const { return m_value & 0xff; }
92 u8 alpha() const { return (m_value >> 24) & 0xff; }
93
94 void set_alpha(u8 value)
95 {
96 m_value &= 0x00ffffff;
97 m_value |= value << 24;
98 }
99
100 void set_red(u8 value)
101 {
102 m_value &= 0xff00ffff;
103 m_value |= value << 16;
104 }
105
106 void set_green(u8 value)
107 {
108 m_value &= 0xffff00ff;
109 m_value |= value << 8;
110 }
111
112 void set_blue(u8 value)
113 {
114 m_value &= 0xffffff00;
115 m_value |= value;
116 }
117
118 Color with_alpha(u8 alpha)
119 {
120 return Color((m_value & 0x00ffffff) | alpha << 24);
121 }
122
123 Color blend(Color source) const
124 {
125 if (!alpha() || source.alpha() == 255)
126 return source;
127
128 if (!source.alpha())
129 return *this;
130
131 int d = 255 * (alpha() + source.alpha()) - alpha() * source.alpha();
132 u8 r = (red() * alpha() * (255 - source.alpha()) + 255 * source.alpha() * source.red()) / d;
133 u8 g = (green() * alpha() * (255 - source.alpha()) + 255 * source.alpha() * source.green()) / d;
134 u8 b = (blue() * alpha() * (255 - source.alpha()) + 255 * source.alpha() * source.blue()) / d;
135 u8 a = d / 255;
136 return Color(r, g, b, a);
137 }
138
139 Color to_grayscale() const
140 {
141 int gray = (red() + green() + blue()) / 3;
142 return Color(gray, gray, gray, alpha());
143 }
144
145 Color darkened(float amount = 0.5f) const
146 {
147 return Color(red() * amount, green() * amount, blue() * amount, alpha());
148 }
149
150 Color lightened(float amount = 1.2f) const
151 {
152 return Color(min(255, (int)((float)red() * amount)), min(255, (int)((float)green() * amount)), min(255, (int)((float)blue() * amount)), alpha());
153 }
154
155 Color inverted() const
156 {
157 return Color(~red(), ~green(), ~blue());
158 }
159
160 RGBA32 value() const { return m_value; }
161
162 bool operator==(const Color& other) const
163 {
164 return m_value == other.m_value;
165 }
166
167 bool operator!=(const Color& other) const
168 {
169 return m_value != other.m_value;
170 }
171
172 String to_string() const;
173 static Optional<Color> from_string(const StringView&);
174
175 HSV to_hsv() const
176 {
177 HSV hsv;
178 double r = static_cast<double>(red()) / 255.0;
179 double g = static_cast<double>(green()) / 255.0;
180 double b = static_cast<double>(blue()) / 255.0;
181 double max = AK::max(AK::max(r, g), b);
182 double min = AK::min(AK::min(r, g), b);
183 double chroma = max - min;
184
185 if (!chroma)
186 hsv.hue = 0.0;
187 else if (max == r)
188 hsv.hue = (60.0 * ((g - b) / chroma)) + 360.0;
189 else if (max == g)
190 hsv.hue = (60.0 * ((b - r) / chroma)) + 120.0;
191 else
192 hsv.hue = (60.0 * ((r - g) / chroma)) + 240.0;
193
194 if (hsv.hue >= 360.0)
195 hsv.hue -= 360.0;
196
197 hsv.hue /= 360.0;
198
199 if (!max)
200 hsv.saturation = 0;
201 else
202 hsv.saturation = chroma / max;
203
204 hsv.value = max;
205 return hsv;
206 }
207
208 static Color from_hsv(double hue, double saturation, double value)
209 {
210 return from_hsv({ hue, saturation, value });
211 }
212
213 static Color from_hsv(const HSV& hsv)
214 {
215 double hue = hsv.hue * 2.0;
216 double saturation = hsv.saturation / 255.0;
217 double value = hsv.value / 255.0;
218
219 int high = static_cast<int>(hue / 60.0) % 6;
220 double f = (hue / 60.0) - high;
221 double c1 = value * (1.0 - saturation);
222 double c2 = value * (1.0 - saturation * f);
223 double c3 = value * (1.0 - saturation * (1.0 - f));
224
225 double r = 0;
226 double g = 0;
227 double b = 0;
228
229 switch (high) {
230 case 0:
231 r = value;
232 g = c3;
233 b = c1;
234 break;
235 case 1:
236 r = c2;
237 g = value;
238 b = c1;
239 break;
240 case 2:
241 r = c1;
242 g = value;
243 b = c3;
244 break;
245 case 3:
246 r = c1;
247 g = c2;
248 b = value;
249 break;
250 case 4:
251 r = c3;
252 g = c1;
253 b = value;
254 break;
255 case 5:
256 r = value;
257 g = c1;
258 b = c2;
259 break;
260 }
261
262 u8 out_r = (u8)(r * 255);
263 u8 out_g = (u8)(g * 255);
264 u8 out_b = (u8)(b * 255);
265 return Color(out_r, out_g, out_b);
266 }
267
268private:
269 explicit Color(RGBA32 rgba)
270 : m_value(rgba)
271 {
272 }
273
274 RGBA32 m_value { 0 };
275};
276
277const LogStream& operator<<(const LogStream&, Color);
278
279}
280
281using Gfx::Color;
282
283namespace IPC {
284bool decode(Decoder&, Gfx::Color&);
285}