2323namespace math
2424{
2525
26+ /* *
27+ * @brief Default gamma correction value.
28+ *
29+ * @details
30+ * The default gamma correction value of 2.2 is chosen because it approximates the natural response curve of CRT
31+ * monitors, which were the most common display technology when gamma correction became standard. Modern displays
32+ * have inherited this standard, as it closely matches how human vision perceives brightness non-linearly.
33+ */
34+ constexpr float defaultGamma = 2 .2f ;
35+
2636/* *
2737 * @brief Converts linear RGBA color to the sRGB color space.
2838 * @param rgba target linear RGBA color
@@ -46,6 +56,65 @@ static f32x4 srgbToRgb(f32x4 sRGB) noexcept
4656 return r;
4757}
4858
59+ /* *
60+ * @brief Applies gamma correction to the specified color.
61+ *
62+ * @details
63+ * Gamma correction is used to adjust the brightness of an image or display to match the
64+ * nonlinear response of display devices, such as monitors. It compensates for the fact that
65+ * displays do not linearly represent the light intensity of a color.
66+ *
67+ * @param color target linear RGB color to gamma correct
68+ * @param invGamma inverse gamma correction value (1.0/x)
69+ */
70+ static f32x4 gammaCorrection (f32x4 color, float invGamma) noexcept
71+ {
72+ return f32x4 (pow (color, f32x4 (invGamma)), color.getW ());
73+ }
74+ /* *
75+ * @brief Applies gamma correction to the specified color.
76+ * @details See the @ref gammaCorrection().
77+ * @param color target linear RGB color to gamma correct
78+ */
79+ static f32x4 gammaCorrection (f32x4 color) noexcept
80+ {
81+ return f32x4 (pow (color, f32x4 (1 .0f / defaultGamma)), color.getW ());
82+ }
83+
84+ /* *
85+ * @brief Applies gamma correction to the specified color. (Fast approximation)
86+ * @details See the @ref gammaCorrection().
87+ *
88+ * @param color target linear RGB color to gamma correct
89+ * @param invGamma inverse gamma correction value (1.0/x)
90+ */
91+ static f32x4 fastGammaCorrection (f32x4 color, float invGamma) noexcept
92+ {
93+ return f32x4 (fastPow (color, f32x4 (invGamma)), color.getW ());
94+ }
95+ /* *
96+ * @brief Applies gamma correction to the specified color. (Fast approximation)
97+ * @details See the @ref gammaCorrection().
98+ * @param color target linear RGB color to gamma correct
99+ */
100+ static f32x4 fastGammaCorrection (f32x4 color) noexcept
101+ {
102+ return f32x4 (fastPow (color, f32x4 (1 .0f / defaultGamma)), color.getW ());
103+ }
104+
105+ /* *
106+ * @brief Calculates relative luminance of a color. (Rec. 709)
107+ * @param x target linear color
108+ */
109+ static float calcLum (f32x4 x) noexcept { return dot3 (x, f32x4 (0 .2126f , 0 .7152f , 0 .0722f )); }
110+ /* *
111+ * @brief Calculates perceptual brightness (Luma) of a color. (Rec. 709)
112+ * @param rgb target linear RGB color
113+ */
114+ static float rgbToLuma (f32x4 rgb) noexcept { return calcLum (fastGammaCorrection (rgb)); }
115+
116+ // **********************************************************************************************************************
117+ // Linear sRGB <-> CIE XYZ
49118static const f32x4x4 rgbToXyzMat = f32x4x4
50119(
51120 0 .41239079926595934f , 0 .21263900587151027f , 0 .01933081871559182f , 0 .0f ,
@@ -65,12 +134,12 @@ static const f32x4x4 xyzToRgbMat = f32x4x4
65134 * @brief Converts linear sRGB color to the CIE XYZ color space.
66135 * @param rgb target linear sRGB color
67136 */
68- static f32x4 rgbToXyz (f32x4 rgb) noexcept { return multiply3x3 (rgbToXyzMat, rgb); }
137+ static f32x4 rgbToXyz (f32x4 rgb) noexcept { return dot3x3 (rgbToXyzMat, rgb); }
69138/* *
70139 * @brief Converts CIE XYZ color to the linear sRGB color space.
71140 * @param xyz target CIE XYZ color
72141 */
73- static f32x4 xyzToRgb (f32x4 xyz) noexcept { return multiply3x3 (xyzToRgbMat, xyz); }
142+ static f32x4 xyzToRgb (f32x4 xyz) noexcept { return dot3x3 (xyzToRgbMat, xyz); }
74143
75144/* *
76145 * @brief Converts CIE XYZ color to the CIE xyY color space.
@@ -102,4 +171,47 @@ static f32x4 rgbToXyy(f32x4 rgb) noexcept { return xyzToXyy(rgbToXyz(rgb)); }
102171 */
103172static f32x4 xyyToRgb (f32x4 xyy) noexcept { return xyzToRgb (xyyToXyz (xyy)); }
104173
174+ // **********************************************************************************************************************
175+ // Linear sRGB <-> LogLuv
176+ static const f32x4x4 rgbToLogLuvMat = f32x4x4
177+ (
178+ 0 .2209f , 0 .1138f , 0 .0102f , 0 .0f ,
179+ 0 .3390f , 0 .6780f , 0 .1130f , 0 .0f ,
180+ 0 .4184f , 0 .7319f , 0 .2969f , 0 .0f ,
181+ 0 .0f , 0 .0f , 0 .0f , 0 .0f
182+ );
183+ static const f32x4x4 logLuvToRgbMat = f32x4x4
184+ (
185+ 6 .0014f , -1 .3320f , 0 .3008f , 0 .0f ,
186+ -2 .7008f , 3 .1029f , -1 .0882f , 0 .0f ,
187+ -1 .7996f , -5 .7721f , 5 .6268f , 0 .0f ,
188+ 0 .0f , 0 .0f , 0 .0f , 0 .0f
189+ );
190+
191+ /* *
192+ * @brief Encodes linear RGB color (HDR) to the LogLuv format.
193+ * @param rgb target linear RGB color
194+ */
195+ static uint32 rgbToLogLuv (f32x4 rgb) noexcept
196+ {
197+ auto luv = max (dot3x3 (rgbToLogLuvMat, rgb), f32x4 (1e-6f ));
198+ auto uv = (uint2)fma (saturate ((float2)luv / luv.getZ ()), float2 (255 .0f ), float2 (0 .5f ));
199+ auto logLuv = (uint32)std::fma (saturate (std::fma (std::log2 (
200+ luv.getY ()), 1 .0f / 64 .0f , 0 .5f )), 65535 .0f , 0 .5f );
201+ logLuv |= (uv.x << 24u ) | (uv.y << 16u );
202+ return dot3 (rgb, rgb) > 0 .0f ? logLuv : 0 ;
203+ }
204+ /* *
205+ * @brief Decodes linear RGB color (HDR) from the LogLuv format.
206+ * @param logLuv target LogLuv encoded color
207+ */
208+ static f32x4 logLuvToRgb (uint32 logLuv)
209+ {
210+ if (logLuv == 0 ) return f32x4::zero;
211+ f32x4 luv; auto uv = float2 (uint2 (logLuv >> 24u , logLuv >> 16u ) & 255u ) * (1 .0f / 255 .0f );
212+ luv.floats .y = std::exp2 (std::fma (logLuv & 65535u , (1 .0f / 65535 .0f ) * 64 .0f , -32 .0f ));
213+ luv.floats .z = luv.floats .y / uv.y ; luv.floats .x = luv.floats .z * uv.x ;
214+ return max (dot3x3 (logLuvToRgbMat, luv), f32x4::zero);
215+ }
216+
105217} // namespace math
0 commit comments