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 © 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(QX_TEXT("0x")) && (svColorName.size() == 8 || svColorName.size() == 10);
356 
357  if (bStartsWith0x || svColorName.starts_with(QX_TEXT("#")) && 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>(QX_TEXT("%llx")))
368  return color(*optInt);
369 
370  return std::nullopt;
371  };
372 
373  if (sColorName.length() == 6)
374  {
375  sColorName += QX_TEXT("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::empty() noexcept
388 {
389  return color(0, 0, 0, 0);
390 }
391 
392 constexpr size_t color::size() noexcept
393 {
394  return 4;
395 }
396 
397 constexpr float color::clamp_value(float fValue) noexcept
398 {
399  return std::clamp(fValue, 0.f, 1.f);
400 }
401 
402 constexpr float color::dec_to_float(int nValue) noexcept
403 {
404  return static_cast<float>(nValue) / 255.f;
405 }
406 
407 constexpr int color::float_to_dec(float fValue) noexcept
408 {
409  return static_cast<int>(fValue * 255.f);
410 }
411 
412 constexpr void color::assign_checked(const glm::vec4& other) noexcept
413 {
414  assign_component_checked(m_Color.x, other.x);
415  assign_component_checked(m_Color.y, other.y);
416  assign_component_checked(m_Color.z, other.z);
417  assign_component_checked(m_Color.w, other.w);
418 }
419 
420 constexpr void color::assign_component_checked(float& pComponent, float fValue) noexcept
421 {
422  pComponent = clamp_value(fValue);
423 }
424 
425 } // namespace qx
426 
427 template<class char_t>
428 struct std::formatter<qx::color, char_t>
429 {
430  template<class format_parse_context_t>
431  constexpr auto parse(format_parse_context_t& ctx)
432  {
433  auto it = ctx.begin();
434  if (it != ctx.end())
435  {
436  if (*it == QX_CHAR_PREFIX(char_t, 's'))
437  {
438  ++it;
439  optColorNameType = qx::color_name_type::css_snake;
440  }
441  else if (*it == QX_CHAR_PREFIX(char_t, 'p'))
442  {
443  ++it;
444  optColorNameType = qx::color_name_type::css_pascal;
445  }
446  else if (*it == QX_CHAR_PREFIX(char_t, 'x'))
447  {
448  ++it;
449  optColorNameType = qx::color_name_type::hex_lower;
450  }
451  else if (*it == QX_CHAR_PREFIX(char_t, 'X'))
452  {
453  ++it;
454  optColorNameType = qx::color_name_type::hex_upper;
455  }
456  else if (*it == QX_CHAR_PREFIX(char_t, 'r'))
457  {
458  ++it;
459  optColorNameType = qx::color_name_type::rgb;
460  }
461 
462  if (it != ctx.end())
463  {
464  if (*it == QX_CHAR_PREFIX(char_t, 'a'))
465  {
466  ++it;
467  bAddAlpha = true;
468  }
469  }
470  }
471 
472  if (it != ctx.end() && *it != QX_CHAR_PREFIX(char_t, '}'))
473  throw std::format_error("unknown spec");
474 
475  return it;
476  }
477 
478  template<class format_context_t>
479  auto format(const qx::color& color, format_context_t& ctx) const
480  {
481  const qx::color_name_type eColorNameType =
482  optColorNameType ? *optColorNameType : qx::color_name_type::hex_upper;
483 
484  std::optional<qx::basic_string_view<char_t>> optResult =
486 
487  if (optResult)
488  {
489  return std::format_to(ctx.out(), QX_STR_PREFIX(char_t, "{}"), *optResult);
490  }
491  else
492  {
493  if (eColorNameType == qx::color_name_type::rgb)
494  {
495  auto it = std::format_to(
496  ctx.out(),
497  QX_STR_PREFIX(char_t, "{},{},{}"),
498  color.r_dec(),
499  color.g_dec(),
500  color.b_dec());
501 
502  if (bAddAlpha)
503  it = std::format_to(ctx.out(), QX_STR_PREFIX(char_t, ",{}"), color.a_dec());
504 
505  return it;
506  }
507  else if (eColorNameType == qx::color_name_type::hex_lower)
508  {
509  auto it = std::format_to(
510  ctx.out(),
511  QX_STR_PREFIX(char_t, "{:x}{:x}{:x}"),
512  color.r_dec(),
513  color.g_dec(),
514  color.b_dec());
515 
516  if (bAddAlpha)
517  it = std::format_to(ctx.out(), QX_STR_PREFIX(char_t, "{:x}"), color.a_dec());
518 
519  return it;
520  }
521  else
522  {
523  // hex_upper as a fallback
524  auto it = std::format_to(
525  ctx.out(),
526  QX_STR_PREFIX(char_t, "{:X}{:X}{:X}"),
527  color.r_dec(),
528  color.g_dec(),
529  color.b_dec());
530 
531  if (bAddAlpha || !optColorNameType)
532  it = std::format_to(ctx.out(), QX_STR_PREFIX(char_t, "{:X}"), color.a_dec());
533 
534  return it;
535  }
536  }
537  }
538 
539  std::optional<qx::color_name_type> optColorNameType;
540  bool bAddAlpha = false;
541 };
String class.
Definition: string.h:64
size_type length() const noexcept
Get string length.
Definition: string.inl:246
RGBA color.
Definition: color.h:193
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 empty() noexcept
Get empty color (0, 0, 0, 0)
Definition: color.inl:387
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:392
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:261
#define QX_STR_PREFIX(value_t, str)
Chose witch of prefixes add to string : L or none.
Definition: string_utils.h:253
std::uint64_t u64
Definition: typedefs.h:26