HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
sRGB.hpp
1// Copyright Take Vos 2020.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "../float16.hpp"
8#include "../geometry/matrix.hpp"
9#include "../codec/base_n.hpp"
10#include "color.hpp"
11#include <cmath>
12#include <array>
13
14hi_warning_push();
15// C26426: Global initializer calls a non-constexpr function '...' (i.22).
16// std::pow() is not constexpr and needed to fill in the gamma conversion tables.
17hi_warning_ignore_msvc(26426);
18
19namespace hi::inline v1 {
20
21constexpr matrix3 sRGB_to_XYZ =
22 matrix3{0.41239080f, 0.35758434f, 0.18048079f, 0.21263901f, 0.71516868f, 0.07219232f, 0.01933082f, 0.11919478f, 0.95053215f};
23
24constexpr matrix3 XYZ_to_sRGB = matrix3{
25 3.24096994f,
26 -1.53738318f,
27 -0.49861076f,
28 -0.96924364f,
29 1.87596750f,
30 0.04155506f,
31 0.05563008f,
32 -0.20397696f,
33 1.05697151f};
34
35[[nodiscard]] inline float sRGB_linear_to_gamma(float u) noexcept
36{
37 if (u <= 0.0031308) {
38 return 12.92f * u;
39 } else {
40 return 1.055f * std::pow(u, 0.416f) - 0.055f;
41 }
42}
43
44[[nodiscard]] inline float sRGB_gamma_to_linear(float u) noexcept
45{
46 if (u <= 0.04045) {
47 return u / 12.92f;
48 } else {
49 return std::pow((u + 0.055f) / 1.055f, 2.4f);
50 }
51}
52
53[[nodiscard]] inline auto sRGB_linear16_to_gamma8_table_generator() noexcept
54{
56
57 for (int i = 0; i != 65536; ++i) {
58 r[i] = truncate<uint8_t>(
59 std::clamp(sRGB_linear_to_gamma(float16::from_uint16_t(narrow_cast<uint16_t>(i))), 0.0f, 1.0f) * 255.0f);
60 }
61
62 return r;
63}
64
65inline auto sRGB_linear16_to_gamma8_table = sRGB_linear16_to_gamma8_table_generator();
66
67[[nodiscard]] inline uint8_t sRGB_linear16_to_gamma8(float16 u) noexcept
68{
69 return sRGB_linear16_to_gamma8_table[u.get()];
70}
71
72[[nodiscard]] inline auto sRGB_gamma8_to_linear16_table_generator() noexcept
73{
75
76 for (int i = 0; i != 256; ++i) {
77 r[i] = static_cast<float16>(sRGB_gamma_to_linear(i / 255.0f));
78 }
79
80 return r;
81}
82
83inline auto sRGB_gamma8_to_linear16_table = sRGB_gamma8_to_linear16_table_generator();
84
85[[nodiscard]] inline float16 sRGB_gamma8_to_linear16(uint8_t u) noexcept
86{
87 return sRGB_gamma8_to_linear16_table[u];
88}
89
90[[nodiscard]] inline color color_from_sRGB(float r, float g, float b, float a) noexcept
91{
92 return color{
93 sRGB_gamma_to_linear(narrow_cast<float>(r)),
94 sRGB_gamma_to_linear(narrow_cast<float>(g)),
95 sRGB_gamma_to_linear(narrow_cast<float>(b)),
96 a};
97}
98
99[[nodiscard]] inline color color_from_sRGB(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept
100{
101 return color_from_sRGB(r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f);
102}
103
104[[nodiscard]] inline color color_from_sRGB(std::string_view str)
105{
106 auto tmp = std::string{str};
107
108 if (tmp.starts_with("#")) {
109 tmp = tmp.substr(1);
110 }
111 if (ssize(tmp) != 6 && ssize(tmp) != 8) {
112 throw parse_error(std::format("Expecting 6 or 8 hex-digit sRGB color string, got {}.", str));
113 }
114 if (ssize(tmp) == 6) {
115 tmp += "ff";
116 }
117
118 uint8_t const r = (base16::int_from_char<uint8_t>(tmp[0]) << 4) | base16::int_from_char<uint8_t>(tmp[1]);
119 uint8_t const g = (base16::int_from_char<uint8_t>(tmp[2]) << 4) | base16::int_from_char<uint8_t>(tmp[3]);
120 uint8_t const b = (base16::int_from_char<uint8_t>(tmp[4]) << 4) | base16::int_from_char<uint8_t>(tmp[5]);
121 uint8_t const a = (base16::int_from_char<uint8_t>(tmp[6]) << 4) | base16::int_from_char<uint8_t>(tmp[7]);
122 return color_from_sRGB(r, g, b, a);
123}
124
125} // namespace hi::inline v1
126
127hi_warning_pop();
T pow(T... args)