1 #include "Core.h"
2
3 namespace Upp {
4
RGBtoHSV(double r,double g,double b,double & h,double & s,double & v)5 void RGBtoHSV(double r, double g, double b, double& h, double& s, double& v)
6 {
7 double delta;
8 if((v = max(r, max(g, b))) == 0 || (delta = v - min(r, min(g, b))) == 0)
9 {
10 h = s = 0;
11 return;
12 }
13 s = delta / v;
14 delta *= 6;
15 if(g == v)
16 h = 1 / 3.0 + (b - r) / delta;
17 else if(b == v)
18 h = 2 / 3.0 + (r - g) / delta;
19 else
20 if((h = (g - b) / delta) < 0)
21 h += 1;
22 }
23
HSVtoRGB(double h,double s,double v,double & r,double & g,double & b)24 void HSVtoRGB(double h, double s, double v, double& r, double& g, double& b)
25 {
26 if(s == 0)
27 {
28 r = g = b = v;
29 return;
30 }
31 double rem = fmod(h *= 6, 1);
32 double p = v * (1 - s);
33 double q = v * (1 - s * rem);
34 double t = v * (1 - s * (1 - rem));
35 switch(ffloor(h))
36 {
37 default: NEVER(); // invalid color!
38 case 6:
39 case 0: r = v; g = t; b = p; break;
40 case 1: r = q; g = v; b = p; break;
41 case 2: r = p; g = v; b = t; break;
42 case 3: r = p; g = q; b = v; break;
43 case 4: r = t; g = p; b = v; break;
44 case -1:
45 case 5: r = v; g = p; b = q; break;
46 }
47 }
48
HsvColorf(double h,double s,double v)49 Color HsvColorf(double h, double s, double v)
50 {
51 double r, g, b;
52 HSVtoRGB(h, s, v, r, g, b);
53 return Color(min(int(r * 255), 255), min(int(g * 255), 255), min(int(b * 255), 255));
54 }
55
CMYKtoRGB(double c,double m,double y,double k,double & r,double & g,double & b)56 void CMYKtoRGB(double c, double m, double y, double k, double& r, double& g, double& b)
57 {
58 k = clamp(k, 0.0, 1.0);
59 r = (1 - c) * (1 - k);
60 g = (1 - m) * (1 - k);
61 b = (1 - y) * (1 - k);
62 }
63
RGBtoCMYK(double r,double g,double b,double & c,double & m,double & y,double & k)64 void RGBtoCMYK(double r, double g, double b, double& c, double& m, double& y, double& k)
65 {
66 k = 1 - max(max(r, g), b);
67 c = (1 - r - k) / (1 - k);
68 m = (1 - g - k) / (1 - k);
69 y = (1 - b - k) / (1 - k);
70 }
71
CmykColorf(double c,double m,double y,double k)72 Color CmykColorf(double c, double m, double y, double k)
73 {
74 double r, g, b;
75 CMYKtoRGB(c, m, y, k, r, g, b);
76 return Color(min(int(r * 255), 255), min(int(g * 255), 255), min(int(b * 255), 255));
77 }
78
Get() const79 dword Color::Get() const
80 {
81 if(IsNullInstance()) return 0;
82 dword c = color;
83 return c & 0xffffff;
84 }
85
86 template <>
AsString(const RGBA & c)87 String AsString(const RGBA& c)
88 {
89 return Format("RGBA(%d, %d, %d, %d)", (int)c.r, (int)c.g, (int)c.b, (int)c.a);
90 }
91
operator RGBA() const92 Color::operator RGBA() const
93 {
94 RGBA color;
95 if(IsNullInstance())
96 Zero(color);
97 else {
98 color.r = GetR();
99 color.g = GetG();
100 color.b = GetB();
101 color.a = 255;
102 }
103 return color;
104 }
105
Color(RGBA rgba)106 Color::Color(RGBA rgba)
107 {
108 if(rgba.a == 0)
109 color = 0xffffffff;
110 else {
111 if(rgba.a == 255)
112 color = RGB(rgba.r, rgba.g, rgba.b);
113 else {
114 int alpha = 65536 / rgba.a;
115 color = RGB((alpha * rgba.r) >> 8, (alpha * rgba.g) >> 8, (alpha * rgba.b) >> 8);
116 }
117 }
118 }
119
Jsonize(JsonIO & jio)120 void Color::Jsonize(JsonIO& jio)
121 {
122 int r, g, b;
123 if(IsNullInstance()) {
124 r = g = b = Null;
125 }
126 else {
127 r = GetR();
128 g = GetG();
129 b = GetB();
130 }
131 jio("red", r)("green", g)("blue", b);
132 if(IsNull(r))
133 *this = Null;
134 else
135 *this = Color(r, g, b);
136 }
137
Xmlize(XmlIO & xio)138 void Color::Xmlize(XmlIO& xio)
139 {
140 int r, g, b;
141 if(IsNullInstance()) {
142 r = g = b = Null;
143 }
144 else {
145 r = GetR();
146 g = GetG();
147 b = GetB();
148 }
149 xio
150 .Attr("red", r)
151 .Attr("green", g)
152 .Attr("blue", b)
153 ;
154 if(IsNull(r))
155 *this = Null;
156 else
157 *this = Color(r, g, b);
158 }
159
operator *(int alpha,Color c)160 RGBA operator*(int alpha, Color c)
161 {
162 RGBA r;
163 r.a = alpha;
164 alpha += (alpha >> 7);
165 r.r = (alpha * c.GetR()) >> 8;
166 r.g = (alpha * c.GetG()) >> 8;
167 r.b = (alpha * c.GetB()) >> 8;
168 return r;
169 }
170
171 template<>
AsString(const Color & c)172 String AsString(const Color& c) {
173 if(IsNull(c))
174 return "Color(Null)";
175 if(c.GetRaw() & 0x80000000)
176 return Format("Color(%d, 0)", int(c.GetRaw() & ~0x80000000));
177 return Format("Color(%d, %d, %d)", c.GetR(), c.GetG(), c.GetB());
178 }
179
ColorToHtml(Color color)180 String ColorToHtml(Color color)
181 {
182 return IsNull(color) ? Null : Format("#%02X%02X%02X", color.GetR(), color.GetG(), color.GetB());
183 }
184
sCharFilterNoDigit(int c)185 static int sCharFilterNoDigit(int c)
186 {
187 return IsDigit(c) ? 0 : c;
188 }
189
sCharFilterHex(int c)190 static int sCharFilterHex(int c)
191 {
192 return c >= 'a' && c <= 'f' || c >= 'A' && c <= 'F' || IsDigit(c) ? c : 0;
193 }
194
ColorFromText(const char * s)195 Color ColorFromText(const char *s)
196 {
197 Vector<String> h = Split(s, sCharFilterNoDigit);
198 if(h.GetCount() == 3 && (strchr(s, ',') || strchr(s, ';') || strchr(s, '.') || strchr(s, ' '))) {
199 int r = atoi(h[0]);
200 int g = atoi(h[1]);
201 int b = atoi(h[2]);
202 if(r >= 0 && r <= 255 && g >= 0 && g <= 255 && b >= 0 && b <= 255)
203 return Color(r, g, b);
204 }
205 String hex = Filter(s, sCharFilterHex);
206 if(hex.GetCount() == 6 || hex.GetCount() == 8) {
207 dword w = (dword)ScanInt64(~hex, NULL, 16);
208 return Color(byte(w >> 16), byte(w >> 8), byte(w));
209 }
210 return Null;
211 }
212
Blend(Color c1,Color c2,int alpha)213 Color Blend(Color c1, Color c2, int alpha)
214 {
215 int a = (alpha >> 7) + alpha;
216 return Color(min(((a * (c2.GetR() - c1.GetR())) >> 8) + c1.GetR(), 255),
217 min(((a * (c2.GetG() - c1.GetG())) >> 8) + c1.GetG(), 255),
218 min(((a * (c2.GetB() - c1.GetB())) >> 8) + c1.GetB(), 255));
219 }
220
221 INITBLOCK {
222 Value::SvoRegister<Color>("Color");
223 }
224
225 int Grayscale(const Color& c)
226 {
227 return (77 * c.GetR() + 151 * c.GetG() + 28 * c.GetB()) >> 8;
228 }
229
IsDark(Color c)230 bool IsDark(Color c)
231 {
232 return Grayscale(c) < 80;
233 }
234
IsLight(Color c)235 bool IsLight(Color c)
236 {
237 return Grayscale(c) > 255 - 80;
238 }
239
Grayscale2(const Color & c)240 int Grayscale2(const Color& c)
241 {
242 return (c.GetR() + c.GetG() + c.GetB()) / 3;
243 }
244
DarkTheme(Color color)245 Color DarkTheme(Color color)
246 {
247 if(IsNull(color))
248 return Null;
249
250 double v[3];
251 v[0] = color.GetR();
252 v[1] = color.GetG();
253 v[2] = color.GetB();
254
255 // this represent physiological perception of brightness of R,G,B. Sum = 1
256 // static double c[3] = { 0.21, 0.72, 0.07 }; // physiologically correct values
257 static double c[3] = { 0.3, 0.5, 0.2 }; // with this set, blues and reds are more pronounced
258
259 double m0 = c[0] * v[0] + c[1] * v[1] + c[2] * v[2]; // base brightness
260
261 const int middle = 155; // this value represents gamma-like feature, imbalance of perception of dark vs bright
262 const double up = (256.0 - middle) / middle;
263 const double down = 1 / up;
264
265 double m;
266 if(m0 < middle)
267 m = middle + (middle - m0) * up;
268 else
269 m = middle - (m0 - middle) * down;
270
271 int i0 = 0;
272 int i1 = 1;
273 int i2 = 2;
274
275 if(v[i0] > v[i1])
276 Swap(i0, i1);
277 if(v[i1] > v[i2])
278 Swap(i1, i2);
279 if(v[i0] > v[i1])
280 Swap(i0, i1);
281
282 if(m0 < m) {
283 m -= m0;
284 double a = min(v[i2] + m, 255.0) - v[i2];
285 v[i0] += a;
286 v[i1] += a;
287 v[i2] += a;
288 m -= a;
289
290 a = min(v[i1] + m / (c[i0] + c[i1]), 255.0) - v[i1];
291 v[i0] += a;
292 v[i1] += a;
293 m -= (c[i0] + c[i1]) * a;
294
295 v[i0] = min(v[i0] + m / c[i1], 255.0);
296 }
297 else {
298 m = m0 - m;
299 double a = v[i0] - max(v[i0] - m, 0.0);
300 v[i0] -= a;
301 v[i1] -= a;
302 v[i2] -= a;
303 m -= a;
304
305 a = v[i1] - max(v[i1] - m / (c[i1] + c[i2]), 0.0);
306 v[i1] -= a;
307 v[i2] -= a;
308 m -= (c[i1] + c[i2]) * a;
309
310 v[i2] = max(v[i2] - m / c[i2], 0.0);
311 }
312
313 return Color((int)v[0], (int)v[1], (int)v[2]);
314 }
315
DarkThemeCached(Color c)316 Color DarkThemeCached(Color c)
317 {
318 const int N = 8;
319 thread_local struct Cache {
320 Color icolor[N];
321 Color ocolor[N];
322 int ii = 0;
323
324 Cache() {
325 for(int i = 0; i < N; i++) {
326 icolor[i] = Color(0, 0, 0);
327 ocolor[i] = Color(255, 255, 255);
328 }
329 }
330 } cache;
331 #define DO(i) if(cache.icolor[i] == c) return cache.ocolor[i];
332 DO(0); DO(1); DO(2); DO(3); DO(4); DO(5); DO(6); DO(7);
333 cache.ii = (cache.ii + 1) & (N - 1);
334 cache.icolor[cache.ii] = c;
335 c = DarkTheme(c);
336 cache.ocolor[cache.ii] = c;
337 return c;
338 }
339
340 }
341