qxLib
color.inl
Go to the documentation of this file.
1 /**
2 
3  @file color.inl
4  @author Khrapov
5  @date 10.04.2021
6  @copyright (c) Nick Khrapov, 2021. All right reserved.
7 
8 **/
9 
10 template<>
11 struct std::hash<qx::color>
12 {
13  constexpr size_t operator()(const qx::color& color) const noexcept
14  {
15  size_t nHash = 0;
16  qx::hash_combine(nHash, color.r());
17  qx::hash_combine(nHash, color.g());
18  qx::hash_combine(nHash, color.b());
19  qx::hash_combine(nHash, color.a());
20  return nHash;
21  }
22 };
23 
24 namespace qx
25 {
26 
27 namespace details
28 {
29 
30 /**
31 
32  @class string_to_color_converter
33  @brief Helper class for string -> color conversion
34  @details The only purpose of this class is to hide the map from a user
35  @tparam char_t - string char type
36  @author Khrapov
37  @date 5.04.2023
38 
39 **/
40 template<class char_t>
41 class string_to_color_converter final : public singleton<string_to_color_converter<char_t>>
42 {
43  friend color;
44 
45 #define _QX_ADD_COLOR(snakeCaseName, pascalCaseName, r, g, b) \
46  add(QX_STR_PREFIX(char_t, #snakeCaseName), color_name_type::css_snake, color(r, g, b)); \
47  add(QX_STR_PREFIX(char_t, #pascalCaseName), color_name_type::css_pascal, color(r, g, b));
48 
49 public:
50  string_to_color_converter() noexcept;
51 
52  /**
53  @brief Add new color to the mapping
54  @param sName - name registered for forward lookup
55  @param eColorNameType - css_snake or css_pascal selects the reverse lookup table;
56  other formats only update the forward lookup
57  @param color - RGBA value associated with sName; an existing entry with
58  the same key is replaced
59  **/
60  void add(basic_string<char_t> sName, color_name_type eColorNameType, color color) noexcept;
61 
62  /**
63  @brief Try to get ac olor from a color name
64  @param sName - color name
65  @retval - color or nullopt
66  **/
67  std::optional<color> get(const basic_string<char_t>& sName) const noexcept;
68 
69  /**
70  @brief Try to a color name get from a color
71  @param color - RGBA lookup key
72  @param eColorNameType - css_snake or css_pascal selects the reverse lookup table
73  @retval - registered name, or nullopt if the key is absent or
74  the requested name format is unsupported
75  **/
76  std::optional<basic_string_view<char_t>> get(const color& color, color_name_type eColorNameType) const noexcept;
77 
78 private:
79  std::unordered_map<basic_string<char_t>, color> m_StringToColor;
80  std::unordered_map<color, basic_string<char_t>> m_ColorToCssSnake;
81  std::unordered_map<color, basic_string<char_t>> m_ColorToCssPascal;
82 };
83 
84 template<class char_t>
86 {
87  _QX_COLORS(_QX_ADD_COLOR)
88 }
89 
90 template<class char_t>
93  color_name_type eColorNameType,
94  color color) noexcept
95 {
96  if (eColorNameType == color_name_type::css_snake)
97  m_ColorToCssSnake[color] = sName;
98  else if (eColorNameType == color_name_type::css_pascal)
99  m_ColorToCssPascal[color] = sName;
100 
101  m_StringToColor[sName] = std::move(color);
102 }
103 
104 template<class char_t>
105 inline std::optional<color> string_to_color_converter<char_t>::get(const basic_string<char_t>& sName) const noexcept
106 {
107  if (const auto it = m_StringToColor.find(sName); it != m_StringToColor.end())
108  return it->second;
109 
110  return std::nullopt;
111 }
112 
113 template<class char_t>
114 inline std::optional<basic_string_view<char_t>> string_to_color_converter<char_t>::get(
115  const color& color,
116  color_name_type eColorNameType) const noexcept
117 {
118  if (eColorNameType == color_name_type::css_snake)
119  {
120  if (const auto it = m_ColorToCssSnake.find(color); it != m_ColorToCssSnake.end())
121  return it->second;
122  }
123  else if (eColorNameType == color_name_type::css_pascal)
124  {
125  if (const auto it = m_ColorToCssPascal.find(color); it != m_ColorToCssPascal.end())
126  return it->second;
127  }
128 
129  return std::nullopt;
130 }
131 
132 } // namespace details
133 
134 constexpr color::color(float fRed, float fGreen, float fBlue, float fAlpha) noexcept
135 {
136  assign_checked({ fRed, fGreen, fBlue, fAlpha });
137 }
138 
139 constexpr color::color(int nRed, int nGreen, int nBlue, int nAlpha) noexcept
140  : color(dec_to_float(nRed), dec_to_float(nGreen), dec_to_float(nBlue), dec_to_float(nAlpha))
141 {
142 }
143 
144 constexpr color::color(u64 nHexValue) noexcept
145  : color(
146  dec_to_float(nHexValue >> 24 & 0xFF),
147  dec_to_float(nHexValue >> 16 & 0xFF),
148  dec_to_float(nHexValue >> 8 & 0xFF),
149  dec_to_float(nHexValue >> 0 & 0xFF))
150 {
151 }
152 
153 constexpr color::color(const glm::ivec3& vec3) noexcept
154  : color(dec_to_float(vec3.x), dec_to_float(vec3.y), dec_to_float(vec3.z), 1.f)
155 {
156 }
157 
158 constexpr color::color(const glm::ivec4& vec4) noexcept
159  : color(dec_to_float(vec4.x), dec_to_float(vec4.y), dec_to_float(vec4.z), dec_to_float(vec4.w))
160 {
161 }
162 
163 constexpr float color::r() const noexcept
164 {
165  return m_Color.x;
166 }
167 
168 constexpr float color::g() const noexcept
169 {
170  return m_Color.y;
171 }
172 
173 constexpr float color::b() const noexcept
174 {
175  return m_Color.z;
176 }
177 
178 constexpr float color::a() const noexcept
179 {
180  return m_Color.w;
181 }
182 
183 constexpr float& color::operator[](size_t i) noexcept
184 {
185  return m_Color[static_cast<glm::length_t>(i)];
186 }
187 
188 constexpr const float& color::operator[](size_t i) const noexcept
189 {
190  return m_Color[static_cast<glm::length_t>(i)];
191 }
192 
193 constexpr int color::r_dec() const noexcept
194 {
195  return float_to_dec(m_Color.x);
196 }
197 
198 constexpr int color::g_dec() const noexcept
199 {
200  return float_to_dec(m_Color.y);
201 }
202 
203 constexpr int color::b_dec() const noexcept
204 {
205  return float_to_dec(m_Color.z);
206 }
207 
208 constexpr int color::a_dec() const noexcept
209 {
210  return float_to_dec(m_Color.w);
211 }
212 
213 constexpr const float* color::data() const noexcept
214 {
215  return &(m_Color.x);
216 }
217 
218 constexpr unsigned int color::hex_rgb() const noexcept
219 {
220  const unsigned int r = static_cast<unsigned int>(float_to_dec(m_Color.x));
221  const unsigned int g = static_cast<unsigned int>(float_to_dec(m_Color.y));
222  const unsigned int b = static_cast<unsigned int>(float_to_dec(m_Color.z));
223 
224  return ((r & 0xff) << 16) + ((g & 0xff) << 8) + ((b & 0xff) << 0);
225 }
226 
227 constexpr unsigned int color::hex_rgba() const noexcept
228 {
229  const unsigned int r = static_cast<unsigned int>(float_to_dec(m_Color.x));
230  const unsigned int g = static_cast<unsigned int>(float_to_dec(m_Color.y));
231  const unsigned int b = static_cast<unsigned int>(float_to_dec(m_Color.z));
232  const unsigned int a = static_cast<unsigned int>(float_to_dec(m_Color.w));
233 
234  return ((r & 0xff) << 24) + ((g & 0xff) << 16) + ((b & 0xff) << 8) + (a & 0xff);
235 }
236 
237 constexpr unsigned int color::hex_argb() const noexcept
238 {
239  const unsigned int r = static_cast<unsigned int>(float_to_dec(m_Color.x));
240  const unsigned int g = static_cast<unsigned int>(float_to_dec(m_Color.y));
241  const unsigned int b = static_cast<unsigned int>(float_to_dec(m_Color.z));
242  const unsigned int a = static_cast<unsigned int>(float_to_dec(m_Color.w));
243 
244  return ((a & 0xff) << 24) + ((r & 0xff) << 16) + ((g & 0xff) << 8) + (b & 0xff);
245 }
246 
247 constexpr bool color::operator==(const color& other) const noexcept
248 {
249  return m_Color == other.m_Color;
250 }
251 
252 constexpr color::operator glm::vec3() const noexcept
253 {
254  return m_Color;
255 }
256 
257 constexpr color::operator glm::vec4() const noexcept
258 {
259  return m_Color;
260 }
261 
262 constexpr void color::update_r(float fDeltaValue) noexcept
263 {
264  assign_component_checked(m_Color.x, m_Color.x + fDeltaValue);
265 }
266 
267 constexpr void color::update_g(float fDeltaValue) noexcept
268 {
269  assign_component_checked(m_Color.y, m_Color.y + fDeltaValue);
270 }
271 
272 constexpr void color::update_b(float fDeltaValue) noexcept
273 {
274  assign_component_checked(m_Color.z, m_Color.z + fDeltaValue);
275 }
276 
277 constexpr void color::update_a(float fDeltaValue) noexcept
278 {
279  assign_component_checked(m_Color.w, m_Color.w + fDeltaValue);
280 }
281 
282 constexpr void color::update_r_dec(int nDeltaValue) noexcept
283 {
284  assign_component_checked(m_Color.x, m_Color.x + dec_to_float(nDeltaValue));
285 }
286 
287 constexpr void color::update_g_dec(int nDeltaValue) noexcept
288 {
289  assign_component_checked(m_Color.y, m_Color.y + dec_to_float(nDeltaValue));
290 }
291 
292 constexpr void color::update_b_dec(int nDeltaValue) noexcept
293 {
294  assign_component_checked(m_Color.z, m_Color.z + dec_to_float(nDeltaValue));
295 }
296 
297 constexpr void color::update_a_dec(int nDeltaValue) noexcept
298 {
299  assign_component_checked(m_Color.w, m_Color.w + dec_to_float(nDeltaValue));
300 }
301 
302 constexpr void color::set_r(float fValue) noexcept
303 {
304  assign_component_checked(m_Color.x, fValue);
305 }
306 
307 constexpr void color::set_g(float fValue) noexcept
308 {
309  assign_component_checked(m_Color.y, fValue);
310 }
311 
312 constexpr void color::set_b(float fValue) noexcept
313 {
314  assign_component_checked(m_Color.z, fValue);
315 }
316 
317 constexpr void color::set_a(float fValue) noexcept
318 {
319  assign_component_checked(m_Color.w, fValue);
320 }
321 
322 constexpr void color::set_r_dec(int nValue) noexcept
323 {
324  assign_component_checked(m_Color.x, dec_to_float(nValue));
325 }
326 
327 constexpr void color::set_g_dec(int nValue) noexcept
328 {
329  assign_component_checked(m_Color.y, dec_to_float(nValue));
330 }
331 
332 constexpr void color::set_b_dec(int nValue) noexcept
333 {
334  assign_component_checked(m_Color.z, dec_to_float(nValue));
335 }
336 
337 constexpr void color::set_a_dec(int nValue) noexcept
338 {
339  assign_component_checked(m_Color.w, dec_to_float(nValue));
340 }
341 
342 constexpr void color::darken(float fPercent) noexcept
343 {
344  brighten(-fPercent);
345 }
346 
347 constexpr void color::brighten(float fPercent) noexcept
348 {
349  m_Color.x *= (100.f + fPercent) / 100.f;
350  m_Color.y *= (100.f + fPercent) / 100.f;
351  m_Color.z *= (100.f + fPercent) / 100.f;
352 }
353 
354 constexpr color color::darken(const color& other, float fPercent) noexcept
355 {
356  color ret = other;
357  ret.darken(fPercent);
358  return ret;
359 }
360 
361 constexpr color color::brighten(const color& other, float fPercent) noexcept
362 {
363  color ret = other;
364  ret.brighten(fPercent);
365  return ret;
366 }
367 
368 inline std::optional<color> color::from_string(string_view svColorName) noexcept
369 {
370  if (const auto optColor = details::string_to_color_converter<char_type>::get_instance().get(svColorName))
371  return *optColor;
372 
373  std::optional<color> optColor;
374 
375  const bool bStartsWith0x =
376  svColorName.starts_with(QXT("0x")) && (svColorName.size() == 8 || svColorName.size() == 10);
377 
378  if (bStartsWith0x || svColorName.starts_with(QXT("#")) && svColorName.size() == 7)
379  {
380  // TODO no conversion method for string view so far
381  // but with SSO this should be fine
382  const size_t nOffset = bStartsWith0x ? 2 : 1;
383  string sColorName = string_view(svColorName.data() + nOffset, svColorName.size() - nOffset);
384 
385  auto ReadHex = [](string& s) -> std::optional<color>
386  {
387  s.to_lower();
388  if (const auto optInt = s.template to<u64>(QXT("%llx")))
389  return color(*optInt);
390 
391  return std::nullopt;
392  };
393 
394  if (sColorName.length() == 6)
395  {
396  sColorName += QXT("FF");
397  optColor = ReadHex(sColorName);
398  }
399  else if (sColorName.length() == 8)
400  {
401  optColor = ReadHex(sColorName);
402  }
403  }
404 
405  return optColor;
406 }
407 
408 constexpr color color::from_hash(size_t nHash) noexcept
409 {
410  return color(
411  static_cast<u8>(nHash >> 0 & 0xFF),
412  static_cast<u8>(nHash >> 8 & 0xFF),
413  static_cast<u8>(nHash >> 16 & 0xFF));
414 }
415 
416 constexpr color color::empty() noexcept
417 {
418  return color(0, 0, 0, 0);
419 }
420 
421 constexpr size_t color::size() noexcept
422 {
423  return glm::vec4::length();
424 }
425 
426 constexpr float color::clamp_value(float fValue) noexcept
427 {
428  return std::clamp(fValue, 0.f, 1.f);
429 }
430 
431 constexpr float color::dec_to_float(int nValue) noexcept
432 {
433  return static_cast<float>(nValue) / 255.f;
434 }
435 
436 constexpr int color::float_to_dec(float fValue) noexcept
437 {
438  // Saturate to the channel range and map NaN to zero before converting.
439  if (!(fValue > 0.f))
440  return 0;
441 
442  if (fValue >= 1.f)
443  return 255;
444 
445  return static_cast<int>(fValue * 255.f); //-V2003
446 }
447 
448 constexpr void color::assign_checked(const glm::vec4& other) noexcept
449 {
450  assign_component_checked(m_Color.x, other.x);
451  assign_component_checked(m_Color.y, other.y);
452  assign_component_checked(m_Color.z, other.z);
453  assign_component_checked(m_Color.w, other.w);
454 }
455 
456 constexpr void color::assign_component_checked(float& pComponent, float fValue) noexcept
457 {
458  pComponent = clamp_value(fValue);
459 }
460 
461 } // namespace qx
462 
463 template<class char_t>
464 struct QX_FMT_NS::formatter<qx::color, char_t>
465 {
466  template<class format_parse_context_t>
467  constexpr auto parse(format_parse_context_t& ctx)
468  {
469  auto it = ctx.begin();
470  if (it != ctx.end())
471  {
472  if (*it == QX_CHAR_PREFIX(char_t, 's'))
473  {
474  ++it;
475  optColorNameType = qx::color_name_type::css_snake;
476  }
477  else if (*it == QX_CHAR_PREFIX(char_t, 'p'))
478  {
479  ++it;
480  optColorNameType = qx::color_name_type::css_pascal;
481  }
482  else if (*it == QX_CHAR_PREFIX(char_t, 'x'))
483  {
484  ++it;
485  optColorNameType = qx::color_name_type::hex_lower;
486  }
487  else if (*it == QX_CHAR_PREFIX(char_t, 'X'))
488  {
489  ++it;
490  optColorNameType = qx::color_name_type::hex_upper;
491  }
492  else if (*it == QX_CHAR_PREFIX(char_t, 'r'))
493  {
494  ++it;
495  optColorNameType = qx::color_name_type::rgb;
496  }
497 
498  if (it != ctx.end())
499  {
500  if (*it == QX_CHAR_PREFIX(char_t, 'a'))
501  {
502  ++it;
503  bAddAlpha = true;
504  }
505  }
506  }
507 
508  if (it != ctx.end() && *it != QX_CHAR_PREFIX(char_t, '}'))
509  throw QX_FMT_NS::format_error("unknown spec");
510 
511  return it;
512  }
513 
514  template<class format_context_t>
515  auto format(const qx::color& color, format_context_t& ctx) const -> decltype(ctx.out())
516  {
517  const qx::color_name_type eColorNameType =
518  optColorNameType ? *optColorNameType : qx::color_name_type::hex_upper;
519 
520  std::optional<qx::basic_string_view<char_t>> optResult =
522 
523  if (optResult)
524  {
525  return QX_FMT_NS::format_to(ctx.out(), QX_STR_PREFIX(char_t, "{}"), *optResult);
526  }
527  else
528  {
529  if (eColorNameType == qx::color_name_type::rgb)
530  {
531  auto it = QX_FMT_NS::format_to(
532  ctx.out(),
533  QX_STR_PREFIX(char_t, "{},{},{}"),
534  color.r_dec(),
535  color.g_dec(),
536  color.b_dec());
537 
538  if (bAddAlpha)
539  it = QX_FMT_NS::format_to(ctx.out(), QX_STR_PREFIX(char_t, ",{}"), color.a_dec());
540 
541  return it;
542  }
543  else if (eColorNameType == qx::color_name_type::hex_lower)
544  {
545  auto it = QX_FMT_NS::format_to(
546  ctx.out(),
547  QX_STR_PREFIX(char_t, "{:x}{:x}{:x}"),
548  color.r_dec(),
549  color.g_dec(),
550  color.b_dec());
551 
552  if (bAddAlpha)
553  it = QX_FMT_NS::format_to(ctx.out(), QX_STR_PREFIX(char_t, "{:x}"), color.a_dec());
554 
555  return it;
556  }
557  else
558  {
559  // hex_upper as a fallback
560  auto it = QX_FMT_NS::format_to(
561  ctx.out(),
562  QX_STR_PREFIX(char_t, "{:X}{:X}{:X}"),
563  color.r_dec(),
564  color.g_dec(),
565  color.b_dec());
566 
567  if (bAddAlpha || !optColorNameType)
568  it = QX_FMT_NS::format_to(ctx.out(), QX_STR_PREFIX(char_t, "{:X}"), color.a_dec());
569 
570  return it;
571  }
572  }
573  }
574 
575  std::optional<qx::color_name_type> optColorNameType;
576  bool bAddAlpha = false;
577 };
String class.
Definition: string.h:83
size_type length() const noexcept
Get string length.
Definition: string.inl:267
RGBA color.
Definition: color.h:195
constexpr float g() const noexcept
Get green component.
Definition: color.inl:168
constexpr void update_r_dec(int nDeltaValue) noexcept
Add value to red component.
Definition: color.inl:282
constexpr void update_a(float fDeltaValue) noexcept
Add value to alpha component.
Definition: color.inl:277
constexpr int r_dec() const noexcept
Get red component as decimal.
Definition: color.inl:193
static std::optional< color > from_string(string_view svColorName) noexcept
Try to create color from string.
Definition: color.inl:368
constexpr void update_g(float fDeltaValue) noexcept
Add value to green component.
Definition: color.inl:267
constexpr void update_b(float fDeltaValue) noexcept
Add value to blue component.
Definition: color.inl:272
constexpr void darken(float fPercent) noexcept
Make color darker.
Definition: color.inl:342
constexpr int g_dec() const noexcept
Get green component as decimal.
Definition: color.inl:198
constexpr float a() const noexcept
Get alpha component.
Definition: color.inl:178
constexpr const float * data() const noexcept
Get pointer to the first component.
Definition: color.inl:213
constexpr float b() const noexcept
Get blue component.
Definition: color.inl:173
constexpr float r() const noexcept
Get red component.
Definition: color.inl:163
constexpr void set_g(float fValue) noexcept
Set new value of green component.
Definition: color.inl:307
constexpr float & operator[](size_t i) noexcept
Get color component.
Definition: color.inl:183
constexpr void set_b(float fValue) noexcept
Set new value of blue component.
Definition: color.inl:312
constexpr int b_dec() const noexcept
Get blue component as decimal.
Definition: color.inl:203
static constexpr color from_hash(size_t nHash) noexcept
Generate a color unique for a hash value.
Definition: color.inl:408
static constexpr color empty() noexcept
Get empty color (0, 0, 0, 0)
Definition: color.inl:416
constexpr void update_g_dec(int nDeltaValue) noexcept
Add value to green component.
Definition: color.inl:287
constexpr void set_r(float fValue) noexcept
Set new value of red component.
Definition: color.inl:302
constexpr unsigned int hex_rgba() const noexcept
Get color as hex.
Definition: color.inl:227
constexpr unsigned int hex_rgb() const noexcept
Get color as hex.
Definition: color.inl:218
constexpr void update_r(float fDeltaValue) noexcept
Add value to red component.
Definition: color.inl:262
constexpr int a_dec() const noexcept
Get alpha component as decimal.
Definition: color.inl:208
constexpr void set_g_dec(int nValue) noexcept
Set new value of green component.
Definition: color.inl:327
static constexpr size_t size() noexcept
Get number of float components.
Definition: color.inl:421
constexpr void brighten(float fPercent) noexcept
Make color brighter.
Definition: color.inl:347
constexpr unsigned int hex_argb() const noexcept
Get color as hex.
Definition: color.inl:237
constexpr void update_b_dec(int nDeltaValue) noexcept
Add value to blue component.
Definition: color.inl:292
constexpr void set_r_dec(int nValue) noexcept
Set new value of red component.
Definition: color.inl:322
constexpr void set_b_dec(int nValue) noexcept
Set new value of blue component.
Definition: color.inl:332
constexpr void update_a_dec(int nDeltaValue) noexcept
Add value to alpha component.
Definition: color.inl:297
constexpr void set_a(float fValue) noexcept
Set new value of alpha component.
Definition: color.inl:317
constexpr void set_a_dec(int nValue) noexcept
Set new value of alpha component.
Definition: color.inl:337
Helper class for string -> color conversion.
Definition: color.inl:42
std::optional< color > get(const basic_string< char_t > &sName) const noexcept
Try to get ac olor from a color name.
Definition: color.inl:105
void add(basic_string< char_t > sName, color_name_type eColorNameType, color color) noexcept
Add new color to the mapping.
Definition: color.inl:91
Inherit the necessary singleton class from this.
#define _QX_COLORS(macro)
Definition: color.h:28
#define QX_CHAR_PREFIX(value_t, ch)
Chose witch of prefixes add to char : L or none.
Definition: string_utils.h:128
#define QX_STR_PREFIX(value_t, str)
Chose witch of prefixes add to string : L or none.
Definition: string_utils.h:120
std::uint8_t u8
0 .. 65 535
Definition: typedefs.h:19
std::uint64_t u64
Definition: typedefs.h:26