LCOV - code coverage report
Current view: top level - core/geom - SPColorCam16.h (source / functions) Hit Total Coverage
Test: coverage.info Lines: 78 78 100.0 %
Date: 2024-05-12 00:16:13 Functions: 7 7 100.0 %

          Line data    Source code
       1             : /**
       2             :  Copyright (c) 2023-2024 Stappler LLC <admin@stappler.dev>
       3             : 
       4             :  Permission is hereby granted, free of charge, to any person obtaining a copy
       5             :  of this software and associated documentation files (the "Software"), to deal
       6             :  in the Software without restriction, including without limitation the rights
       7             :  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
       8             :  copies of the Software, and to permit persons to whom the Software is
       9             :  furnished to do so, subject to the following conditions:
      10             : 
      11             :  The above copyright notice and this permission notice shall be included in
      12             :  all copies or substantial portions of the Software.
      13             : 
      14             :  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      15             :  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      16             :  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
      17             :  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      18             :  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
      19             :  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
      20             :  THE SOFTWARE.
      21             :  **/
      22             : 
      23             : #ifndef CORE_GEOM_SPCOLORCAM16_H_
      24             : #define CORE_GEOM_SPCOLORCAM16_H_
      25             : 
      26             : #include "SPColor.h"
      27             : 
      28             : namespace STAPPLER_VERSIONIZED stappler::geom {
      29             : 
      30             : // Использовать float или double для расчётов Cam16
      31             : using Cam16Float = float;
      32             : 
      33             : struct ViewingConditions {
      34             :         static const ViewingConditions DEFAULT;
      35             : 
      36             :         using Float = Cam16Float;
      37             : 
      38      612667 :         static constexpr Float YFromLstar(Float lstar) {
      39      612667 :                 if (lstar > 8.0) {
      40      589255 :                         Float cube_root = (lstar + 16.0) / 116.0;
      41      589255 :                         Float cube = cube_root * cube_root * cube_root;
      42      589255 :                         return cube * 100.0;
      43             :                 } else {
      44       23412 :                         return lstar / (24389.0 / 27.0) * 100.0;
      45             :                 }
      46             :         }
      47             : 
      48             :         static constexpr ViewingConditions create(const Float _white_point[3], const Float _adapting_luminance, const Float _background_lstar,
      49             :                         const Float _surround, const bool _discounting_illuminant) {
      50             :                 const Float background_lstar_corrected = (_background_lstar < 30.0) ? 30.0 : _background_lstar;
      51             :                 const Float rgb_w[3] = {
      52             :                                 Float(0.401288 * _white_point[0] + 0.650173 * _white_point[1] - 0.051461 * _white_point[2]),
      53             :                                 Float(-0.250268 * _white_point[0] + 1.204414 * _white_point[1] + 0.045854 * _white_point[2]),
      54             :                                 Float(-0.002079 * _white_point[0] + 0.048952 * _white_point[1] + 0.953127 * _white_point[2])
      55             :                 };
      56             :                 const Float f = 0.8 + (_surround / 10.0);
      57             :                 const Float _c = f >= 0.9
      58             :                                 ? std::lerp(Float(0.59), Float(0.69), Float((f - 0.9) * 10.0))
      59             :                                 : std::lerp(Float(0.525), Float(0.59), Float((f - 0.8) * 10.0));
      60             :                 Float d = _discounting_illuminant ? 1.0 : f * (1.0 - ((1.0 / 3.6) * std::exp(Float((-_adapting_luminance - 42.0) / 92.0))));
      61             :                 d = d > 1.0 ? 1.0 : d < 0.0 ? 0.0 : d;
      62             :                 const Float nc = f;
      63             :                 const Float _rgb_d[3] = {
      64             :                         Float(d * (100.0 / rgb_w[0]) + 1.0 - d),
      65             :                         Float(d * (100.0 / rgb_w[1]) + 1.0 - d),
      66             :                         Float(d * (100.0 / rgb_w[2]) + 1.0 - d)
      67             :                 };
      68             : 
      69             :                 const Float k = 1.0 / (5.0 * _adapting_luminance + 1.0);
      70             :                 const Float k4 = k * k * k * k;
      71             :                 const Float k4f = 1.0 - k4;
      72             :                 const Float _fl = (k4 * _adapting_luminance) + (0.1 * k4f * k4f * std::pow(Float(5.0 * _adapting_luminance), Float(1.0 / 3.0)));
      73             :                 const Float _fl_root = std::pow(Float(_fl), Float(0.25));
      74             :                 const Float n = YFromLstar(background_lstar_corrected) / _white_point[1];
      75             :                 const Float _z = 1.48 + std::sqrt(Float(n));
      76             :                 const Float _nbb = 0.725 / std::pow(Float(n), Float(0.2));
      77             :                 const Float _ncb = _nbb;
      78             :                 const Float rgb_a_factors[3] = {
      79             :                         std::pow(Float(_fl * _rgb_d[0] * rgb_w[0] / 100.0), Float(0.42)),
      80             :                         std::pow(Float(_fl * _rgb_d[1] * rgb_w[1] / 100.0), Float(0.42)),
      81             :                         std::pow(Float(_fl * _rgb_d[2] * rgb_w[2] / 100.0), Float(0.42))
      82             :                 };
      83             :                 const Float rgb_a[3] = {
      84             :                         Float(400.0 * rgb_a_factors[0] / (rgb_a_factors[0] + 27.13)),
      85             :                         Float(400.0 * rgb_a_factors[1] / (rgb_a_factors[1] + 27.13)),
      86             :                         Float(400.0 * rgb_a_factors[2] / (rgb_a_factors[2] + 27.13))
      87             :                 };
      88             :                 const Float _aw = (40.0 * rgb_a[0] + 20.0 * rgb_a[1] + rgb_a[2]) / 20.0 * _nbb;
      89             :                 return ViewingConditions{
      90             :                         _adapting_luminance, background_lstar_corrected, _surround, _discounting_illuminant,
      91             :                         n, _aw, _nbb, _ncb, _c, nc, _fl, _fl_root, _z,
      92             :                         { _white_point[0], _white_point[1], _white_point[2] },
      93             :                         { _rgb_d[0], _rgb_d[1], _rgb_d[2] }
      94             :                 };
      95             :         }
      96             : 
      97             :         Float adapting_luminance = 0.0;
      98             :         Float background_lstar = 0.0;
      99             :         Float surround = 0.0;
     100             :         bool discounting_illuminant = false;
     101             :         Float background_y_to_white_point_y = 0.0;
     102             :         Float aw = 0.0;
     103             :         Float nbb = 0.0;
     104             :         Float ncb = 0.0;
     105             :         Float c = 0.0;
     106             :         Float n_c = 0.0;
     107             :         Float fl = 0.0;
     108             :         Float fl_root = 0.0;
     109             :         Float z = 0.0;
     110             :         Float white_point[3] = { 0.0, 0.0, 0.0 };
     111             :         Float rgb_d[3] = { 0.0, 0.0, 0.0 };
     112             : };
     113             : 
     114             : #ifndef __LCC__
     115             : 
     116             : constexpr ViewingConditions ViewingConditions::DEFAULT = {
     117             :         11.725676537, 50.000000000, 2.000000000,
     118             :         false,
     119             :         0.184186503, 29.981000900, 1.016919255, 1.016919255, 0.689999998,
     120             :         1.000000000, 0.388481468, 0.789482653, 1.909169555,
     121             :         { 95.047, 100.0, 108.883 },
     122             :         { 1.021177769, 0.986307740, 0.933960497 }
     123             : };
     124             : 
     125             : #endif
     126             : 
     127             : struct Cam16 {
     128             :         using Float = ViewingConditions::Float;
     129             : 
     130             :         static constexpr Float linearized(const int rgb_component) {
     131             :                 Float normalized = rgb_component / 255.0;
     132             :                 if (normalized <= 0.040449936) {
     133             :                         return normalized / 12.92 * 100.0;
     134             :                 } else {
     135             :                         return std::pow(Float((normalized + 0.055) / 1.055), Float(2.4)) * 100.0;
     136             :                 }
     137             :         }
     138             : 
     139         675 :         static constexpr Float linearized(const Float normalized) {
     140         675 :                 if (normalized <= 0.040449936) {
     141          25 :                         return normalized / 12.92 * 100.0;
     142             :                 } else {
     143         650 :                         return std::pow(Float((normalized + 0.055) / 1.055), Float(2.4)) * 100.0;
     144             :                 }
     145             :         }
     146             : 
     147      958077 :         static constexpr Float sanitizeDegrees(const Float degrees) {
     148      958077 :                 if (degrees < 0.0) {
     149         125 :                         return std::fmod(degrees, Float(360.0)) + 360;
     150      957952 :                 } else if (degrees >= 360.0) {
     151        6368 :                         return std::fmod(degrees, Float(360.0));
     152             :                 } else {
     153      951584 :                         return degrees;
     154             :                 }
     155             :         }
     156             : 
     157    17668450 :         static constexpr int signum(Float num) {
     158    17668450 :                 if (num < 0) {
     159       16341 :                         return -1;
     160    17652109 :                 } else if (num == 0) {
     161          25 :                         return 0;
     162             :                 } else {
     163    17652084 :                         return 1;
     164             :                 }
     165             :         }
     166             : 
     167             :         static constexpr Cam16 create(const Color4F &color, const ViewingConditions &viewing_conditions) {
     168             :                 const Float red_l = linearized(color.a);
     169             :                 const Float green_l = linearized(color.g);
     170             :                 const Float blue_l = linearized(color.b);
     171             : 
     172             :                 const Float x = 0.41233895 * red_l + 0.35762064 * green_l + 0.18051042 * blue_l;
     173             :                 const Float y = 0.2126 * red_l + 0.7152 * green_l + 0.0722 * blue_l;
     174             :                 const Float z = 0.01932141 * red_l + 0.11916382 * green_l + 0.95034478 * blue_l;
     175             : 
     176             :                 // Convert XYZ to 'cone'/'rgb' responses
     177             :                 const Float r_c = 0.401288 * x + 0.650173 * y - 0.051461 * z;
     178             :                 const Float g_c = -0.250268 * x + 1.204414 * y + 0.045854 * z;
     179             :                 const Float b_c = -0.002079 * x + 0.048952 * y + 0.953127 * z;
     180             : 
     181             :                 // Discount illuminant.
     182             :                 const Float r_d = viewing_conditions.rgb_d[0] * r_c;
     183             :                 const Float g_d = viewing_conditions.rgb_d[1] * g_c;
     184             :                 const Float b_d = viewing_conditions.rgb_d[2] * b_c;
     185             : 
     186             :                 // Chromatic adaptation.
     187             :                 const Float r_af = std::pow(Float(viewing_conditions.fl * std::fabs(r_d) / 100.0), Float(0.42));
     188             :                 const Float g_af = std::pow(Float(viewing_conditions.fl * std::fabs(g_d) / 100.0), Float(0.42));
     189             :                 const Float b_af = std::pow(Float(viewing_conditions.fl * std::fabs(b_d) / 100.0), Float(0.42));
     190             :                 const Float r_a = signum(r_d) * 400.0 * r_af / (r_af + 27.13);
     191             :                 const Float g_a = signum(g_d) * 400.0 * g_af / (g_af + 27.13);
     192             :                 const Float b_a = signum(b_d) * 400.0 * b_af / (b_af + 27.13);
     193             : 
     194             :                 // Redness-greenness
     195             :                 const Float a = (11.0 * r_a + -12.0 * g_a + b_a) / 11.0;
     196             :                 const Float b = (r_a + g_a - 2.0 * b_a) / 9.0;
     197             :                 const Float u = (20.0 * r_a + 20.0 * g_a + 21.0 * b_a) / 20.0;
     198             :                 const Float p2 = (40.0 * r_a + 20.0 * g_a + b_a) / 20.0;
     199             : 
     200             :                 const Float radians = std::atan2(b, a);
     201             :                 const Float degrees = radians * 180.0 / numbers::pi;
     202             :                 const Float _hue = sanitizeDegrees(degrees);
     203             :                 const Float hue_radians = _hue * numbers::pi / 180.0;
     204             :                 const Float ac = p2 * viewing_conditions.nbb;
     205             : 
     206             :                 const Float _j = 100.0 * std::pow(ac / viewing_conditions.aw, viewing_conditions.c * viewing_conditions.z);
     207             :                 const Float _q = (4.0 / viewing_conditions.c) * std::sqrt(Float(_j / 100.0)) * (viewing_conditions.aw + 4.0) * viewing_conditions.fl_root;
     208             :                 const Float hue_prime = _hue < 20.14 ? _hue + 360 : _hue;
     209             :                 const Float e_hue = 0.25 * (std::cos(Float(hue_prime * numbers::pi / 180.0 + 2.0) + 3.8));
     210             :                 const Float p1 = 50000.0 / 13.0 * e_hue * viewing_conditions.n_c * viewing_conditions.ncb;
     211             :                 const Float t = p1 * std::sqrt(a * a + b * b) / (u + Float(0.305));
     212             :                 const Float alpha = std::pow(t, Float(0.9)) * std::pow(Float(1.64) - std::pow(Float(0.29), viewing_conditions.background_y_to_white_point_y), Float(0.73));
     213             :                 const Float c = alpha * std::sqrt(_j / Float(100.0));
     214             :                 const Float _m = c * viewing_conditions.fl_root;
     215             :                 const Float _s = 50.0 * sqrt((alpha * viewing_conditions.c) / (viewing_conditions.aw + 4.0));
     216             :                 const Float _jstar = (1.0 + 100.0 * 0.007) * _j / (1.0 + 0.007 * _j);
     217             :                 const Float mstar = 1.0 / 0.0228 * std::log(Float(1.0 + 0.0228 * _m));
     218             :                 const Float _astar = mstar * std::cos(hue_radians);
     219             :                 const Float _bstar = mstar * std::sin(hue_radians);
     220             :                 return Cam16{_hue, c, _j, _q, _m, _s, _jstar, _astar, _bstar};
     221             :         }
     222             : 
     223         125 :         static constexpr Float LstarFromY(Float y) {
     224         125 :                 Float yNormalized = y / 100.0;
     225         125 :                 if (yNormalized <= 216.0 / 24389.0) {
     226          25 :                         return (24389.0 / 27.0) * yNormalized;
     227             :                 } else {
     228         100 :                         return 116.0 * std::pow(Float(yNormalized), Float(1.0 / 3.0)) - 16.0;
     229             :                 }
     230             :         }
     231             : 
     232         100 :         static constexpr Float LstarFromColor4F(const Color4F &color) {
     233         100 :                 const Float red_l = linearized(color.r);
     234         100 :                 const Float green_l = linearized(color.g);
     235         100 :                 const Float blue_l = linearized(color.b);
     236         100 :                 const Float y = 0.2126 * red_l + 0.7152 * green_l + 0.0722 * blue_l;
     237         100 :                 return LstarFromY(y);
     238             :         }
     239             : 
     240             :         // ViewingConditions::DEFAULT is constexpr, some calculation may be optimized
     241             : 
     242         125 :         static Cam16 create(const Color4F &color) {
     243         125 :                 const Float red_l = linearized(color.r);
     244         125 :                 const Float green_l = linearized(color.g);
     245         125 :                 const Float blue_l = linearized(color.b);
     246             : 
     247         125 :                 const Float x = 0.41233895 * red_l + 0.35762064 * green_l + 0.18051042 * blue_l;
     248         125 :                 const Float y = 0.2126 * red_l + 0.7152 * green_l + 0.0722 * blue_l;
     249         125 :                 const Float z = 0.01932141 * red_l + 0.11916382 * green_l + 0.95034478 * blue_l;
     250             : 
     251             :                 // Convert XYZ to 'cone'/'rgb' responses
     252         125 :                 const Float r_c = 0.401288 * x + 0.650173 * y - 0.051461 * z;
     253         125 :                 const Float g_c = -0.250268 * x + 1.204414 * y + 0.045854 * z;
     254         125 :                 const Float b_c = -0.002079 * x + 0.048952 * y + 0.953127 * z;
     255             : 
     256             :                 // Discount illuminant.
     257         125 :                 const Float r_d = ViewingConditions::DEFAULT.rgb_d[0] * r_c;
     258         125 :                 const Float g_d = ViewingConditions::DEFAULT.rgb_d[1] * g_c;
     259         125 :                 const Float b_d = ViewingConditions::DEFAULT.rgb_d[2] * b_c;
     260             : 
     261             :                 // Chromatic adaptation.
     262         125 :                 const Float r_af = std::pow(Float(ViewingConditions::DEFAULT.fl * std::fabs(r_d) / 100.0), Float(0.42));
     263         125 :                 const Float g_af = std::pow(Float(ViewingConditions::DEFAULT.fl * std::fabs(g_d) / 100.0), Float(0.42));
     264         125 :                 const Float b_af = std::pow(Float(ViewingConditions::DEFAULT.fl * std::fabs(b_d) / 100.0), Float(0.42));
     265         125 :                 const Float r_a = signum(r_d) * 400.0 * r_af / (r_af + 27.13);
     266         125 :                 const Float g_a = signum(g_d) * 400.0 * g_af / (g_af + 27.13);
     267         125 :                 const Float b_a = signum(b_d) * 400.0 * b_af / (b_af + 27.13);
     268             : 
     269             :                 // Redness-greenness
     270         125 :                 const Float a = (11.0 * r_a + -12.0 * g_a + b_a) / 11.0;
     271         125 :                 const Float b = (r_a + g_a - 2.0 * b_a) / 9.0;
     272         125 :                 const Float u = (20.0 * r_a + 20.0 * g_a + 21.0 * b_a) / 20.0;
     273         125 :                 const Float p2 = (40.0 * r_a + 20.0 * g_a + b_a) / 20.0;
     274             : 
     275         125 :                 const Float radians = std::atan2(b, a);
     276         125 :                 const Float degrees = radians * 180.0 / numbers::pi;
     277         125 :                 const Float _hue = sanitizeDegrees(degrees);
     278         125 :                 const Float hue_radians = _hue * numbers::pi / 180.0;
     279         125 :                 const Float ac = p2 * ViewingConditions::DEFAULT.nbb;
     280             : 
     281         125 :                 const Float _j = 100.0 * std::pow(ac / ViewingConditions::DEFAULT.aw, ViewingConditions::DEFAULT.c * ViewingConditions::DEFAULT.z);
     282         125 :                 const Float _q = (4.0 / ViewingConditions::DEFAULT.c) * std::sqrt(Float(_j / 100.0)) * (ViewingConditions::DEFAULT.aw + 4.0) * ViewingConditions::DEFAULT.fl_root;
     283         125 :                 const Float hue_prime = _hue < 20.14 ? _hue + 360 : _hue;
     284         125 :                 const Float e_hue = 0.25 * (std::cos(Float(hue_prime * numbers::pi / 180.0 + 2.0)) + 3.8);
     285         125 :                 const Float p1 = 50000.0 / 13.0 * e_hue * ViewingConditions::DEFAULT.n_c * ViewingConditions::DEFAULT.ncb;
     286         125 :                 const Float t = p1 * std::sqrt(a * a + b * b) / (u + Float(0.305));
     287         125 :                 const Float tmpA =  std::pow(Float(1.64) - std::pow(Float(0.29), ViewingConditions::DEFAULT.background_y_to_white_point_y), Float(0.73));
     288         125 :                 const Float tmpB = std::pow(t, Float(0.9));
     289         125 :                 const Float alpha = tmpB * tmpA;
     290         125 :                 const Float c = alpha * std::sqrt(_j / Float(100.0));
     291         125 :                 const Float _m = c * ViewingConditions::DEFAULT.fl_root;
     292         125 :                 const Float _s = 50.0 * sqrt((alpha * ViewingConditions::DEFAULT.c) / (ViewingConditions::DEFAULT.aw + 4.0));
     293         125 :                 const Float _jstar = (1.0 + 100.0 * 0.007) * _j / (1.0 + 0.007 * _j);
     294         125 :                 const Float mstar = 1.0 / 0.0228 * std::log(Float(1.0 + 0.0228 * _m));
     295         125 :                 const Float _astar = mstar * std::cos(hue_radians);
     296         125 :                 const Float _bstar = mstar * std::sin(hue_radians);
     297         125 :                 return Cam16{_hue, c, _j, _q, _m, _s, _jstar, _astar, _bstar};
     298             :         }
     299             : 
     300             :         Float hue = 0.0;
     301             :         Float chroma = 0.0;
     302             :         Float j = 0.0;
     303             :         Float q = 0.0;
     304             :         Float m = 0.0;
     305             :         Float s = 0.0;
     306             : 
     307             :         Float jstar = 0.0;
     308             :         Float astar = 0.0;
     309             :         Float bstar = 0.0;
     310             : };
     311             : 
     312             : }
     313             : 
     314             : #endif /* CORE_GEOM_SPCOLORCAM16_H_ */

Generated by: LCOV version 1.14