1*970e1046SAndroid Build Coastguard Worker /* 2*970e1046SAndroid Build Coastguard Worker * Copyright 2021 Google LLC 3*970e1046SAndroid Build Coastguard Worker * 4*970e1046SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License"); 5*970e1046SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License. 6*970e1046SAndroid Build Coastguard Worker * You may obtain a copy of the License at 7*970e1046SAndroid Build Coastguard Worker * 8*970e1046SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0 9*970e1046SAndroid Build Coastguard Worker * 10*970e1046SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software 11*970e1046SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS, 12*970e1046SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*970e1046SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and 14*970e1046SAndroid Build Coastguard Worker * limitations under the License. 15*970e1046SAndroid Build Coastguard Worker */ 16*970e1046SAndroid Build Coastguard Worker 17*970e1046SAndroid Build Coastguard Worker // This file is automatically generated. Do not modify it. 18*970e1046SAndroid Build Coastguard Worker 19*970e1046SAndroid Build Coastguard Worker package com.google.ux.material.libmonet.hct; 20*970e1046SAndroid Build Coastguard Worker 21*970e1046SAndroid Build Coastguard Worker import com.google.ux.material.libmonet.utils.ColorUtils; 22*970e1046SAndroid Build Coastguard Worker import com.google.ux.material.libmonet.utils.MathUtils; 23*970e1046SAndroid Build Coastguard Worker 24*970e1046SAndroid Build Coastguard Worker /** A class that solves the HCT equation. */ 25*970e1046SAndroid Build Coastguard Worker public class HctSolver { HctSolver()26*970e1046SAndroid Build Coastguard Worker private HctSolver() {} 27*970e1046SAndroid Build Coastguard Worker 28*970e1046SAndroid Build Coastguard Worker static final double[][] SCALED_DISCOUNT_FROM_LINRGB = 29*970e1046SAndroid Build Coastguard Worker new double[][] { 30*970e1046SAndroid Build Coastguard Worker new double[] { 31*970e1046SAndroid Build Coastguard Worker 0.001200833568784504, 0.002389694492170889, 0.0002795742885861124, 32*970e1046SAndroid Build Coastguard Worker }, 33*970e1046SAndroid Build Coastguard Worker new double[] { 34*970e1046SAndroid Build Coastguard Worker 0.0005891086651375999, 0.0029785502573438758, 0.0003270666104008398, 35*970e1046SAndroid Build Coastguard Worker }, 36*970e1046SAndroid Build Coastguard Worker new double[] { 37*970e1046SAndroid Build Coastguard Worker 0.00010146692491640572, 0.0005364214359186694, 0.0032979401770712076, 38*970e1046SAndroid Build Coastguard Worker }, 39*970e1046SAndroid Build Coastguard Worker }; 40*970e1046SAndroid Build Coastguard Worker 41*970e1046SAndroid Build Coastguard Worker static final double[][] LINRGB_FROM_SCALED_DISCOUNT = 42*970e1046SAndroid Build Coastguard Worker new double[][] { 43*970e1046SAndroid Build Coastguard Worker new double[] { 44*970e1046SAndroid Build Coastguard Worker 1373.2198709594231, -1100.4251190754821, -7.278681089101213, 45*970e1046SAndroid Build Coastguard Worker }, 46*970e1046SAndroid Build Coastguard Worker new double[] { 47*970e1046SAndroid Build Coastguard Worker -271.815969077903, 559.6580465940733, -32.46047482791194, 48*970e1046SAndroid Build Coastguard Worker }, 49*970e1046SAndroid Build Coastguard Worker new double[] { 50*970e1046SAndroid Build Coastguard Worker 1.9622899599665666, -57.173814538844006, 308.7233197812385, 51*970e1046SAndroid Build Coastguard Worker }, 52*970e1046SAndroid Build Coastguard Worker }; 53*970e1046SAndroid Build Coastguard Worker 54*970e1046SAndroid Build Coastguard Worker static final double[] Y_FROM_LINRGB = new double[] {0.2126, 0.7152, 0.0722}; 55*970e1046SAndroid Build Coastguard Worker 56*970e1046SAndroid Build Coastguard Worker static final double[] CRITICAL_PLANES = 57*970e1046SAndroid Build Coastguard Worker new double[] { 58*970e1046SAndroid Build Coastguard Worker 0.015176349177441876, 59*970e1046SAndroid Build Coastguard Worker 0.045529047532325624, 60*970e1046SAndroid Build Coastguard Worker 0.07588174588720938, 61*970e1046SAndroid Build Coastguard Worker 0.10623444424209313, 62*970e1046SAndroid Build Coastguard Worker 0.13658714259697685, 63*970e1046SAndroid Build Coastguard Worker 0.16693984095186062, 64*970e1046SAndroid Build Coastguard Worker 0.19729253930674434, 65*970e1046SAndroid Build Coastguard Worker 0.2276452376616281, 66*970e1046SAndroid Build Coastguard Worker 0.2579979360165119, 67*970e1046SAndroid Build Coastguard Worker 0.28835063437139563, 68*970e1046SAndroid Build Coastguard Worker 0.3188300904430532, 69*970e1046SAndroid Build Coastguard Worker 0.350925934958123, 70*970e1046SAndroid Build Coastguard Worker 0.3848314933096426, 71*970e1046SAndroid Build Coastguard Worker 0.42057480301049466, 72*970e1046SAndroid Build Coastguard Worker 0.458183274052838, 73*970e1046SAndroid Build Coastguard Worker 0.4976837250274023, 74*970e1046SAndroid Build Coastguard Worker 0.5391024159806381, 75*970e1046SAndroid Build Coastguard Worker 0.5824650784040898, 76*970e1046SAndroid Build Coastguard Worker 0.6277969426914107, 77*970e1046SAndroid Build Coastguard Worker 0.6751227633498623, 78*970e1046SAndroid Build Coastguard Worker 0.7244668422128921, 79*970e1046SAndroid Build Coastguard Worker 0.775853049866786, 80*970e1046SAndroid Build Coastguard Worker 0.829304845476233, 81*970e1046SAndroid Build Coastguard Worker 0.8848452951698498, 82*970e1046SAndroid Build Coastguard Worker 0.942497089126609, 83*970e1046SAndroid Build Coastguard Worker 1.0022825574869039, 84*970e1046SAndroid Build Coastguard Worker 1.0642236851973577, 85*970e1046SAndroid Build Coastguard Worker 1.1283421258858297, 86*970e1046SAndroid Build Coastguard Worker 1.1946592148522128, 87*970e1046SAndroid Build Coastguard Worker 1.2631959812511864, 88*970e1046SAndroid Build Coastguard Worker 1.3339731595349034, 89*970e1046SAndroid Build Coastguard Worker 1.407011200216447, 90*970e1046SAndroid Build Coastguard Worker 1.4823302800086415, 91*970e1046SAndroid Build Coastguard Worker 1.5599503113873272, 92*970e1046SAndroid Build Coastguard Worker 1.6398909516233677, 93*970e1046SAndroid Build Coastguard Worker 1.7221716113234105, 94*970e1046SAndroid Build Coastguard Worker 1.8068114625156377, 95*970e1046SAndroid Build Coastguard Worker 1.8938294463134073, 96*970e1046SAndroid Build Coastguard Worker 1.9832442801866852, 97*970e1046SAndroid Build Coastguard Worker 2.075074464868551, 98*970e1046SAndroid Build Coastguard Worker 2.1693382909216234, 99*970e1046SAndroid Build Coastguard Worker 2.2660538449872063, 100*970e1046SAndroid Build Coastguard Worker 2.36523901573795, 101*970e1046SAndroid Build Coastguard Worker 2.4669114995532007, 102*970e1046SAndroid Build Coastguard Worker 2.5710888059345764, 103*970e1046SAndroid Build Coastguard Worker 2.6777882626779785, 104*970e1046SAndroid Build Coastguard Worker 2.7870270208169257, 105*970e1046SAndroid Build Coastguard Worker 2.898822059350997, 106*970e1046SAndroid Build Coastguard Worker 3.0131901897720907, 107*970e1046SAndroid Build Coastguard Worker 3.1301480604002863, 108*970e1046SAndroid Build Coastguard Worker 3.2497121605402226, 109*970e1046SAndroid Build Coastguard Worker 3.3718988244681087, 110*970e1046SAndroid Build Coastguard Worker 3.4967242352587946, 111*970e1046SAndroid Build Coastguard Worker 3.624204428461639, 112*970e1046SAndroid Build Coastguard Worker 3.754355295633311, 113*970e1046SAndroid Build Coastguard Worker 3.887192587735158, 114*970e1046SAndroid Build Coastguard Worker 4.022731918402185, 115*970e1046SAndroid Build Coastguard Worker 4.160988767090289, 116*970e1046SAndroid Build Coastguard Worker 4.301978482107941, 117*970e1046SAndroid Build Coastguard Worker 4.445716283538092, 118*970e1046SAndroid Build Coastguard Worker 4.592217266055746, 119*970e1046SAndroid Build Coastguard Worker 4.741496401646282, 120*970e1046SAndroid Build Coastguard Worker 4.893568542229298, 121*970e1046SAndroid Build Coastguard Worker 5.048448422192488, 122*970e1046SAndroid Build Coastguard Worker 5.20615066083972, 123*970e1046SAndroid Build Coastguard Worker 5.3666897647573375, 124*970e1046SAndroid Build Coastguard Worker 5.5300801301023865, 125*970e1046SAndroid Build Coastguard Worker 5.696336044816294, 126*970e1046SAndroid Build Coastguard Worker 5.865471690767354, 127*970e1046SAndroid Build Coastguard Worker 6.037501145825082, 128*970e1046SAndroid Build Coastguard Worker 6.212438385869475, 129*970e1046SAndroid Build Coastguard Worker 6.390297286737924, 130*970e1046SAndroid Build Coastguard Worker 6.571091626112461, 131*970e1046SAndroid Build Coastguard Worker 6.7548350853498045, 132*970e1046SAndroid Build Coastguard Worker 6.941541251256611, 133*970e1046SAndroid Build Coastguard Worker 7.131223617812143, 134*970e1046SAndroid Build Coastguard Worker 7.323895587840543, 135*970e1046SAndroid Build Coastguard Worker 7.5195704746346665, 136*970e1046SAndroid Build Coastguard Worker 7.7182615035334345, 137*970e1046SAndroid Build Coastguard Worker 7.919981813454504, 138*970e1046SAndroid Build Coastguard Worker 8.124744458384042, 139*970e1046SAndroid Build Coastguard Worker 8.332562408825165, 140*970e1046SAndroid Build Coastguard Worker 8.543448553206703, 141*970e1046SAndroid Build Coastguard Worker 8.757415699253682, 142*970e1046SAndroid Build Coastguard Worker 8.974476575321063, 143*970e1046SAndroid Build Coastguard Worker 9.194643831691977, 144*970e1046SAndroid Build Coastguard Worker 9.417930041841839, 145*970e1046SAndroid Build Coastguard Worker 9.644347703669503, 146*970e1046SAndroid Build Coastguard Worker 9.873909240696694, 147*970e1046SAndroid Build Coastguard Worker 10.106627003236781, 148*970e1046SAndroid Build Coastguard Worker 10.342513269534024, 149*970e1046SAndroid Build Coastguard Worker 10.58158024687427, 150*970e1046SAndroid Build Coastguard Worker 10.8238400726681, 151*970e1046SAndroid Build Coastguard Worker 11.069304815507364, 152*970e1046SAndroid Build Coastguard Worker 11.317986476196008, 153*970e1046SAndroid Build Coastguard Worker 11.569896988756009, 154*970e1046SAndroid Build Coastguard Worker 11.825048221409341, 155*970e1046SAndroid Build Coastguard Worker 12.083451977536606, 156*970e1046SAndroid Build Coastguard Worker 12.345119996613247, 157*970e1046SAndroid Build Coastguard Worker 12.610063955123938, 158*970e1046SAndroid Build Coastguard Worker 12.878295467455942, 159*970e1046SAndroid Build Coastguard Worker 13.149826086772048, 160*970e1046SAndroid Build Coastguard Worker 13.42466730586372, 161*970e1046SAndroid Build Coastguard Worker 13.702830557985108, 162*970e1046SAndroid Build Coastguard Worker 13.984327217668513, 163*970e1046SAndroid Build Coastguard Worker 14.269168601521828, 164*970e1046SAndroid Build Coastguard Worker 14.55736596900856, 165*970e1046SAndroid Build Coastguard Worker 14.848930523210871, 166*970e1046SAndroid Build Coastguard Worker 15.143873411576273, 167*970e1046SAndroid Build Coastguard Worker 15.44220572664832, 168*970e1046SAndroid Build Coastguard Worker 15.743938506781891, 169*970e1046SAndroid Build Coastguard Worker 16.04908273684337, 170*970e1046SAndroid Build Coastguard Worker 16.35764934889634, 171*970e1046SAndroid Build Coastguard Worker 16.66964922287304, 172*970e1046SAndroid Build Coastguard Worker 16.985093187232053, 173*970e1046SAndroid Build Coastguard Worker 17.30399201960269, 174*970e1046SAndroid Build Coastguard Worker 17.62635644741625, 175*970e1046SAndroid Build Coastguard Worker 17.95219714852476, 176*970e1046SAndroid Build Coastguard Worker 18.281524751807332, 177*970e1046SAndroid Build Coastguard Worker 18.614349837764564, 178*970e1046SAndroid Build Coastguard Worker 18.95068293910138, 179*970e1046SAndroid Build Coastguard Worker 19.290534541298456, 180*970e1046SAndroid Build Coastguard Worker 19.633915083172692, 181*970e1046SAndroid Build Coastguard Worker 19.98083495742689, 182*970e1046SAndroid Build Coastguard Worker 20.331304511189067, 183*970e1046SAndroid Build Coastguard Worker 20.685334046541502, 184*970e1046SAndroid Build Coastguard Worker 21.042933821039977, 185*970e1046SAndroid Build Coastguard Worker 21.404114048223256, 186*970e1046SAndroid Build Coastguard Worker 21.76888489811322, 187*970e1046SAndroid Build Coastguard Worker 22.137256497705877, 188*970e1046SAndroid Build Coastguard Worker 22.50923893145328, 189*970e1046SAndroid Build Coastguard Worker 22.884842241736916, 190*970e1046SAndroid Build Coastguard Worker 23.264076429332462, 191*970e1046SAndroid Build Coastguard Worker 23.6469514538663, 192*970e1046SAndroid Build Coastguard Worker 24.033477234264016, 193*970e1046SAndroid Build Coastguard Worker 24.42366364919083, 194*970e1046SAndroid Build Coastguard Worker 24.817520537484558, 195*970e1046SAndroid Build Coastguard Worker 25.21505769858089, 196*970e1046SAndroid Build Coastguard Worker 25.61628489293138, 197*970e1046SAndroid Build Coastguard Worker 26.021211842414342, 198*970e1046SAndroid Build Coastguard Worker 26.429848230738664, 199*970e1046SAndroid Build Coastguard Worker 26.842203703840827, 200*970e1046SAndroid Build Coastguard Worker 27.258287870275353, 201*970e1046SAndroid Build Coastguard Worker 27.678110301598522, 202*970e1046SAndroid Build Coastguard Worker 28.10168053274597, 203*970e1046SAndroid Build Coastguard Worker 28.529008062403893, 204*970e1046SAndroid Build Coastguard Worker 28.96010235337422, 205*970e1046SAndroid Build Coastguard Worker 29.39497283293396, 206*970e1046SAndroid Build Coastguard Worker 29.83362889318845, 207*970e1046SAndroid Build Coastguard Worker 30.276079891419332, 208*970e1046SAndroid Build Coastguard Worker 30.722335150426627, 209*970e1046SAndroid Build Coastguard Worker 31.172403958865512, 210*970e1046SAndroid Build Coastguard Worker 31.62629557157785, 211*970e1046SAndroid Build Coastguard Worker 32.08401920991837, 212*970e1046SAndroid Build Coastguard Worker 32.54558406207592, 213*970e1046SAndroid Build Coastguard Worker 33.010999283389665, 214*970e1046SAndroid Build Coastguard Worker 33.4802739966603, 215*970e1046SAndroid Build Coastguard Worker 33.953417292456834, 216*970e1046SAndroid Build Coastguard Worker 34.430438229418264, 217*970e1046SAndroid Build Coastguard Worker 34.911345834551085, 218*970e1046SAndroid Build Coastguard Worker 35.39614910352207, 219*970e1046SAndroid Build Coastguard Worker 35.88485700094671, 220*970e1046SAndroid Build Coastguard Worker 36.37747846067349, 221*970e1046SAndroid Build Coastguard Worker 36.87402238606382, 222*970e1046SAndroid Build Coastguard Worker 37.37449765026789, 223*970e1046SAndroid Build Coastguard Worker 37.87891309649659, 224*970e1046SAndroid Build Coastguard Worker 38.38727753828926, 225*970e1046SAndroid Build Coastguard Worker 38.89959975977785, 226*970e1046SAndroid Build Coastguard Worker 39.41588851594697, 227*970e1046SAndroid Build Coastguard Worker 39.93615253289054, 228*970e1046SAndroid Build Coastguard Worker 40.460400508064545, 229*970e1046SAndroid Build Coastguard Worker 40.98864111053629, 230*970e1046SAndroid Build Coastguard Worker 41.520882981230194, 231*970e1046SAndroid Build Coastguard Worker 42.05713473317016, 232*970e1046SAndroid Build Coastguard Worker 42.597404951718396, 233*970e1046SAndroid Build Coastguard Worker 43.141702194811224, 234*970e1046SAndroid Build Coastguard Worker 43.6900349931913, 235*970e1046SAndroid Build Coastguard Worker 44.24241185063697, 236*970e1046SAndroid Build Coastguard Worker 44.798841244188324, 237*970e1046SAndroid Build Coastguard Worker 45.35933162437017, 238*970e1046SAndroid Build Coastguard Worker 45.92389141541209, 239*970e1046SAndroid Build Coastguard Worker 46.49252901546552, 240*970e1046SAndroid Build Coastguard Worker 47.065252796817916, 241*970e1046SAndroid Build Coastguard Worker 47.64207110610409, 242*970e1046SAndroid Build Coastguard Worker 48.22299226451468, 243*970e1046SAndroid Build Coastguard Worker 48.808024568002054, 244*970e1046SAndroid Build Coastguard Worker 49.3971762874833, 245*970e1046SAndroid Build Coastguard Worker 49.9904556690408, 246*970e1046SAndroid Build Coastguard Worker 50.587870934119984, 247*970e1046SAndroid Build Coastguard Worker 51.189430279724725, 248*970e1046SAndroid Build Coastguard Worker 51.79514187861014, 249*970e1046SAndroid Build Coastguard Worker 52.40501387947288, 250*970e1046SAndroid Build Coastguard Worker 53.0190544071392, 251*970e1046SAndroid Build Coastguard Worker 53.637271562750364, 252*970e1046SAndroid Build Coastguard Worker 54.259673423945976, 253*970e1046SAndroid Build Coastguard Worker 54.88626804504493, 254*970e1046SAndroid Build Coastguard Worker 55.517063457223934, 255*970e1046SAndroid Build Coastguard Worker 56.15206766869424, 256*970e1046SAndroid Build Coastguard Worker 56.79128866487574, 257*970e1046SAndroid Build Coastguard Worker 57.43473440856916, 258*970e1046SAndroid Build Coastguard Worker 58.08241284012621, 259*970e1046SAndroid Build Coastguard Worker 58.734331877617365, 260*970e1046SAndroid Build Coastguard Worker 59.39049941699807, 261*970e1046SAndroid Build Coastguard Worker 60.05092333227251, 262*970e1046SAndroid Build Coastguard Worker 60.715611475655585, 263*970e1046SAndroid Build Coastguard Worker 61.38457167773311, 264*970e1046SAndroid Build Coastguard Worker 62.057811747619894, 265*970e1046SAndroid Build Coastguard Worker 62.7353394731159, 266*970e1046SAndroid Build Coastguard Worker 63.417162620860914, 267*970e1046SAndroid Build Coastguard Worker 64.10328893648692, 268*970e1046SAndroid Build Coastguard Worker 64.79372614476921, 269*970e1046SAndroid Build Coastguard Worker 65.48848194977529, 270*970e1046SAndroid Build Coastguard Worker 66.18756403501224, 271*970e1046SAndroid Build Coastguard Worker 66.89098006357258, 272*970e1046SAndroid Build Coastguard Worker 67.59873767827808, 273*970e1046SAndroid Build Coastguard Worker 68.31084450182222, 274*970e1046SAndroid Build Coastguard Worker 69.02730813691093, 275*970e1046SAndroid Build Coastguard Worker 69.74813616640164, 276*970e1046SAndroid Build Coastguard Worker 70.47333615344107, 277*970e1046SAndroid Build Coastguard Worker 71.20291564160104, 278*970e1046SAndroid Build Coastguard Worker 71.93688215501312, 279*970e1046SAndroid Build Coastguard Worker 72.67524319850172, 280*970e1046SAndroid Build Coastguard Worker 73.41800625771542, 281*970e1046SAndroid Build Coastguard Worker 74.16517879925733, 282*970e1046SAndroid Build Coastguard Worker 74.9167682708136, 283*970e1046SAndroid Build Coastguard Worker 75.67278210128072, 284*970e1046SAndroid Build Coastguard Worker 76.43322770089146, 285*970e1046SAndroid Build Coastguard Worker 77.1981124613393, 286*970e1046SAndroid Build Coastguard Worker 77.96744375590167, 287*970e1046SAndroid Build Coastguard Worker 78.74122893956174, 288*970e1046SAndroid Build Coastguard Worker 79.51947534912904, 289*970e1046SAndroid Build Coastguard Worker 80.30219030335869, 290*970e1046SAndroid Build Coastguard Worker 81.08938110306934, 291*970e1046SAndroid Build Coastguard Worker 81.88105503125999, 292*970e1046SAndroid Build Coastguard Worker 82.67721935322541, 293*970e1046SAndroid Build Coastguard Worker 83.4778813166706, 294*970e1046SAndroid Build Coastguard Worker 84.28304815182372, 295*970e1046SAndroid Build Coastguard Worker 85.09272707154808, 296*970e1046SAndroid Build Coastguard Worker 85.90692527145302, 297*970e1046SAndroid Build Coastguard Worker 86.72564993000343, 298*970e1046SAndroid Build Coastguard Worker 87.54890820862819, 299*970e1046SAndroid Build Coastguard Worker 88.3767072518277, 300*970e1046SAndroid Build Coastguard Worker 89.2090541872801, 301*970e1046SAndroid Build Coastguard Worker 90.04595612594655, 302*970e1046SAndroid Build Coastguard Worker 90.88742016217518, 303*970e1046SAndroid Build Coastguard Worker 91.73345337380438, 304*970e1046SAndroid Build Coastguard Worker 92.58406282226491, 305*970e1046SAndroid Build Coastguard Worker 93.43925555268066, 306*970e1046SAndroid Build Coastguard Worker 94.29903859396902, 307*970e1046SAndroid Build Coastguard Worker 95.16341895893969, 308*970e1046SAndroid Build Coastguard Worker 96.03240364439274, 309*970e1046SAndroid Build Coastguard Worker 96.9059996312159, 310*970e1046SAndroid Build Coastguard Worker 97.78421388448044, 311*970e1046SAndroid Build Coastguard Worker 98.6670533535366, 312*970e1046SAndroid Build Coastguard Worker 99.55452497210776, 313*970e1046SAndroid Build Coastguard Worker }; 314*970e1046SAndroid Build Coastguard Worker 315*970e1046SAndroid Build Coastguard Worker /** 316*970e1046SAndroid Build Coastguard Worker * Sanitizes a small enough angle in radians. 317*970e1046SAndroid Build Coastguard Worker * 318*970e1046SAndroid Build Coastguard Worker * @param angle An angle in radians; must not deviate too much from 0. 319*970e1046SAndroid Build Coastguard Worker * @return A coterminal angle between 0 and 2pi. 320*970e1046SAndroid Build Coastguard Worker */ sanitizeRadians(double angle)321*970e1046SAndroid Build Coastguard Worker static double sanitizeRadians(double angle) { 322*970e1046SAndroid Build Coastguard Worker return (angle + Math.PI * 8) % (Math.PI * 2); 323*970e1046SAndroid Build Coastguard Worker } 324*970e1046SAndroid Build Coastguard Worker 325*970e1046SAndroid Build Coastguard Worker /** 326*970e1046SAndroid Build Coastguard Worker * Delinearizes an RGB component, returning a floating-point number. 327*970e1046SAndroid Build Coastguard Worker * 328*970e1046SAndroid Build Coastguard Worker * @param rgbComponent 0.0 <= rgb_component <= 100.0, represents linear R/G/B channel 329*970e1046SAndroid Build Coastguard Worker * @return 0.0 <= output <= 255.0, color channel converted to regular RGB space 330*970e1046SAndroid Build Coastguard Worker */ trueDelinearized(double rgbComponent)331*970e1046SAndroid Build Coastguard Worker static double trueDelinearized(double rgbComponent) { 332*970e1046SAndroid Build Coastguard Worker double normalized = rgbComponent / 100.0; 333*970e1046SAndroid Build Coastguard Worker double delinearized = 0.0; 334*970e1046SAndroid Build Coastguard Worker if (normalized <= 0.0031308) { 335*970e1046SAndroid Build Coastguard Worker delinearized = normalized * 12.92; 336*970e1046SAndroid Build Coastguard Worker } else { 337*970e1046SAndroid Build Coastguard Worker delinearized = 1.055 * Math.pow(normalized, 1.0 / 2.4) - 0.055; 338*970e1046SAndroid Build Coastguard Worker } 339*970e1046SAndroid Build Coastguard Worker return delinearized * 255.0; 340*970e1046SAndroid Build Coastguard Worker } 341*970e1046SAndroid Build Coastguard Worker chromaticAdaptation(double component)342*970e1046SAndroid Build Coastguard Worker static double chromaticAdaptation(double component) { 343*970e1046SAndroid Build Coastguard Worker double af = Math.pow(Math.abs(component), 0.42); 344*970e1046SAndroid Build Coastguard Worker return MathUtils.signum(component) * 400.0 * af / (af + 27.13); 345*970e1046SAndroid Build Coastguard Worker } 346*970e1046SAndroid Build Coastguard Worker 347*970e1046SAndroid Build Coastguard Worker /** 348*970e1046SAndroid Build Coastguard Worker * Returns the hue of a linear RGB color in CAM16. 349*970e1046SAndroid Build Coastguard Worker * 350*970e1046SAndroid Build Coastguard Worker * @param linrgb The linear RGB coordinates of a color. 351*970e1046SAndroid Build Coastguard Worker * @return The hue of the color in CAM16, in radians. 352*970e1046SAndroid Build Coastguard Worker */ hueOf(double[] linrgb)353*970e1046SAndroid Build Coastguard Worker static double hueOf(double[] linrgb) { 354*970e1046SAndroid Build Coastguard Worker double[] scaledDiscount = MathUtils.matrixMultiply(linrgb, SCALED_DISCOUNT_FROM_LINRGB); 355*970e1046SAndroid Build Coastguard Worker double rA = chromaticAdaptation(scaledDiscount[0]); 356*970e1046SAndroid Build Coastguard Worker double gA = chromaticAdaptation(scaledDiscount[1]); 357*970e1046SAndroid Build Coastguard Worker double bA = chromaticAdaptation(scaledDiscount[2]); 358*970e1046SAndroid Build Coastguard Worker // redness-greenness 359*970e1046SAndroid Build Coastguard Worker double a = (11.0 * rA + -12.0 * gA + bA) / 11.0; 360*970e1046SAndroid Build Coastguard Worker // yellowness-blueness 361*970e1046SAndroid Build Coastguard Worker double b = (rA + gA - 2.0 * bA) / 9.0; 362*970e1046SAndroid Build Coastguard Worker return Math.atan2(b, a); 363*970e1046SAndroid Build Coastguard Worker } 364*970e1046SAndroid Build Coastguard Worker areInCyclicOrder(double a, double b, double c)365*970e1046SAndroid Build Coastguard Worker static boolean areInCyclicOrder(double a, double b, double c) { 366*970e1046SAndroid Build Coastguard Worker double deltaAB = sanitizeRadians(b - a); 367*970e1046SAndroid Build Coastguard Worker double deltaAC = sanitizeRadians(c - a); 368*970e1046SAndroid Build Coastguard Worker return deltaAB < deltaAC; 369*970e1046SAndroid Build Coastguard Worker } 370*970e1046SAndroid Build Coastguard Worker 371*970e1046SAndroid Build Coastguard Worker /** 372*970e1046SAndroid Build Coastguard Worker * Solves the lerp equation. 373*970e1046SAndroid Build Coastguard Worker * 374*970e1046SAndroid Build Coastguard Worker * @param source The starting number. 375*970e1046SAndroid Build Coastguard Worker * @param mid The number in the middle. 376*970e1046SAndroid Build Coastguard Worker * @param target The ending number. 377*970e1046SAndroid Build Coastguard Worker * @return A number t such that lerp(source, target, t) = mid. 378*970e1046SAndroid Build Coastguard Worker */ intercept(double source, double mid, double target)379*970e1046SAndroid Build Coastguard Worker static double intercept(double source, double mid, double target) { 380*970e1046SAndroid Build Coastguard Worker return (mid - source) / (target - source); 381*970e1046SAndroid Build Coastguard Worker } 382*970e1046SAndroid Build Coastguard Worker lerpPoint(double[] source, double t, double[] target)383*970e1046SAndroid Build Coastguard Worker static double[] lerpPoint(double[] source, double t, double[] target) { 384*970e1046SAndroid Build Coastguard Worker return new double[] { 385*970e1046SAndroid Build Coastguard Worker source[0] + (target[0] - source[0]) * t, 386*970e1046SAndroid Build Coastguard Worker source[1] + (target[1] - source[1]) * t, 387*970e1046SAndroid Build Coastguard Worker source[2] + (target[2] - source[2]) * t, 388*970e1046SAndroid Build Coastguard Worker }; 389*970e1046SAndroid Build Coastguard Worker } 390*970e1046SAndroid Build Coastguard Worker 391*970e1046SAndroid Build Coastguard Worker /** 392*970e1046SAndroid Build Coastguard Worker * Intersects a segment with a plane. 393*970e1046SAndroid Build Coastguard Worker * 394*970e1046SAndroid Build Coastguard Worker * @param source The coordinates of point A. 395*970e1046SAndroid Build Coastguard Worker * @param coordinate The R-, G-, or B-coordinate of the plane. 396*970e1046SAndroid Build Coastguard Worker * @param target The coordinates of point B. 397*970e1046SAndroid Build Coastguard Worker * @param axis The axis the plane is perpendicular with. (0: R, 1: G, 2: B) 398*970e1046SAndroid Build Coastguard Worker * @return The intersection point of the segment AB with the plane R=coordinate, G=coordinate, or 399*970e1046SAndroid Build Coastguard Worker * B=coordinate 400*970e1046SAndroid Build Coastguard Worker */ setCoordinate(double[] source, double coordinate, double[] target, int axis)401*970e1046SAndroid Build Coastguard Worker static double[] setCoordinate(double[] source, double coordinate, double[] target, int axis) { 402*970e1046SAndroid Build Coastguard Worker double t = intercept(source[axis], coordinate, target[axis]); 403*970e1046SAndroid Build Coastguard Worker return lerpPoint(source, t, target); 404*970e1046SAndroid Build Coastguard Worker } 405*970e1046SAndroid Build Coastguard Worker isBounded(double x)406*970e1046SAndroid Build Coastguard Worker static boolean isBounded(double x) { 407*970e1046SAndroid Build Coastguard Worker return 0.0 <= x && x <= 100.0; 408*970e1046SAndroid Build Coastguard Worker } 409*970e1046SAndroid Build Coastguard Worker 410*970e1046SAndroid Build Coastguard Worker /** 411*970e1046SAndroid Build Coastguard Worker * Returns the nth possible vertex of the polygonal intersection. 412*970e1046SAndroid Build Coastguard Worker * 413*970e1046SAndroid Build Coastguard Worker * @param y The Y value of the plane. 414*970e1046SAndroid Build Coastguard Worker * @param n The zero-based index of the point. 0 <= n <= 11. 415*970e1046SAndroid Build Coastguard Worker * @return The nth possible vertex of the polygonal intersection of the y plane and the RGB cube, 416*970e1046SAndroid Build Coastguard Worker * in linear RGB coordinates, if it exists. If this possible vertex lies outside of the cube, 417*970e1046SAndroid Build Coastguard Worker * [-1.0, -1.0, -1.0] is returned. 418*970e1046SAndroid Build Coastguard Worker */ nthVertex(double y, int n)419*970e1046SAndroid Build Coastguard Worker static double[] nthVertex(double y, int n) { 420*970e1046SAndroid Build Coastguard Worker double kR = Y_FROM_LINRGB[0]; 421*970e1046SAndroid Build Coastguard Worker double kG = Y_FROM_LINRGB[1]; 422*970e1046SAndroid Build Coastguard Worker double kB = Y_FROM_LINRGB[2]; 423*970e1046SAndroid Build Coastguard Worker double coordA = n % 4 <= 1 ? 0.0 : 100.0; 424*970e1046SAndroid Build Coastguard Worker double coordB = n % 2 == 0 ? 0.0 : 100.0; 425*970e1046SAndroid Build Coastguard Worker if (n < 4) { 426*970e1046SAndroid Build Coastguard Worker double g = coordA; 427*970e1046SAndroid Build Coastguard Worker double b = coordB; 428*970e1046SAndroid Build Coastguard Worker double r = (y - g * kG - b * kB) / kR; 429*970e1046SAndroid Build Coastguard Worker if (isBounded(r)) { 430*970e1046SAndroid Build Coastguard Worker return new double[] {r, g, b}; 431*970e1046SAndroid Build Coastguard Worker } else { 432*970e1046SAndroid Build Coastguard Worker return new double[] {-1.0, -1.0, -1.0}; 433*970e1046SAndroid Build Coastguard Worker } 434*970e1046SAndroid Build Coastguard Worker } else if (n < 8) { 435*970e1046SAndroid Build Coastguard Worker double b = coordA; 436*970e1046SAndroid Build Coastguard Worker double r = coordB; 437*970e1046SAndroid Build Coastguard Worker double g = (y - r * kR - b * kB) / kG; 438*970e1046SAndroid Build Coastguard Worker if (isBounded(g)) { 439*970e1046SAndroid Build Coastguard Worker return new double[] {r, g, b}; 440*970e1046SAndroid Build Coastguard Worker } else { 441*970e1046SAndroid Build Coastguard Worker return new double[] {-1.0, -1.0, -1.0}; 442*970e1046SAndroid Build Coastguard Worker } 443*970e1046SAndroid Build Coastguard Worker } else { 444*970e1046SAndroid Build Coastguard Worker double r = coordA; 445*970e1046SAndroid Build Coastguard Worker double g = coordB; 446*970e1046SAndroid Build Coastguard Worker double b = (y - r * kR - g * kG) / kB; 447*970e1046SAndroid Build Coastguard Worker if (isBounded(b)) { 448*970e1046SAndroid Build Coastguard Worker return new double[] {r, g, b}; 449*970e1046SAndroid Build Coastguard Worker } else { 450*970e1046SAndroid Build Coastguard Worker return new double[] {-1.0, -1.0, -1.0}; 451*970e1046SAndroid Build Coastguard Worker } 452*970e1046SAndroid Build Coastguard Worker } 453*970e1046SAndroid Build Coastguard Worker } 454*970e1046SAndroid Build Coastguard Worker 455*970e1046SAndroid Build Coastguard Worker /** 456*970e1046SAndroid Build Coastguard Worker * Finds the segment containing the desired color. 457*970e1046SAndroid Build Coastguard Worker * 458*970e1046SAndroid Build Coastguard Worker * @param y The Y value of the color. 459*970e1046SAndroid Build Coastguard Worker * @param targetHue The hue of the color. 460*970e1046SAndroid Build Coastguard Worker * @return A list of two sets of linear RGB coordinates, each corresponding to an endpoint of the 461*970e1046SAndroid Build Coastguard Worker * segment containing the desired color. 462*970e1046SAndroid Build Coastguard Worker */ bisectToSegment(double y, double targetHue)463*970e1046SAndroid Build Coastguard Worker static double[][] bisectToSegment(double y, double targetHue) { 464*970e1046SAndroid Build Coastguard Worker double[] left = new double[] {-1.0, -1.0, -1.0}; 465*970e1046SAndroid Build Coastguard Worker double[] right = left; 466*970e1046SAndroid Build Coastguard Worker double leftHue = 0.0; 467*970e1046SAndroid Build Coastguard Worker double rightHue = 0.0; 468*970e1046SAndroid Build Coastguard Worker boolean initialized = false; 469*970e1046SAndroid Build Coastguard Worker boolean uncut = true; 470*970e1046SAndroid Build Coastguard Worker for (int n = 0; n < 12; n++) { 471*970e1046SAndroid Build Coastguard Worker double[] mid = nthVertex(y, n); 472*970e1046SAndroid Build Coastguard Worker if (mid[0] < 0) { 473*970e1046SAndroid Build Coastguard Worker continue; 474*970e1046SAndroid Build Coastguard Worker } 475*970e1046SAndroid Build Coastguard Worker double midHue = hueOf(mid); 476*970e1046SAndroid Build Coastguard Worker if (!initialized) { 477*970e1046SAndroid Build Coastguard Worker left = mid; 478*970e1046SAndroid Build Coastguard Worker right = mid; 479*970e1046SAndroid Build Coastguard Worker leftHue = midHue; 480*970e1046SAndroid Build Coastguard Worker rightHue = midHue; 481*970e1046SAndroid Build Coastguard Worker initialized = true; 482*970e1046SAndroid Build Coastguard Worker continue; 483*970e1046SAndroid Build Coastguard Worker } 484*970e1046SAndroid Build Coastguard Worker if (uncut || areInCyclicOrder(leftHue, midHue, rightHue)) { 485*970e1046SAndroid Build Coastguard Worker uncut = false; 486*970e1046SAndroid Build Coastguard Worker if (areInCyclicOrder(leftHue, targetHue, midHue)) { 487*970e1046SAndroid Build Coastguard Worker right = mid; 488*970e1046SAndroid Build Coastguard Worker rightHue = midHue; 489*970e1046SAndroid Build Coastguard Worker } else { 490*970e1046SAndroid Build Coastguard Worker left = mid; 491*970e1046SAndroid Build Coastguard Worker leftHue = midHue; 492*970e1046SAndroid Build Coastguard Worker } 493*970e1046SAndroid Build Coastguard Worker } 494*970e1046SAndroid Build Coastguard Worker } 495*970e1046SAndroid Build Coastguard Worker return new double[][] {left, right}; 496*970e1046SAndroid Build Coastguard Worker } 497*970e1046SAndroid Build Coastguard Worker midpoint(double[] a, double[] b)498*970e1046SAndroid Build Coastguard Worker static double[] midpoint(double[] a, double[] b) { 499*970e1046SAndroid Build Coastguard Worker return new double[] { 500*970e1046SAndroid Build Coastguard Worker (a[0] + b[0]) / 2, (a[1] + b[1]) / 2, (a[2] + b[2]) / 2, 501*970e1046SAndroid Build Coastguard Worker }; 502*970e1046SAndroid Build Coastguard Worker } 503*970e1046SAndroid Build Coastguard Worker criticalPlaneBelow(double x)504*970e1046SAndroid Build Coastguard Worker static int criticalPlaneBelow(double x) { 505*970e1046SAndroid Build Coastguard Worker return (int) Math.floor(x - 0.5); 506*970e1046SAndroid Build Coastguard Worker } 507*970e1046SAndroid Build Coastguard Worker criticalPlaneAbove(double x)508*970e1046SAndroid Build Coastguard Worker static int criticalPlaneAbove(double x) { 509*970e1046SAndroid Build Coastguard Worker return (int) Math.ceil(x - 0.5); 510*970e1046SAndroid Build Coastguard Worker } 511*970e1046SAndroid Build Coastguard Worker 512*970e1046SAndroid Build Coastguard Worker /** 513*970e1046SAndroid Build Coastguard Worker * Finds a color with the given Y and hue on the boundary of the cube. 514*970e1046SAndroid Build Coastguard Worker * 515*970e1046SAndroid Build Coastguard Worker * @param y The Y value of the color. 516*970e1046SAndroid Build Coastguard Worker * @param targetHue The hue of the color. 517*970e1046SAndroid Build Coastguard Worker * @return The desired color, in linear RGB coordinates. 518*970e1046SAndroid Build Coastguard Worker */ bisectToLimit(double y, double targetHue)519*970e1046SAndroid Build Coastguard Worker static double[] bisectToLimit(double y, double targetHue) { 520*970e1046SAndroid Build Coastguard Worker double[][] segment = bisectToSegment(y, targetHue); 521*970e1046SAndroid Build Coastguard Worker double[] left = segment[0]; 522*970e1046SAndroid Build Coastguard Worker double leftHue = hueOf(left); 523*970e1046SAndroid Build Coastguard Worker double[] right = segment[1]; 524*970e1046SAndroid Build Coastguard Worker for (int axis = 0; axis < 3; axis++) { 525*970e1046SAndroid Build Coastguard Worker if (left[axis] != right[axis]) { 526*970e1046SAndroid Build Coastguard Worker int lPlane = -1; 527*970e1046SAndroid Build Coastguard Worker int rPlane = 255; 528*970e1046SAndroid Build Coastguard Worker if (left[axis] < right[axis]) { 529*970e1046SAndroid Build Coastguard Worker lPlane = criticalPlaneBelow(trueDelinearized(left[axis])); 530*970e1046SAndroid Build Coastguard Worker rPlane = criticalPlaneAbove(trueDelinearized(right[axis])); 531*970e1046SAndroid Build Coastguard Worker } else { 532*970e1046SAndroid Build Coastguard Worker lPlane = criticalPlaneAbove(trueDelinearized(left[axis])); 533*970e1046SAndroid Build Coastguard Worker rPlane = criticalPlaneBelow(trueDelinearized(right[axis])); 534*970e1046SAndroid Build Coastguard Worker } 535*970e1046SAndroid Build Coastguard Worker for (int i = 0; i < 8; i++) { 536*970e1046SAndroid Build Coastguard Worker if (Math.abs(rPlane - lPlane) <= 1) { 537*970e1046SAndroid Build Coastguard Worker break; 538*970e1046SAndroid Build Coastguard Worker } else { 539*970e1046SAndroid Build Coastguard Worker int mPlane = (int) Math.floor((lPlane + rPlane) / 2.0); 540*970e1046SAndroid Build Coastguard Worker double midPlaneCoordinate = CRITICAL_PLANES[mPlane]; 541*970e1046SAndroid Build Coastguard Worker double[] mid = setCoordinate(left, midPlaneCoordinate, right, axis); 542*970e1046SAndroid Build Coastguard Worker double midHue = hueOf(mid); 543*970e1046SAndroid Build Coastguard Worker if (areInCyclicOrder(leftHue, targetHue, midHue)) { 544*970e1046SAndroid Build Coastguard Worker right = mid; 545*970e1046SAndroid Build Coastguard Worker rPlane = mPlane; 546*970e1046SAndroid Build Coastguard Worker } else { 547*970e1046SAndroid Build Coastguard Worker left = mid; 548*970e1046SAndroid Build Coastguard Worker leftHue = midHue; 549*970e1046SAndroid Build Coastguard Worker lPlane = mPlane; 550*970e1046SAndroid Build Coastguard Worker } 551*970e1046SAndroid Build Coastguard Worker } 552*970e1046SAndroid Build Coastguard Worker } 553*970e1046SAndroid Build Coastguard Worker } 554*970e1046SAndroid Build Coastguard Worker } 555*970e1046SAndroid Build Coastguard Worker return midpoint(left, right); 556*970e1046SAndroid Build Coastguard Worker } 557*970e1046SAndroid Build Coastguard Worker inverseChromaticAdaptation(double adapted)558*970e1046SAndroid Build Coastguard Worker static double inverseChromaticAdaptation(double adapted) { 559*970e1046SAndroid Build Coastguard Worker double adaptedAbs = Math.abs(adapted); 560*970e1046SAndroid Build Coastguard Worker double base = Math.max(0, 27.13 * adaptedAbs / (400.0 - adaptedAbs)); 561*970e1046SAndroid Build Coastguard Worker return MathUtils.signum(adapted) * Math.pow(base, 1.0 / 0.42); 562*970e1046SAndroid Build Coastguard Worker } 563*970e1046SAndroid Build Coastguard Worker 564*970e1046SAndroid Build Coastguard Worker /** 565*970e1046SAndroid Build Coastguard Worker * Finds a color with the given hue, chroma, and Y. 566*970e1046SAndroid Build Coastguard Worker * 567*970e1046SAndroid Build Coastguard Worker * @param hueRadians The desired hue in radians. 568*970e1046SAndroid Build Coastguard Worker * @param chroma The desired chroma. 569*970e1046SAndroid Build Coastguard Worker * @param y The desired Y. 570*970e1046SAndroid Build Coastguard Worker * @return The desired color as a hexadecimal integer, if found; 0 otherwise. 571*970e1046SAndroid Build Coastguard Worker */ findResultByJ(double hueRadians, double chroma, double y)572*970e1046SAndroid Build Coastguard Worker static int findResultByJ(double hueRadians, double chroma, double y) { 573*970e1046SAndroid Build Coastguard Worker // Initial estimate of j. 574*970e1046SAndroid Build Coastguard Worker double j = Math.sqrt(y) * 11.0; 575*970e1046SAndroid Build Coastguard Worker // =========================================================== 576*970e1046SAndroid Build Coastguard Worker // Operations inlined from Cam16 to avoid repeated calculation 577*970e1046SAndroid Build Coastguard Worker // =========================================================== 578*970e1046SAndroid Build Coastguard Worker ViewingConditions viewingConditions = ViewingConditions.DEFAULT; 579*970e1046SAndroid Build Coastguard Worker double tInnerCoeff = 1 / Math.pow(1.64 - Math.pow(0.29, viewingConditions.getN()), 0.73); 580*970e1046SAndroid Build Coastguard Worker double eHue = 0.25 * (Math.cos(hueRadians + 2.0) + 3.8); 581*970e1046SAndroid Build Coastguard Worker double p1 = eHue * (50000.0 / 13.0) * viewingConditions.getNc() * viewingConditions.getNcb(); 582*970e1046SAndroid Build Coastguard Worker double hSin = Math.sin(hueRadians); 583*970e1046SAndroid Build Coastguard Worker double hCos = Math.cos(hueRadians); 584*970e1046SAndroid Build Coastguard Worker for (int iterationRound = 0; iterationRound < 5; iterationRound++) { 585*970e1046SAndroid Build Coastguard Worker // =========================================================== 586*970e1046SAndroid Build Coastguard Worker // Operations inlined from Cam16 to avoid repeated calculation 587*970e1046SAndroid Build Coastguard Worker // =========================================================== 588*970e1046SAndroid Build Coastguard Worker double jNormalized = j / 100.0; 589*970e1046SAndroid Build Coastguard Worker double alpha = chroma == 0.0 || j == 0.0 ? 0.0 : chroma / Math.sqrt(jNormalized); 590*970e1046SAndroid Build Coastguard Worker double t = Math.pow(alpha * tInnerCoeff, 1.0 / 0.9); 591*970e1046SAndroid Build Coastguard Worker double ac = 592*970e1046SAndroid Build Coastguard Worker viewingConditions.getAw() 593*970e1046SAndroid Build Coastguard Worker * Math.pow(jNormalized, 1.0 / viewingConditions.getC() / viewingConditions.getZ()); 594*970e1046SAndroid Build Coastguard Worker double p2 = ac / viewingConditions.getNbb(); 595*970e1046SAndroid Build Coastguard Worker double gamma = 23.0 * (p2 + 0.305) * t / (23.0 * p1 + 11 * t * hCos + 108.0 * t * hSin); 596*970e1046SAndroid Build Coastguard Worker double a = gamma * hCos; 597*970e1046SAndroid Build Coastguard Worker double b = gamma * hSin; 598*970e1046SAndroid Build Coastguard Worker double rA = (460.0 * p2 + 451.0 * a + 288.0 * b) / 1403.0; 599*970e1046SAndroid Build Coastguard Worker double gA = (460.0 * p2 - 891.0 * a - 261.0 * b) / 1403.0; 600*970e1046SAndroid Build Coastguard Worker double bA = (460.0 * p2 - 220.0 * a - 6300.0 * b) / 1403.0; 601*970e1046SAndroid Build Coastguard Worker double rCScaled = inverseChromaticAdaptation(rA); 602*970e1046SAndroid Build Coastguard Worker double gCScaled = inverseChromaticAdaptation(gA); 603*970e1046SAndroid Build Coastguard Worker double bCScaled = inverseChromaticAdaptation(bA); 604*970e1046SAndroid Build Coastguard Worker double[] linrgb = 605*970e1046SAndroid Build Coastguard Worker MathUtils.matrixMultiply( 606*970e1046SAndroid Build Coastguard Worker new double[] {rCScaled, gCScaled, bCScaled}, LINRGB_FROM_SCALED_DISCOUNT); 607*970e1046SAndroid Build Coastguard Worker // =========================================================== 608*970e1046SAndroid Build Coastguard Worker // Operations inlined from Cam16 to avoid repeated calculation 609*970e1046SAndroid Build Coastguard Worker // =========================================================== 610*970e1046SAndroid Build Coastguard Worker if (linrgb[0] < 0 || linrgb[1] < 0 || linrgb[2] < 0) { 611*970e1046SAndroid Build Coastguard Worker return 0; 612*970e1046SAndroid Build Coastguard Worker } 613*970e1046SAndroid Build Coastguard Worker double kR = Y_FROM_LINRGB[0]; 614*970e1046SAndroid Build Coastguard Worker double kG = Y_FROM_LINRGB[1]; 615*970e1046SAndroid Build Coastguard Worker double kB = Y_FROM_LINRGB[2]; 616*970e1046SAndroid Build Coastguard Worker double fnj = kR * linrgb[0] + kG * linrgb[1] + kB * linrgb[2]; 617*970e1046SAndroid Build Coastguard Worker if (fnj <= 0) { 618*970e1046SAndroid Build Coastguard Worker return 0; 619*970e1046SAndroid Build Coastguard Worker } 620*970e1046SAndroid Build Coastguard Worker if (iterationRound == 4 || Math.abs(fnj - y) < 0.002) { 621*970e1046SAndroid Build Coastguard Worker if (linrgb[0] > 100.01 || linrgb[1] > 100.01 || linrgb[2] > 100.01) { 622*970e1046SAndroid Build Coastguard Worker return 0; 623*970e1046SAndroid Build Coastguard Worker } 624*970e1046SAndroid Build Coastguard Worker return ColorUtils.argbFromLinrgb(linrgb); 625*970e1046SAndroid Build Coastguard Worker } 626*970e1046SAndroid Build Coastguard Worker // Iterates with Newton method, 627*970e1046SAndroid Build Coastguard Worker // Using 2 * fn(j) / j as the approximation of fn'(j) 628*970e1046SAndroid Build Coastguard Worker j = j - (fnj - y) * j / (2 * fnj); 629*970e1046SAndroid Build Coastguard Worker } 630*970e1046SAndroid Build Coastguard Worker return 0; 631*970e1046SAndroid Build Coastguard Worker } 632*970e1046SAndroid Build Coastguard Worker 633*970e1046SAndroid Build Coastguard Worker /** 634*970e1046SAndroid Build Coastguard Worker * Finds an sRGB color with the given hue, chroma, and L*, if possible. 635*970e1046SAndroid Build Coastguard Worker * 636*970e1046SAndroid Build Coastguard Worker * @param hueDegrees The desired hue, in degrees. 637*970e1046SAndroid Build Coastguard Worker * @param chroma The desired chroma. 638*970e1046SAndroid Build Coastguard Worker * @param lstar The desired L*. 639*970e1046SAndroid Build Coastguard Worker * @return A hexadecimal representing the sRGB color. The color has sufficiently close hue, 640*970e1046SAndroid Build Coastguard Worker * chroma, and L* to the desired values, if possible; otherwise, the hue and L* will be 641*970e1046SAndroid Build Coastguard Worker * sufficiently close, and chroma will be maximized. 642*970e1046SAndroid Build Coastguard Worker */ solveToInt(double hueDegrees, double chroma, double lstar)643*970e1046SAndroid Build Coastguard Worker public static int solveToInt(double hueDegrees, double chroma, double lstar) { 644*970e1046SAndroid Build Coastguard Worker if (chroma < 0.0001 || lstar < 0.0001 || lstar > 99.9999) { 645*970e1046SAndroid Build Coastguard Worker return ColorUtils.argbFromLstar(lstar); 646*970e1046SAndroid Build Coastguard Worker } 647*970e1046SAndroid Build Coastguard Worker hueDegrees = MathUtils.sanitizeDegreesDouble(hueDegrees); 648*970e1046SAndroid Build Coastguard Worker double hueRadians = hueDegrees / 180 * Math.PI; 649*970e1046SAndroid Build Coastguard Worker double y = ColorUtils.yFromLstar(lstar); 650*970e1046SAndroid Build Coastguard Worker int exactAnswer = findResultByJ(hueRadians, chroma, y); 651*970e1046SAndroid Build Coastguard Worker if (exactAnswer != 0) { 652*970e1046SAndroid Build Coastguard Worker return exactAnswer; 653*970e1046SAndroid Build Coastguard Worker } 654*970e1046SAndroid Build Coastguard Worker double[] linrgb = bisectToLimit(y, hueRadians); 655*970e1046SAndroid Build Coastguard Worker return ColorUtils.argbFromLinrgb(linrgb); 656*970e1046SAndroid Build Coastguard Worker } 657*970e1046SAndroid Build Coastguard Worker 658*970e1046SAndroid Build Coastguard Worker /** 659*970e1046SAndroid Build Coastguard Worker * Finds an sRGB color with the given hue, chroma, and L*, if possible. 660*970e1046SAndroid Build Coastguard Worker * 661*970e1046SAndroid Build Coastguard Worker * @param hueDegrees The desired hue, in degrees. 662*970e1046SAndroid Build Coastguard Worker * @param chroma The desired chroma. 663*970e1046SAndroid Build Coastguard Worker * @param lstar The desired L*. 664*970e1046SAndroid Build Coastguard Worker * @return A CAM16 object representing the sRGB color. The color has sufficiently close hue, 665*970e1046SAndroid Build Coastguard Worker * chroma, and L* to the desired values, if possible; otherwise, the hue and L* will be 666*970e1046SAndroid Build Coastguard Worker * sufficiently close, and chroma will be maximized. 667*970e1046SAndroid Build Coastguard Worker */ solveToCam(double hueDegrees, double chroma, double lstar)668*970e1046SAndroid Build Coastguard Worker public static Cam16 solveToCam(double hueDegrees, double chroma, double lstar) { 669*970e1046SAndroid Build Coastguard Worker return Cam16.fromInt(solveToInt(hueDegrees, chroma, lstar)); 670*970e1046SAndroid Build Coastguard Worker } 671*970e1046SAndroid Build Coastguard Worker } 672*970e1046SAndroid Build Coastguard Worker 673