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