Last active
June 19, 2024 06:38
-
-
Save Sam-Izdat/43e630ea683a07c935863ba32e26d476 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import {g, tm} from './util'; | |
let ready = false; | |
let setup = () => ti.addToKernelScope({ | |
g, tm, | |
ColorTransform, TransferFunction, | |
convert_to_xyz, convert_from_xyz, convert_color_space | |
}); | |
export class ColorTransform { | |
static convert = (cst, v) => convert_color_space(cst, v); // [ColorTransform, vec] -> vec | |
static Variant = class { | |
static UNKNOWN = 1<<0; | |
static NONCOLOR = 1<<1; | |
static CIE_XYZ = 1<<2; | |
static CIE_XYY = 1<<3; | |
static SRGB = 1<<4; | |
static SRGB_LIN = 1<<5; | |
static REC709 = 1<<6; | |
static REC2020 = 1<<7; | |
static REC2020_LIN = 1<<8; | |
static DCI_P3 = 1<<9; | |
static DCI_P3_LIN = 1<<10; | |
static DISPLAY_P3 = 1<<11; | |
static ACESCG = 1<<12; | |
static ACESCC = 1<<13; | |
static ACESCCT = 1<<14; | |
static ACES2065_1 = 1<<15; | |
static LMS = 1<<16; | |
static SUPPORTED = this.CIE_XYZ | | |
this.SRGB | this.SRGB_LIN | this.REC709 | | |
this.REC2020 | this.REC2020_LIN | | |
this.DCI_P3 | this.DCI_P3_LIN | | |
this.ACESCG | this.LMS; | |
}; | |
constructor(cs_from=ColorTransform.Variant.SRGB, cs_to=ColorTransform.Variant.ACESCG) { | |
return (async () => { | |
// Dummy kernel for ti.Static for now. | |
this.init = ti.classKernel(this, () => {}); | |
this.init(); | |
if (!ready) { setup(); ready=true; } | |
g.assert_bitwise_and(cs_from, ColorTransform.Variant.SUPPORTED, 'unsupported input color space'); | |
g.assert_bitwise_and(cs_to, ColorTransform.Variant.SUPPORTED, 'unsupported output color space'); | |
this.cs_from = cs_from; | |
this.cs_to = cs_to; | |
return this; | |
})(); | |
}; | |
static mat_xyz_to_srgb = [ | |
[3.24096994190452134, -1.53738317757009346, -0.498610760293003284], | |
[-0.969243636280879826, 1.87596750150772067, 0.0415550574071756125], | |
[0.0556300796969936084, -0.203976958888976564, 1.05697151424287856]]; | |
static mat_srgb_to_xyz = [ | |
[0.412390799265959481, 0.357584339383877964, 0.180480788401834288], | |
[0.212639005871510358, 0.715168678767755927, 0.072192315360733715], | |
[0.0193308187155918507, 0.119194779794625988, 0.950532152249660581]]; | |
// NOTE: Includes "D60"/D65 white point conversion | |
static mat_srgb_to_acescg = [ | |
[ 0.6130974024, 0.3395231462, 0.04737945141], | |
[ 0.07019372247, 0.916353879, 0.01345239847], | |
[ 0.02061559288, 0.1095697729, 0.8698146341]]; | |
// NOTE: Includes "D60"/D65 white point conversion | |
static mat_acescg_to_srgb = [ | |
[ 1.705050993, -0.6217921206,-0.083258872], | |
[-0.1302564175, 1.140804737, -0.01054831907], | |
[-0.02400335681,-0.1289689761, 1.152972333]]; | |
static mat_aces_rrt_sat = [ | |
[0.9708890, 0.0269633, 0.00214758], | |
[0.0108892, 0.9869630, 0.00214758], | |
[0.0108892, 0.0269633, 0.96214800]]; | |
static mat_aces_odt_sat = [ | |
[0.949056, 0.0471857, 0.00375827], | |
[0.019056, 0.9771860, 0.00375827], | |
[0.019056, 0.0471857, 0.93375800]]; | |
// sRGB => XYZ => D65_2_D60 => AP1 => RRT_SAT | |
static mat_srgb_to_ap1_tm = [ | |
[0.59719, 0.35458, 0.04823], | |
[0.07600, 0.90834, 0.01566], | |
[0.02840, 0.13383, 0.83777]]; | |
// ODT_SAT => XYZ => D60_2_D65 => sRGB | |
static mat_ap1_to_srgb_tm = [ | |
[ 1.60475, -0.53108, -0.07367], | |
[-0.10208, 1.10813, -0.00605], | |
[-0.00327, -0.07276, 1.07602]]; | |
// NOTE: Includes "D60"/D65 white point conversion | |
static mat_srgb_to_aces2065_1 = [ | |
[ 0.439632982, 0.382988698, 0.17737832], | |
[ 0.0897764431, 0.813439429, 0.0967841284], | |
[ 0.0175411704, 0.111546553, 0.870912277]]; | |
// NOTE: Includes "D60"/D65 white point conversion | |
static mat_aces2065_1_to_srgb = [ | |
[ 2.52168619, -1.13413099, -0.387555198], | |
[-0.276479914, 1.37271909, -0.0962391736], | |
[-0.015378065, -0.152975336, 1.1683534]]; | |
static mat_srgb_to_displayp3 = [ | |
[ 0.822461969, 0.177538031, 1.15772692e-10], | |
[ 0.0331941989, 0.966805801, 1.95085037e-11], | |
[ 0.0170826307, 0.0723974405, 0.910519929]]; | |
static mat_displayp3_to_srgb = [ | |
[ 1.22494018, -0.224940176, -4.77534979e-11], | |
[-0.0420569547, 1.04205695, 3.37864801e-11], | |
[-0.0196375546,-0.0786360454, 1.0982736]] ; | |
// NOTE: No chromatic adaptation | |
static mat_srgb_to_dcip3 = [ | |
[0.868579739716132409, 0.128919138460847047, 0.00250112182302054368], | |
[0.0345404102543194426, 0.961811386361919975, 0.0036482033837605824], | |
[0.0167714290414502718, 0.0710399977868858352, 0.912188573171663893]]; | |
// NOTE: No chromatic adaptation | |
static mat_dcip3_to_srgb = [ | |
[ 1.15751640619975871, -0.154962378073857756, -0.00255402812590095854], | |
[-0.0415000715306859699, 1.04556792307969925, -0.00406785154901328463], | |
[-0.0180500389562539583,-0.0785782726530290654, 1.09662831160928302]]; | |
// NOTE: No chromatic adaptation | |
static mat_dcip3_to_xyz = [ | |
[ 0.445169815564552417, 0.277134409206777664, 0.172282669815564564], | |
[ 0.209491677912730539, 0.721595254161043636, 0.0689130679262258258], | |
[-3.63410131696985616e-17, 0.0470605600539811521, 0.907355394361973415]]; | |
// NOTE: No chromatic adaptation | |
static mat_xyz_to_dcip3 = [ | |
[2.7253940304917328, -1.01800300622718496, -0.440163195190036463], | |
[-0.795168025808764195, 1.689732054843624, 0.0226471906084774533], | |
[0.0412418913957000325, -0.0876390192158623825, 1.10092937864632191]]; | |
static mat_srgb_to_rec2020 = [ | |
[ 0.627403896, 0.329283039, 0.0433130657], | |
[ 0.0690972894, 0.919540395, 0.0113623156], | |
[ 0.0163914389, 0.0880133077, 0.895595253]]; | |
static mat_rec2020_to_srgb = [ | |
[ 1.660491, -0.587641139,-0.0728498633], | |
[-0.124550475, 1.1328999, -0.00834942258], | |
[-0.0181507633,-0.100578898, 1.11872966]]; | |
static mat_rec2020_to_xyz = [ | |
[0.636958048301291, 0.144616903586208, 0.168880975164172], | |
[0.262700212011267, 0.677998071518871, 0.059301716469862], | |
[4.99410657446607e-17, 0.0280726930490874, 1.06098505771079]]; | |
static mat_xyz_to_rec2020 = [ | |
[1.71665118797127, -0.355670783776393, -0.25336628137366], | |
[-0.666684351832489, 1.61648123663494, 0.0157685458139111], | |
[0.0176398574453108, -0.0427706132578085, 0.942103121235474]]; | |
// NOTE: No chromatic adaptation | |
static mat_acescg_to_xyz = [ | |
[0.662454181108505, 0.134004206456433, 0.156187687004908], | |
[0.272228716780915, 0.674081765811148, 0.0536895174079371], | |
[-0.00557464949039411, 0.00406073352898283, 1.010339100313]]; | |
// NOTE: No chromatic adaptation | |
static mat_xyz_to_acescg = [ | |
[1.64102337969433, -0.32480329418479, -0.236424695237612], | |
[-0.663662858722983, 1.61533159165734, 0.0167563476855301], | |
[0.0117218943283754, -0.00828444199623741, 0.988394858539022]]; | |
// NOTE: For CIE XYZ color | |
static mat_d60_to_d65 = [ | |
[0.987224008703017, -0.00611322860685692, 0.0159532883359127], | |
[-0.0075983718116624, 1.00186148473965, 0.00533003579138895], | |
[0.00307257705853153, -0.0050959615111306, 1.0816806030658]]; | |
// NOTE: For CIE XYZ color | |
static mat_d65_to_d60 = [ | |
[1.01303491464999, 0.00610525782320722, -0.0149709436265875], | |
[0.00769823012541507, 0.998163352118278, -0.00503203853511188], | |
[-0.00284131743243907, 0.00468515672253722, 0.924506137457663]]; | |
// NOTE: For CIE XYZ color | |
static mat_d65_to_dci = [ | |
[0.976578896646979768, -0.0154362646984919742, -0.016686021704209866], | |
[-0.0256896658505145926, 1.02853916787996963, -0.00378517365630504153], | |
[-0.00570574587417104179, 0.0110778657389971485, 0.871176159390377409]]; | |
// NOTE: For CIE XYZ color | |
static mat_dci_to_d65 = [ | |
[1.02449672775257752, 0.0151635410224165156, 0.0196885223342066827], | |
[0.0256121933371584198, 0.97258630562441342, 0.00471635229242730096], | |
[0.0063842306500876874, -0.012268082736730219, 1.14794244517367791]]; | |
static mat_xyz_to_lms = [ | |
[ 0.8951, 0.2664,-0.1614], | |
[-0.7502, 1.7135, 0.0367], | |
[ 0.0389,-0.0685, 1.0296]]; | |
static mat_lms_to_xyz = [ | |
[ 0.986993, -0.147054, 0.159963], | |
[ 0.432305, 0.51836, 0.0492912], | |
[ -0.00852866, 0.0400428, 0.968487]]; | |
// For OKLAB's XYZ to LMS | |
static mat_oklab_m1 = [ | |
[ 0.8189330101, 0.3618667424, -0.1288597137], | |
[ 0.0329845436, 0.9293118715, 0.0361456387], | |
[ 0.0482003018, 0.2643662691, 0.6338517070]]; | |
// For OKLAB's non-linear L'M'S' to OKLAB | |
static mat_oklab_m2 = [ | |
[ 0.2104542553, 0.7936177850, -0.0040720468], | |
[ 1.9779984951, -2.4285922050, 0.4505937099], | |
[ 0.0259040371, 0.7827717662, -0.8086757660]]; | |
// Inverse of OKLAB M1 | |
static mat_oklab_m1_inv = [ | |
[ 1.22701385, -0.55779998, 0.28125615], | |
[-0.04058018, 1.11225687, -0.07167668], | |
[-0.07638128, -0.42148198, 1.58616322]]; | |
// Inverse of OKLAB M2 | |
static mat_oklab_m2_inv = [ | |
[ 1. , 0.39633779, 0.21580376], | |
[ 1.00000001, -0.10556134, -0.06385417], | |
[ 1.00000005, -0.08948418, -1.29148554]]; | |
} | |
export class TransferFunction { | |
static srgb_eotf = (v) => { | |
let s1 = v / 12.92; | |
let s2 = ti.pow((v + 0.055) / 1.055, 2.4); | |
return tm.where_lte(v, 0.04045, s1, s2); | |
} | |
static srgb_oetf = (v) => { | |
let s1 = v * 12.92; | |
let s2 = ti.pow(v, 1. / 2.4) * 1.055 - 0.055; | |
return tm.where_lte(v, 0.0031308, s1, s2); | |
} | |
static rec709_eotf = (v) => { | |
let s1 = v / 4.5; | |
let s2 = ti.pow((v + 0.099) / 1.099, 2.2); | |
return tm.where_lte(v, 0.081, s1, s2); | |
} | |
static rec709_oetf = (v) => { | |
let s1 = v * 4.5; | |
let s2 = ti.pow(v, 1. / 2.2) * 1.099 - 0.099; | |
let cond = 1. - ti.step(0.018, v); | |
return tm.where_lte(v, 0.018, s1, s2); | |
} | |
static rec2020_eotf = (v) => { | |
let a = 1.09929682680944; | |
let b = 0.08124285829; | |
let s1 = v / 4.5; | |
let s2 = ti.pow((v + a - 1.) / a, 1./ 0.45); | |
return tm.where_lte(v, b, s1, s2) | |
} | |
static rec2020_oetf = (v) => { | |
let a = 1.09929682680944; | |
let b = 0.018053968510807; | |
let s1 = v * 4.5; | |
let s2 = a * ti.pow(v, .45) - (a - 1.); | |
return tm.where_lte(v, b, s1, s2); | |
} | |
static dcip3_eotf = (v) => { | |
return ti.pow(ti.abs(v), 2.6) * ti.sign(v); | |
} | |
static dcip3_oetf = (v) => { | |
return ti.pow(ti.abs(v), 1./2.6) * ti.sign(v); | |
} | |
static log_c_eotf = (v) => { | |
let offset = 0.00937677; | |
let x = tm.where_gt(v, 0.1496582, | |
ti.pow(10.0, (v - 0.385537) / 0.2471896) * 0.18 - offset, | |
(x / 0.9661776 - 0.04378604) * 0.18 - offset); | |
return x; | |
} | |
static log_c_oetf = (v) => { | |
let offset = 0.00937677; | |
let x = tm.where_gt(v, 0.02 - offset, | |
(((tm.log10((v + offset) / 0.18)) * 0.2471896) + 0.385537), | |
((((v + offset) / 0.18) + 0.04378604) * 0.9661776)); | |
return x; | |
} | |
static s_log_eotf = (v) => { | |
return ti.pow(10.0, ((v - 0.616596 - 0.03) / 0.432699)) - 0.037584; | |
} | |
static s_log_oetf = (v) => { | |
return (0.432699 * tm.log10(v + 0.037584) + 0.616596) + 0.03; | |
} | |
} | |
export var convert_to_xyz = (c, v) => { // [ColorTransform, vec] -> vec | |
if (ti.Static(c.cs_from == ColorTransform.Variant.SRGB_LIN)) { | |
return tm.mm(ColorTransform.mat_srgb_to_xyz, v); | |
} else if (ti.Static(c.cs_from == ColorTransform.Variant.SRGB)) { | |
return tm.mm(ColorTransform.mat_srgb_to_xyz, TransferFunction.srgb_eotf(v)); | |
} else if (ti.Static(c.cs_from == ColorTransform.Variant.REC709)) { | |
return tm.mm(ColorTransform.mat_srgb_to_xyz, TransferFunction.rec709_eotf(v)); | |
} else if (ti.Static(c.cs_from == ColorTransform.Variant.REC2020_LIN)) { | |
return tm.mm(ColorTransform.mat_rec2020_to_xyz, v); | |
} else if (ti.Static(c.cs_from == ColorTransform.Variant.REC2020)) { | |
return tm.mm(ColorTransform.mat_rec2020_to_xyz, TransferFunction.rec2020_eotf(v)); | |
} else if (ti.Static(c.cs_from == ColorTransform.Variant.DCI_P3_LIN)) { | |
return tm.mm(ColorTransform.mat_dci_to_d65, tm.mm(ColorTransform.mat_dcip3_to_xyz, v)); | |
} else if (ti.Static(c.cs_from == ColorTransform.Variant.DCI_P3)) { | |
return tm.mm(ColorTransform.mat_dci_to_d65, tm.mm(ColorTransform.mat_dcip3_to_xyz, TransferFunction.dcip3_eotf(v))); | |
} else if (ti.Static(c.cs_from == ColorTransform.Variant.LMS)) { | |
return tm.mm(ColorTransform.mat_lms_to_xyz, v); | |
} else if (ti.Static(c.cs_from == ColorTransform.Variant.ACESCG)) { | |
return tm.mm(ColorTransform.mat_d60_to_d65, tm.mm(ColorTransform.mat_acescg_to_xyz, v)); | |
} | |
} | |
export var convert_from_xyz = (c, v) => { // [ColorTransform, vec] -> vec | |
if (ti.Static(c.cs_to == ColorTransform.Variant.SRGB_LIN)) { | |
return tm.mm(ColorTransform.mat_xyz_to_srgb, v); | |
} else if (ti.Static(c.cs_to == ColorTransform.Variant.SRGB)) { | |
return TransferFunction.srgb_oetf(tm.mm(ColorTransform.mat_xyz_to_srgb, v)); | |
} else if (ti.Static(c.cs_to == ColorTransform.Variant.REC709)) { | |
return TransferFunction.rec709_oetf(tm.mm(ColorTransform.mat_xyz_to_srgb, v)); | |
} else if (ti.Static(c.cs_to == ColorTransform.Variant.REC2020_LIN)) { | |
return tm.mm(ColorTransform.mat_xyz_to_rec2020, v); | |
} else if (ti.Static(c.cs_to == ColorTransform.Variant.REC2020)) { | |
return TransferFunction.rec2020_oetf(tm.mm(ColorTransform.mat_xyz_to_rec2020, v)); | |
} else if (ti.Static(c.cs_to == ColorTransform.Variant.DCI_P3_LIN)) { | |
return tm.mm(ColorTransform.mat_xyz_to_dcip3, tm.mm(ColorTransform.mat_d65_to_dci, v)); | |
} else if (ti.Static(c.cs_to == ColorTransform.Variant.DCI_P3)) { | |
return TransferFunction.dcip3_oetf(tm.mm(ColorTransform.mat_xyz_to_dcip3, tm.mm(ColorTransform.mat_d65_to_dci, v))); | |
} else if (ti.Static(c.cs_to == ColorTransform.Variant.LMS)) { | |
return tm.mm(ColorTransform.mat_xyz_to_lms, v); | |
} else if (ti.Static(c.cs_to == ColorTransform.Variant.ACESCG)) { | |
return tm.mm(ColorTransform.mat_xyz_to_acescg, tm.mm(ColorTransform.mat_d65_to_d60, v)); | |
} | |
} | |
export var convert_color_space = (c, v) => { // [ColorTransform, vec] -> vec | |
if (ti.Static(c.cs_from == ColorTransform.Variant.SRGB && c.cs_to == ColorTransform.Variant.SRGB_LIN)) { | |
return TransferFunction.srgb_eotf(v); | |
} else if (ti.Static(c.cs_from == ColorTransform.Variant.SRGB_LIN && c.cs_to == ColorTransform.Variant.SRGB)) { | |
return TransferFunction.srgb_oetf(v); | |
} else if (ti.Static(c.cs_from == ColorTransform.Variant.SRGB_LIN && c.cs_to == ColorTransform.Variant.REC709)) { | |
return TransferFunction.rec2020_oetf(v); | |
} else if (ti.Static(c.cs_from == ColorTransform.Variant.REC2020 && c.cs_to == ColorTransform.Variant.REC2020_LIN)) { | |
return TransferFunction.rec2020_eotf(v); | |
} else if (ti.Static(c.cs_from == ColorTransform.Variant.REC2020_LIN && c.cs_to == ColorTransform.Variant.REC2020)) { | |
return TransferFunction.rec2020_oetf(v); | |
} else if (ti.Static(c.cs_from == ColorTransform.Variant.DCI_P3 && c.cs_to == ColorTransform.Variant.DCI_P3_LIN)) { | |
return TransferFunction.dcip3_eotf(v); | |
} else if (ti.Static(c.cs_from == ColorTransform.Variant.DCI_P3_LIN && c.cs_to == ColorTransform.Variant.DCI_P3)) { | |
return TransferFunction.dcip3_oetf(v); | |
} else if (ti.Static(c.cs_from == ColorTransform.Variant.ACESCG && c.cs_to == ColorTransform.Variant.SRGB_LIN)) { | |
return tm.mm(ColorTransform.mat_acescg_to_srgb, v); | |
} else if (ti.Static(c.cs_from == ColorTransform.Variant.SRGB_LIN && c.cs_to == ColorTransform.Variant.ACESCG)) { | |
return tm.mm(ColorTransform.mat_srgb_to_acescg, v); | |
} else if (ti.Static(c.cs_from == ColorTransform.Variant.ACESCG && c.cs_to == ColorTransform.Variant.SRGB)) { | |
return TransferFunction.srgb_oetf(tm.mm(ColorTransform.mat_acescg_to_srgb, v)); | |
} else if (ti.Static(c.cs_from == ColorTransform.Variant.SRGB && c.cs_to == ColorTransform.Variant.ACESCG)) { | |
return tm.mm(ColorTransform.mat_srgb_to_acescg, TransferFunction.srgb_eotf(v)); | |
} else if (ti.Static(c.cs_to == c.cs_from)) { | |
return v; | |
} else if (ti.Static(c.cs_to == ColorTransform.Variant.CIE_XYZ)) { | |
return convert_to_xyz(c, v); | |
} else if (ti.Static(c.cs_from == ColorTransform.Variant.CIE_XYZ)) { | |
return convert_from_xyz(c, v); | |
} else { | |
return convert_from_xyz(c, convert_to_xyz(c, v)); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment