1 /*
2 * Copyright (c) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #ifndef API_BASE_UTIL_COLOR_H
17 #define API_BASE_UTIL_COLOR_H
18
19 #include <cstdint>
20
21 #include <base/math/vector.h>
22 #include <base/namespace.h>
23
24 BASE_BEGIN_NAMESPACE()
25 constexpr const uint8_t COLOR_SHIFT_0 = 0;
26 constexpr const uint8_t COLOR_SHIFT_8 = 8;
27 constexpr const uint8_t COLOR_SHIFT_16 = 16;
28 constexpr const uint8_t COLOR_SHIFT_24 = 24;
29
30 /** Color values are in linear space (non premultiplied value)
31 * Linear color space (non-srgb values)
32 * Default initialized to ones (opaque white).
33 */
34 class Color {
35 public:
36 union {
37 struct {
38 float x;
39 float y;
40 float z;
41 float w;
42 };
43 struct {
44 float r;
45 float g;
46 float b;
47 float a;
48 };
49
50 float data[4] { 0.0f };
51 };
52
Color()53 constexpr Color() noexcept : x(1.f), y(1.f), z(1.f), w(1.f) {}
54
Color(float val)55 constexpr explicit Color(float val) noexcept : x(val), y(val), z(val), w(val) {}
56
57 /** Will set the alpha channel to 1.0
58 */
Color(float x,float y,float z)59 constexpr Color(float x, float y, float z) noexcept : x(x), y(y), z(z), w(1.0f) {}
60
Color(float x,float y,float z,float w)61 constexpr Color(float x, float y, float z, float w) noexcept : x(x), y(y), z(z), w(w) {}
62
63 /** Constructs Color from ARGB format (uint32_t)
64 */
Color(const uint32_t color)65 constexpr explicit Color(const uint32_t color) noexcept
66 : x(((color >> COLOR_SHIFT_16) & UINT8_MAX) / 255.0f), y(((color >> COLOR_SHIFT_8) & UINT8_MAX) / 255.0f),
67 z(((color >> COLOR_SHIFT_0) & UINT8_MAX) / 255.0f), w(((color >> COLOR_SHIFT_24) & UINT8_MAX) / 255.0f)
68 {}
69
Color(const BASE_NS::Math::Vec4 & other)70 constexpr explicit Color(const BASE_NS::Math::Vec4& other) noexcept : x(other.x), y(other.y), z(other.z), w(other.w)
71 {}
72
73 constexpr void operator=(const BASE_NS::Math::Vec4& other) noexcept
74 {
75 x = other.x;
76 y = other.y;
77 z = other.z;
78 w = other.w;
79 }
80
81 constexpr Color operator+(const Color& other) const noexcept
82 {
83 Color result;
84 result.x = x + other.x;
85 result.y = y + other.y;
86 result.z = z + other.z;
87 result.w = w + other.w;
88
89 return result;
90 }
91
92 constexpr Color operator-(const Color& other) const noexcept
93 {
94 Color result;
95 result.x = x - other.x;
96 result.y = y - other.y;
97 result.z = z - other.z;
98 result.w = w - other.w;
99
100 return result;
101 }
102
103 constexpr Color operator*(const Color& other) const noexcept
104 {
105 Color result;
106 result.x = x * other.x;
107 result.y = y * other.y;
108 result.z = z * other.z;
109 result.w = w * other.w;
110
111 return result;
112 }
113
114 constexpr Color operator/(const Color& other) const noexcept
115 {
116 Color result;
117 result.x = other.x != 0.f ? x / other.x : 0.f;
118 result.y = other.y != 0.f ? y / other.y : 0.f;
119 result.z = other.z != 0.f ? z / other.z : 0.f;
120 result.w = other.w != 0.f ? w / other.w : 0.f;
121
122 return result;
123 }
124
125 constexpr Color operator*(const float scalar) const noexcept
126 {
127 Color result;
128 result.x = x * scalar;
129 result.y = y * scalar;
130 result.z = z * scalar;
131 result.w = w * scalar;
132
133 return result;
134 }
135
136 constexpr Color operator/(const float scalar) const noexcept
137 {
138 if (scalar != 0.f) {
139 Color result;
140 result.x = x / scalar;
141 result.y = y / scalar;
142 result.z = z / scalar;
143 result.w = w / scalar;
144
145 return result;
146 }
147
148 return *this;
149 }
150
151 constexpr Color& operator*=(const float scalar) noexcept
152 {
153 x *= scalar;
154 y *= scalar;
155 z *= scalar;
156 w *= scalar;
157
158 return *this;
159 }
160
161 constexpr Color& operator/=(const float scalar) noexcept
162 {
163 if (scalar != 0.f) {
164 x /= scalar;
165 y /= scalar;
166 z /= scalar;
167 w /= scalar;
168 }
169
170 return *this;
171 }
172
173 constexpr bool operator==(const Color& other) const noexcept
174 {
175 return x == other.x && y == other.y && z == other.z && w == other.w;
176 }
177
178 constexpr bool operator!=(const Color& other) const noexcept
179 {
180 return x != other.x || y != other.y || z != other.z || w != other.w;
181 }
182
183 constexpr float& operator[](const uint32_t index) noexcept
184 {
185 switch (index) {
186 case 0: // 0: index
187 return x;
188 case 1: // 1: index
189 return y;
190 case 2: // 2: index
191 return z;
192 case 3: // 3: index
193 return w;
194 default: // do nothing
195 break;
196 }
197
198 return x; // dummy
199 }
200
201 constexpr const float& operator[](const uint32_t index) const noexcept
202 {
203 switch (index) {
204 case 0: // 0: index
205 return x;
206 case 1: // 1: index
207 return y;
208 case 2: // 2: index
209 return z;
210 case 3: // 3: index
211 return w;
212 default: // do nothing
213 break;
214 }
215
216 return x; // dummy
217 }
218
219 /** Returns Color's members as Math::Vec4
220 */
Vec4()221 constexpr operator Math::Vec4() const noexcept
222 {
223 return Math::Vec4(x, y, z, w);
224 }
225
GetLerp(const Color & other,float weight)226 constexpr Color GetLerp(const Color& other, float weight) const noexcept
227 {
228 Color result = *this;
229 result.x = Math::lerp(result.x, other.x, weight);
230 result.y = Math::lerp(result.y, other.y, weight);
231 result.z = Math::lerp(result.z, other.z, weight);
232 result.w = Math::lerp(result.w, other.w, weight);
233
234 return result;
235 }
236
GetRgba32()237 inline uint32_t GetRgba32() const noexcept
238 {
239 uint32_t c = (uint8_t)Math::round(x * 255.0f);
240 c <<= COLOR_SHIFT_8;
241 c |= (uint8_t)Math::round(y * 255.0f);
242 c <<= COLOR_SHIFT_8;
243 c |= (uint8_t)Math::round(z * 255.0f);
244 c <<= COLOR_SHIFT_8;
245 c |= (uint8_t)Math::round(w * 255.0f);
246
247 return c;
248 }
249
GetRgba64()250 inline uint64_t GetRgba64() const noexcept
251 {
252 uint64_t c = (uint16_t)Math::round(x * 65535.0f);
253 c <<= COLOR_SHIFT_16;
254 c |= (uint16_t)Math::round(y * 65535.0f);
255 c <<= COLOR_SHIFT_16;
256 c |= (uint16_t)Math::round(z * 65535.0f);
257 c <<= COLOR_SHIFT_16;
258 c |= (uint16_t)Math::round(w * 65535.0f);
259
260 return c;
261 }
262
GetArgb32()263 inline uint32_t GetArgb32() const noexcept
264 {
265 uint32_t c = (uint8_t)Math::round(w * 255.0f);
266 c <<= COLOR_SHIFT_8;
267 c |= (uint8_t)Math::round(x * 255.0f);
268 c <<= COLOR_SHIFT_8;
269 c |= (uint8_t)Math::round(y * 255.0f);
270 c <<= COLOR_SHIFT_8;
271 c |= (uint8_t)Math::round(z * 255.0f);
272
273 return c;
274 }
275
GetArgb64()276 inline uint64_t GetArgb64() const noexcept
277 {
278 uint64_t c = (uint16_t)Math::round(w * 65535.0f);
279 c <<= COLOR_SHIFT_16;
280 c |= (uint16_t)Math::round(x * 65535.0f);
281 c <<= COLOR_SHIFT_16;
282 c |= (uint16_t)Math::round(y * 65535.0f);
283 c <<= COLOR_SHIFT_16;
284 c |= (uint16_t)Math::round(z * 65535.0f);
285
286 return c;
287 }
288
GetLuminanceLinear()289 constexpr float GetLuminanceLinear() const noexcept
290 {
291 return x * 0.299f + y * 0.587f + z * 0.114f;
292 }
293
GetLuminanceSrgb()294 constexpr float GetLuminanceSrgb() const noexcept
295 {
296 return x * 0.2126f + y * 0.7152f + z * 0.0722f;
297 }
298
GetGamma()299 inline Color GetGamma() const
300 {
301 Color result = *this;
302 result.x = Math::pow(result.x, 1.f / 2.2f);
303 result.y = Math::pow(result.y, 1.f / 2.2f);
304 result.z = Math::pow(result.z, 1.f / 2.2f);
305
306 return result;
307 }
308
GetGrayscaleSrgb()309 constexpr Color GetGrayscaleSrgb() const noexcept
310 {
311 float luminance = GetLuminanceSrgb();
312 return Color(luminance, luminance, luminance, w);
313 }
314
GetGrayscaleLinear()315 constexpr Color GetGrayscaleLinear() const noexcept
316 {
317 float luminance = GetLuminanceLinear();
318 return Color(luminance, luminance, luminance, w);
319 }
320 };
321
322 /** Color conversion types
323 */
324 enum ColorConversionType {
325 /** No conversion and should be used as default */
326 COLOR_CONVERSION_TYPE_NONE = 0,
327 /** sRGB to linear conversion */
328 COLOR_CONVERSION_TYPE_SRGB_TO_LINEAR = 1,
329 /** Linear to sRGB conversion */
330 COLOR_CONVERSION_TYPE_LINEAR_TO_SRGB = 2,
331 };
332
333 /** Color space flag bits
334 * Defaults are linear
335 */
336 enum ColorSpaceFlagBits {
337 /** Add hint to treat typical sRGB data as linear with input data and with final output (swapchain)
338 * This value should be retrieved from the engine and use with all (or some of) the plugins
339 * NOTE: mainly affects the input formats where data is written into (and typically results as non-srgb formats),
340 * and swapchain formats.
341 */
342 COLOR_SPACE_SRGB_AS_LINEAR_BIT = 0x00000001,
343 };
344 /** Container for color space flag bits */
345 using ColorSpaceFlags = uint32_t;
346
SRGBToLinearConv(const float srgb)347 inline float SRGBToLinearConv(const float srgb)
348 {
349 float col = srgb;
350 if (srgb <= 0.04045f) {
351 col *= (1.f / 12.92f);
352 } else {
353 col = Math::pow((col + 0.055f) * (1.f / 1.055f), 2.4f);
354 }
355 return col;
356 }
357
LinearToSRGBConv(const float linear)358 inline float LinearToSRGBConv(const float linear)
359 {
360 float srgb = linear;
361 if (srgb <= 0.0031308f) {
362 srgb *= 12.92f;
363 } else {
364 srgb = 1.0550000667572021484375f * Math::pow(srgb, 1.f / 2.4f) - 0.05500000715255737305f;
365 }
366 return srgb;
367 }
368
369 /** Input color in linear ARGB format (uint32_t)
370 * Output color in linear (Vec4)
371 */
MakeColorFromLinear(const uint32_t color)372 constexpr Color MakeColorFromLinear(const uint32_t color)
373 {
374 return Color(color);
375 }
376
377 /** Input color in sRGB ARGB format (uint32_t)
378 * Output color in linear (Vec4)
379 */
MakeColorFromSRGB(const uint32_t color)380 inline Color MakeColorFromSRGB(const uint32_t color)
381 {
382 uint8_t b = (color >> COLOR_SHIFT_0) & UINT8_MAX;
383 uint8_t g = (color >> COLOR_SHIFT_8) & UINT8_MAX;
384 uint8_t r = (color >> COLOR_SHIFT_16) & UINT8_MAX;
385 uint8_t a = (color >> COLOR_SHIFT_24) & UINT8_MAX;
386
387 return { SRGBToLinearConv(r / 255.0f), SRGBToLinearConv(g / 255.0f), SRGBToLinearConv(b / 255.0f), a / 255.0f };
388 }
389
390 /** Input color in linear (Vec4)
391 * Output color in linear (uint32_t)
392 */
FromColorToLinear(Color color)393 constexpr uint32_t FromColorToLinear(Color color)
394 {
395 color *= 255.f;
396 uint32_t b = ((uint32_t)color.z & UINT8_MAX) << COLOR_SHIFT_0;
397 uint32_t g = ((uint32_t)color.y & UINT8_MAX) << COLOR_SHIFT_8;
398 uint32_t r = ((uint32_t)color.x & UINT8_MAX) << COLOR_SHIFT_16;
399 uint32_t a = ((uint32_t)color.w & UINT8_MAX) << COLOR_SHIFT_24;
400
401 return a | r | g | b;
402 }
403
404 /** Input color in linear (Vec4)
405 * Output color in sRGB (uint32_t)
406 */
FromColorToSRGB(Color color)407 inline uint32_t FromColorToSRGB(Color color)
408 {
409 // rounding should be handled in some smart way to get actual min and max values too
410 uint32_t b = ((uint32_t)(LinearToSRGBConv(color.z) * 255.f) & UINT8_MAX) << COLOR_SHIFT_0;
411 uint32_t g = ((uint32_t)(LinearToSRGBConv(color.y) * 255.f) & UINT8_MAX) << COLOR_SHIFT_8;
412 uint32_t r = ((uint32_t)(LinearToSRGBConv(color.x) * 255.f) & UINT8_MAX) << COLOR_SHIFT_16;
413 uint32_t a = ((uint32_t)(color.w * 255.f) & UINT8_MAX) << COLOR_SHIFT_24;
414
415 return a | r | g | b;
416 }
417
418 /** Input color in linear (Vec4)
419 * Output color in linear (uint32_t)
420 */
FromColorRGBAToLinear(Color color)421 constexpr uint32_t FromColorRGBAToLinear(Color color)
422 {
423 color *= 255.f;
424 uint32_t r = ((uint32_t)color.x & UINT8_MAX) << COLOR_SHIFT_0;
425 uint32_t g = ((uint32_t)color.y & UINT8_MAX) << COLOR_SHIFT_8;
426 uint32_t b = ((uint32_t)color.z & UINT8_MAX) << COLOR_SHIFT_16;
427 uint32_t a = ((uint32_t)color.w & UINT8_MAX) << COLOR_SHIFT_24;
428
429 return a | b | g | r;
430 }
431
432 /** Input color in linear (Vec4)
433 * Output color in sRGB (uint32_t)
434 */
FromColorRGBAToSRGB(Color color)435 inline uint32_t FromColorRGBAToSRGB(Color color)
436 {
437 uint32_t r = ((uint32_t)(LinearToSRGBConv(color.x) * 255.0f) & UINT8_MAX) << COLOR_SHIFT_0;
438 uint32_t g = ((uint32_t)(LinearToSRGBConv(color.y) * 255.0f) & UINT8_MAX) << COLOR_SHIFT_8;
439 uint32_t b = ((uint32_t)(LinearToSRGBConv(color.z) * 255.0f) & UINT8_MAX) << COLOR_SHIFT_16;
440 uint32_t a = ((uint32_t)(color.w * 255.0f) & UINT8_MAX) << COLOR_SHIFT_24;
441
442 return a | b | g | r;
443 }
444
FromColorRGBA(const Color color,const ColorSpaceFlags flags)445 inline uint32_t FromColorRGBA(const Color color, const ColorSpaceFlags flags)
446 {
447 if (flags == 0U) {
448 return FromColorRGBAToLinear(color);
449 } else {
450 return FromColorRGBAToSRGB(color);
451 }
452 }
453
PackPremultiplyColorUnorm(Color color)454 inline constexpr uint32_t PackPremultiplyColorUnorm(Color color)
455 {
456 // premultiply colors by alpha and scale the components from 0..1 to 0..255 and pack them in a 32 bit value RGBA
457 // where R is the lowest byte and A the highest.
458 return (((uint32_t(BASE_NS::Math::clamp01(color.w) * 255.0f) & UINT8_MAX) << COLOR_SHIFT_24) |
459 ((uint32_t(BASE_NS::Math::clamp01(color.w * color.z) * 255.0f) & UINT8_MAX) << COLOR_SHIFT_16) |
460 ((uint32_t(BASE_NS::Math::clamp01(color.w * color.y) * 255.0f) & UINT8_MAX) << COLOR_SHIFT_8) |
461 ((uint32_t(BASE_NS::Math::clamp01(color.w * color.x) * 255.0f) & UINT8_MAX)));
462 }
463
464 static const Color BLACK_COLOR = MakeColorFromLinear(0xFF000000);
465 static const Color WHITE_COLOR = MakeColorFromLinear(0xFFFFFFFF);
466 BASE_END_NAMESPACE()
467
468 #endif // API_BASE_UTIL_COLOR_H
469