@@ -19,7 +19,9 @@ const convert = {
1919 cmyk : { channels : 4 , labels : 'cmyk' } ,
2020 xyz : { channels : 3 , labels : 'xyz' } ,
2121 lab : { channels : 3 , labels : 'lab' } ,
22+ oklab : { channels : 3 , labels : [ 'okl' , 'oka' , 'okb' ] } ,
2223 lch : { channels : 3 , labels : 'lch' } ,
24+ oklch : { channels : 3 , labels : [ 'okl' , 'okc' , 'okh' ] } ,
2325 hex : { channels : 1 , labels : [ 'hex' ] } ,
2426 keyword : { channels : 1 , labels : [ 'keyword' ] } ,
2527 ansi16 : { channels : 1 , labels : [ 'ansi16' ] } ,
@@ -34,6 +36,18 @@ export default convert;
3436// LAB f(t) constant
3537const LAB_FT = ( 6 / 29 ) ** 3 ;
3638
39+ // SRGB non-linear transform functions
40+ function srgbNonlinearTransform ( c ) {
41+ const cc = c > 0.003_130_8
42+ ? ( ( 1.055 * ( c ** ( 1 / 2.4 ) ) ) - 0.055 )
43+ : c * 12.92 ;
44+ return Math . min ( Math . max ( 0 , cc ) , 1 ) ;
45+ }
46+
47+ function srgbNonlinearTransformInv ( c ) {
48+ return c > 0.040_45 ? ( ( ( c + 0.055 ) / 1.055 ) ** 2.4 ) : ( c / 12.92 ) ;
49+ }
50+
3751// Hide .channels and .labels properties
3852for ( const model of Object . keys ( convert ) ) {
3953 if ( ! ( 'channels' in convert [ model ] ) ) {
@@ -183,6 +197,23 @@ convert.rgb.hwb = function (rgb) {
183197 return [ h , w * 100 , b * 100 ] ;
184198} ;
185199
200+ convert . rgb . oklab = function ( rgb ) {
201+ // Assume sRGB
202+ const r = srgbNonlinearTransformInv ( rgb [ 0 ] / 255 ) ;
203+ const g = srgbNonlinearTransformInv ( rgb [ 1 ] / 255 ) ;
204+ const b = srgbNonlinearTransformInv ( rgb [ 2 ] / 255 ) ;
205+
206+ const lp = Math . cbrt ( 0.412_221_470_8 * r + 0.536_332_536_3 * g + 0.051_445_992_9 * b ) ;
207+ const mp = Math . cbrt ( 0.211_903_498_2 * r + 0.680_699_545_1 * g + 0.107_396_956_6 * b ) ;
208+ const sp = Math . cbrt ( 0.088_302_461_9 * r + 0.281_718_837_6 * g + 0.629_978_700_5 * b ) ;
209+
210+ const l = 0.210_454_255_3 * lp + 0.793_617_785 * mp - 0.004_072_046_8 * sp ;
211+ const aa = 1.977_998_495_1 * lp - 2.428_592_205 * mp + 0.450_593_709_9 * sp ;
212+ const bb = 0.025_904_037_1 * lp + 0.782_771_766_2 * mp - 0.808_675_766 * sp ;
213+
214+ return [ l * 100 , aa * 100 , bb * 100 ] ;
215+ } ;
216+
186217convert . rgb . cmyk = function ( rgb ) {
187218 const r = rgb [ 0 ] / 255 ;
188219 const g = rgb [ 1 ] / 255 ;
@@ -237,14 +268,10 @@ convert.keyword.rgb = function (keyword) {
237268} ;
238269
239270convert . rgb . xyz = function ( rgb ) {
240- let r = rgb [ 0 ] / 255 ;
241- let g = rgb [ 1 ] / 255 ;
242- let b = rgb [ 2 ] / 255 ;
243-
244271 // Assume sRGB
245- r = r > 0.040_45 ? ( ( ( r + 0.055 ) / 1.055 ) ** 2.4 ) : ( r / 12.92 ) ;
246- g = g > 0.040_45 ? ( ( ( g + 0.055 ) / 1.055 ) ** 2.4 ) : ( g / 12.92 ) ;
247- b = b > 0.040_45 ? ( ( ( b + 0.055 ) / 1.055 ) ** 2.4 ) : ( b / 12.92 ) ;
272+ const r = srgbNonlinearTransformInv ( rgb [ 0 ] / 255 ) ;
273+ const g = srgbNonlinearTransformInv ( rgb [ 1 ] / 255 ) ;
274+ const b = srgbNonlinearTransformInv ( rgb [ 2 ] / 255 ) ;
248275
249276 const x = ( r * 0.412_456_4 ) + ( g * 0.357_576_1 ) + ( b * 0.180_437_5 ) ;
250277 const y = ( r * 0.212_672_9 ) + ( g * 0.715_152_2 ) + ( b * 0.072_175 ) ;
@@ -471,21 +498,9 @@ convert.xyz.rgb = function (xyz) {
471498 b = ( x * 0.055_643_4 ) + ( y * - 0.204_025_9 ) + ( z * 1.057_225_2 ) ;
472499
473500 // Assume sRGB
474- r = r > 0.003_130_8
475- ? ( ( 1.055 * ( r ** ( 1 / 2.4 ) ) ) - 0.055 )
476- : r * 12.92 ;
477-
478- g = g > 0.003_130_8
479- ? ( ( 1.055 * ( g ** ( 1 / 2.4 ) ) ) - 0.055 )
480- : g * 12.92 ;
481-
482- b = b > 0.003_130_8
483- ? ( ( 1.055 * ( b ** ( 1 / 2.4 ) ) ) - 0.055 )
484- : b * 12.92 ;
485-
486- r = Math . min ( Math . max ( 0 , r ) , 1 ) ;
487- g = Math . min ( Math . max ( 0 , g ) , 1 ) ;
488- b = Math . min ( Math . max ( 0 , b ) , 1 ) ;
501+ r = srgbNonlinearTransform ( r ) ;
502+ g = srgbNonlinearTransform ( g ) ;
503+ b = srgbNonlinearTransform ( b ) ;
489504
490505 return [ r * 255 , g * 255 , b * 255 ] ;
491506} ;
@@ -510,6 +525,63 @@ convert.xyz.lab = function (xyz) {
510525 return [ l , a , b ] ;
511526} ;
512527
528+ convert . xyz . oklab = function ( xyz ) {
529+ const x = xyz [ 0 ] / 100 ;
530+ const y = xyz [ 1 ] / 100 ;
531+ const z = xyz [ 2 ] / 100 ;
532+
533+ const lp = Math . cbrt ( 0.818_933_010_1 * x + 0.361_866_742_4 * y - 0.128_859_713_7 * z ) ;
534+ const mp = Math . cbrt ( 0.032_984_543_6 * x + 0.929_311_871_5 * y + 0.036_145_638_7 * z ) ;
535+ const sp = Math . cbrt ( 0.048_200_301_8 * x + 0.264_366_269_1 * y + 0.633_851_707 * z ) ;
536+
537+ const l = 0.210_454_255_3 * lp + 0.793_617_785 * mp - 0.004_072_046_8 * sp ;
538+ const a = 1.977_998_495_1 * lp - 2.428_592_205 * mp + 0.450_593_709_9 * sp ;
539+ const b = 0.025_904_037_1 * lp + 0.782_771_766_2 * mp - 0.808_675_766 * sp ;
540+
541+ return [ l * 100 , a * 100 , b * 100 ] ;
542+ } ;
543+
544+ convert . oklab . oklch = function ( oklab ) {
545+ return convert . lab . lch ( oklab ) ;
546+ } ;
547+
548+ convert . oklab . xyz = function ( oklab ) {
549+ const ll = oklab [ 0 ] / 100 ;
550+ const a = oklab [ 1 ] / 100 ;
551+ const b = oklab [ 2 ] / 100 ;
552+
553+ const l = ( 0.999_999_998 * ll + 0.396_337_792 * a + 0.215_803_758 * b ) ** 3 ;
554+ const m = ( 1.000_000_008 * ll - 0.105_561_342 * a - 0.063_854_175 * b ) ** 3 ;
555+ const s = ( 1.000_000_055 * ll - 0.089_484_182 * a - 1.291_485_538 * b ) ** 3 ;
556+
557+ const x = 1.227_013_851 * l - 0.557_799_98 * m + 0.281_256_149 * s ;
558+ const y = - 0.040_580_178 * l + 1.112_256_87 * m - 0.071_676_679 * s ;
559+ const z = - 0.076_381_285 * l - 0.421_481_978 * m + 1.586_163_22 * s ;
560+
561+ return [ x * 100 , y * 100 , z * 100 ] ;
562+ } ;
563+
564+ convert . oklab . rgb = function ( oklab ) {
565+ const ll = oklab [ 0 ] / 100 ;
566+ const aa = oklab [ 1 ] / 100 ;
567+ const bb = oklab [ 2 ] / 100 ;
568+
569+ const l = ( ll + 0.396_337_777_4 * aa + 0.215_803_757_3 * bb ) ** 3 ;
570+ const m = ( ll - 0.105_561_345_8 * aa - 0.063_854_172_8 * bb ) ** 3 ;
571+ const s = ( ll - 0.089_484_177_5 * aa - 1.291_485_548 * bb ) ** 3 ;
572+
573+ // Assume sRGB
574+ const r = srgbNonlinearTransform ( 4.076_741_662_1 * l - 3.307_711_591_3 * m + 0.230_969_929_2 * s ) ;
575+ const g = srgbNonlinearTransform ( - 1.268_438_004_6 * l + 2.609_757_401_1 * m - 0.341_319_396_5 * s ) ;
576+ const b = srgbNonlinearTransform ( - 0.004_196_086_3 * l - 0.703_418_614_7 * m + 1.707_614_701 * s ) ;
577+
578+ return [ r * 255 , g * 255 , b * 255 ] ;
579+ } ;
580+
581+ convert . oklch . oklab = function ( oklch ) {
582+ return convert . lch . lab ( oklch ) ;
583+ } ;
584+
513585convert . lab . xyz = function ( lab ) {
514586 const l = lab [ 0 ] ;
515587 const a = lab [ 1 ] ;
0 commit comments