nx_colorsci_lib.nx
buildroot/runtime/nx_colorsci_lib.nx
about
nx_colorsci_lib.nx -- COLOUR SCIENCE AS ONE SOVEREIGN RULER (2026-08-24, phototwin PT1: cs_de2000, cs_icc_gamut).
license_tier: ORIGINAL No hw writes (Rule 26). LIB (no main).
WHAT IT IS. CIE 1976 L*a*b* both ways (CIE 15:2004), CIEDE2000 (CIE 142-2001, Sharma-Wu-Dalal 2005 eqs 4-22,
KAT = their Table I as data in knowledge/colorsci_ciede2000_kat.tsv), an ICC v2/v4 matrix/TRC profile reader
(rXYZ gXYZ bXYZ wtpt rTRC gTRC bTRC; the PCS is D50 by the spec, so image and substrate meet in ONE space), a
segment-maxima gamut boundary descriptor (Morovic and Luo 2000) and chroma clipping toward the achromatic
axis at constant L and hue. All arithmetic is nx_fixq30_lib Q30 fixed point: no float, no typed-in constants.
WHY THROUGH THE PROFILE. The estate had RGB/HSV/YCbCr and no CIELAB, so no colour difference could be stated in
the unit the whole field uses. Going RGB -> TRC -> matrix -> XYZ(D50) -> Lab through a parsed ICC profile is
the ICC workflow itself, which means a measured substrate profile from a swatch bench drops in with no code.
EVERY THRESHOLD IS CONF (knowledge/colorsci.conf, cs_conf_int) and an absent key is a REFUSAL, never a default.
dependencies 2 imports · 6 importers
imports: nx_syscalls.nxnx_fixq30_lib.nx
imported by: nx_colorsci_cat_gate.nxnx_colorsci_gate.nxnx_de_referee_lib.nxnx_garment_twin.nxnx_phototwin.nxnx_phototwin_gate.nx
structs
| none |
consts
| 19 | const CS_LAB_DELTA_NUM: i64 = 6 |
| 20 | const CS_LAB_DELTA_DEN: i64 = 29 |
| 21 | const CS_LAB_L_SCALE: i64 = 116 |
| 22 | const CS_LAB_L_OFFSET: i64 = 16 |
| 23 | const CS_LAB_A_SCALE: i64 = 500 |
| 24 | const CS_LAB_B_SCALE: i64 = 200 |
| 25 | const CS_LAB_F_LIN_NUM: i64 = 4 |
| 26 | const CS_LAB_F_LIN_DEN: i64 = 29 |
| 27 | const CS_THREE: i64 = 3 |
| 28 | const CS_TWO: i64 = 2 |
| 30 | const CS_DE_C_REF: i64 = 25 |
| 31 | const CS_DE_T_W1_MILLI: i64 = 170 |
| 32 | const CS_DE_T_W2_MILLI: i64 = 240 |
| 33 | const CS_DE_T_W3_MILLI: i64 = 320 |
| 34 | const CS_DE_T_W4_MILLI: i64 = 200 |
| 35 | const CS_DE_T_D1_DEG: i64 = 30 |
| 36 | const CS_DE_T_M2: i64 = 2 |
| 37 | const CS_DE_T_M3: i64 = 3 |
| 38 | const CS_DE_T_D3_DEG: i64 = 6 |
| 39 | const CS_DE_T_M4: i64 = 4 |
| 40 | const CS_DE_T_D4_DEG: i64 = 63 |
| 41 | const CS_DE_THETA_CENTER_DEG: i64 = 275 |
| 42 | const CS_DE_THETA_WIDTH_DEG: i64 = 25 |
| 43 | const CS_DE_THETA_SCALE_DEG: i64 = 30 |
| 44 | const CS_DE_SL_K_MILLI: i64 = 15 |
| 45 | const CS_DE_SL_L0: i64 = 50 |
| 46 | const CS_DE_SL_C: i64 = 20 |
| 47 | const CS_DE_SC_K_MILLI: i64 = 45 |
| 48 | const CS_DE_SH_K_MILLI: i64 = 15 |
| 49 | const CS_MILLI: i64 = 1000 |
| 50 | const CS_DEG_FULL: i64 = 360 |
| 51 | const CS_DEG_HALF: i64 = 180 |
| 52 | const CS_HUE_KNIFE_EPS_Q30: i64 = 64 // 6e-8 degrees: the rounding bound of two Q30 rad->deg conversions (see cs_de2000_ex) |
| 54 | const CS_X_FQ: i64 = 0 |
| 55 | const CS_X_DELTA3: i64 = 1 |
| 56 | const CS_X_INV3D2: i64 = 2 |
| 57 | const CS_X_FLIN_OFF: i64 = 3 |
| 58 | const CS_X_DELTA: i64 = 4 |
| 59 | const CS_X_SLOTS: i64 = 8 |
| 60 | const CS_SLOT: i64 = 8 |
| 62 | const CS_ICC_HDR_BYTES: i64 = 128 |
| 63 | const CS_ICC_MAGIC_OFF: i64 = 36 |
| 64 | const CS_ICC_TAG_ENTRY_BYTES: i64 = 12 |
| 65 | const CS_ICC_TAG_OFF_FIELD: i64 = 4 |
| 66 | const CS_ICC_TAG_SIZE_FIELD: i64 = 8 |
| 67 | const CS_ICC_TYPE_HDR_BYTES: i64 = 8 |
| 68 | const CS_ICC_XYZ_BYTES: i64 = 4 |
| 69 | const CS_ICC_S15F16_SCALE: i64 = 16384 // 2^(30-16): s15Fixed16 -> Q30 |
| 70 | const CS_ICC_U8F8_SCALE: i64 = 4194304 // 2^(30-8): u8Fixed8 -> Q30 |
| 71 | const CS_ICC_U16_MAX: i64 = 65535 |
| 72 | const CS_ICC_U32_SIGN: i64 = 2147483648 |
| 73 | const CS_ICC_U32_WRAP: i64 = 4294967296 |
| 74 | const CS_ICC_TRC_IDENTITY: i64 = 0 |
| 75 | const CS_ICC_TRC_GAMMA: i64 = 1 |
| 76 | const CS_ICC_TRC_TABLE: i64 = 2 |
| 77 | const CS_ICC_TAGS_NEEDED: i64 = 7 |
| 78 | const CS_BYTE_BITS_3: i64 = 24 |
| 79 | const CS_BYTE_BITS_2: i64 = 16 |
| 80 | const CS_BYTE_BITS_1: i64 = 8 |
| 81 | const CS_BYTE_MAX: i64 = 255 |
| 82 | const CS_LUT_ENTRIES: i64 = 256 |
| 83 | const CS_FOURCC: i64 = 4 |
| 84 | const CS_P_MAT: i64 = 0 |
| 85 | const CS_P_WTPT: i64 = 9 |
| 86 | const CS_P_LUT_R: i64 = 12 |
| 87 | const CS_P_LUT_G: i64 = 13 |
| 88 | const CS_P_LUT_B: i64 = 14 |
| 89 | const CS_P_INV: i64 = 15 |
| 90 | const CS_P_OK: i64 = 24 |
| 91 | const CS_P_BYTES: i64 = 25 |
| 92 | const CS_P_TAGS: i64 = 26 |
| 93 | const CS_P_TRC_R: i64 = 27 |
| 94 | const CS_P_TRC_G: i64 = 28 |
| 95 | const CS_P_TRC_B: i64 = 29 |
| 96 | const CS_P_HAVE: i64 = 30 |
| 97 | const CS_P_B64: i64 = 31 // 1 when the profile arrived as base64 text |
| 98 | const CS_P_SLOTS: i64 = 32 |
| 100 | const CS_B64_UPPER_A: i64 = 65 |
| 101 | const CS_B64_UPPER_Z: i64 = 90 |
| 102 | const CS_B64_LOWER_A: i64 = 97 |
| 103 | const CS_B64_LOWER_Z: i64 = 122 |
| 104 | const CS_B64_LOWER_BASE: i64 = 26 |
| 105 | const CS_B64_DIGIT_BASE: i64 = 52 |
| 106 | const CS_B64_PLUS: i64 = 43 |
| 107 | const CS_B64_PLUS_VAL: i64 = 62 |
| 108 | const CS_B64_SLASH: i64 = 47 |
| 109 | const CS_B64_SLASH_VAL: i64 = 63 |
| 110 | const CS_B64_PAD: i64 = 61 |
| 111 | const CS_B64_BITS: i64 = 6 |
| 112 | const CS_MAT_N: i64 = 9 |
| 113 | const CS_MAT_COLS: i64 = 3 |
| 115 | const CS_G_H: i64 = 0 |
| 116 | const CS_G_E: i64 = 1 |
| 117 | const CS_G_COUNT: i64 = 2 |
| 118 | const CS_G_BASE: i64 = 3 |
| 119 | const CS_G_CENTER_L: i64 = 50 |
| 120 | const CS_PERMIL: i64 = 1000 |
| 121 | const CS_G_UNSAMPLED: i64 = 0 - 1 |
| 122 | const CS_CUBE_FACES: i64 = 6 |
| 123 | const CS_CLIP_LOW_RADIUS: i64 = 0 |
| 125 | const CS_CONF_MISS: i64 = 0 - 999999999 |
| 126 | const CS_ASCII_NL: i64 = 10 |
| 127 | const CS_ASCII_EQ: i64 = 61 |
| 128 | const CS_ASCII_ZERO: i64 = 48 |
| 129 | const CS_ASCII_NINE: i64 = 57 |
| 130 | const CS_DECIMAL: i64 = 10 |
| 140 | const CS_CAT_SCALE: i64 = 10000 |
| 141 | const CS_CAT_BRADFORD: i64 = 0 |
| 142 | const CS_CAT_CAT02: i64 = 1 |
| 143 | const CS_CAT_M: i64 = 9 |
| 144 | const CS_CAT_BFD_0: i64 = 8951 |
| 145 | const CS_CAT_BFD_1: i64 = 2664 |
| 146 | const CS_CAT_BFD_2: i64 = 0 - 1614 |
| 147 | const CS_CAT_BFD_3: i64 = 0 - 7502 |
| 148 | const CS_CAT_BFD_4: i64 = 17135 |
| 149 | const CS_CAT_BFD_5: i64 = 367 |
| 150 | const CS_CAT_BFD_6: i64 = 389 |
| 151 | const CS_CAT_BFD_7: i64 = 0 - 685 |
| 152 | const CS_CAT_BFD_8: i64 = 10296 |
| 153 | const CS_CAT_02_0: i64 = 7328 |
| 154 | const CS_CAT_02_1: i64 = 4296 |
| 155 | const CS_CAT_02_2: i64 = 0 - 1624 |
| 156 | const CS_CAT_02_3: i64 = 0 - 7036 |
| 157 | const CS_CAT_02_4: i64 = 16975 |
| 158 | const CS_CAT_02_5: i64 = 61 |
| 159 | const CS_CAT_02_6: i64 = 30 |
| 160 | const CS_CAT_02_7: i64 = 136 |
| 161 | const CS_CAT_02_8: i64 = 9834 |
functions
| 164 | func cs_be32(b: *u8, off: i64) -> i64 |
| 167 | func cs_be16(b: *u8, off: i64) -> i64 { return ((b[off] as i64) << CS_BYTE_BITS_1) + (b[off + 1] as i64) } called by 1: cs_icc_trc_tag |
| 168 | func cs_s15f16_q30(b: *u8, off: i64) -> i64 |
| 173 | func cs_tag_is(b: *u8, off: i64, s: *u8) -> i64 called by 1: cs_icc_load |
| 180 | func cs_conf_int(path: *u8, key: *u8) -> i64 |
| 221 | func cs_ctx() -> *i64 |
| 233 | func cs_fq(cs: *i64) -> *i64 { return cs[CS_X_FQ] as *i64 } |
| 236 | func cs_lab_f(cs: *i64, t: i64) -> i64 |
| 240 | func cs_lab_finv(cs: *i64, f: i64) -> i64 |
| 245 | func cs_lab_from_xyz(cs: *i64, X: i64, Y: i64, Z: i64, Xn: i64, Yn: i64, Zn: i64, out3: *i64) -> i64 |
| 254 | func cs_xyz_from_lab(cs: *i64, L: i64, a: i64, b: i64, Xn: i64, Yn: i64, Zn: i64, out3: *i64) -> i64 |
| 269 | func cs_de_c7_sqrt_ratio(fq: *i64, C: i64) -> i64 |
| 281 | func cs_hue_deg(fq: *i64, a: i64, b: i64) -> i64 |
| 288 | func cs_cos_deg(fq: *i64, deg: i64) -> i64 { return fq_cos(fq, fq_deg2rad(fq, deg)) } |
| 289 | func cs_sin_deg(fq: *i64, deg: i64) -> i64 { return fq_sin(fq, fq_deg2rad(fq, deg)) } |
| 290 | func cs_milli(m: i64) -> i64 { return fq_div(fq_from_int(m), fq_from_int(CS_MILLI)) } |
| 292 | func cs_de2000_ex(cs: *i64, L1: i64, a1: i64, b1: i64, L2: i64, a2: i64, b2: i64, out4: *i64) -> i64 |
| 353 | func cs_de2000(cs: *i64, L1: i64, a1: i64, b1: i64, L2: i64, a2: i64, b2: i64) -> i64 |
| 360 | func cs_icc_xyz_tag(b: *u8, off: i64, p: *i64, col: i64) -> i64 |
| 367 | func cs_icc_wtpt_tag(b: *u8, off: i64, p: *i64) -> i64 |
| 375 | func cs_icc_trc_tag(cs: *i64, b: *u8, off: i64, lut: *i64) -> i64 |
| 406 | func cs_inv3(m: *i64, mo: i64, out: *i64, oo: i64) -> i64 |
| 441 | func cs_b64_val(c: i64) -> i64 called by 1: cs_b64_decode |
| 449 | func cs_b64_decode(src: *u8, n: i64, out: *u8) -> i64 |
| 475 | func cs_icc_load(cs: *i64, path: *u8) -> *i64 called by 4: maincs_icc_gamutmainmain calls 10: sys_mmapsys_read_filecs_tag_iscs_b64_decodecs_be32cs_icc_xyz_tag+4 |
| 530 | func cs_prof_rgb_to_lab(cs: *i64, p: *i64, r: i64, g: i64, b: i64, out3: *i64) -> i64 |
| 543 | func cs_lut_inverse(lut: *i64, lin: i64) -> i64 called by 1: cs_prof_lab_to_rgb |
| 555 | func cs_clamp01(v: i64) -> i64 { if v < 0 { return 0 } if v > FQ_ONE { return FQ_ONE } return v } called by 1: cs_prof_lab_to_rgb |
| 557 | func cs_prof_lab_to_rgb(cs: *i64, p: *i64, L: i64, a: i64, b: i64, out3: *i64) -> i64 |
| 578 | func cs_gbd_alloc(hs: i64, es: i64) -> *i64 |
| 588 | func cs_gbd_sector(cs: *i64, g: *i64, L: i64, a: i64, b: i64, out3: *i64) -> i64 called by 3: cs_gbd_addcs_gbd_permilpt_gamut_report calls 8: cs_fqfq_from_intfq_hypotcs_hue_degfq_atan2fq_to_int+2 |
| 607 | func cs_gbd_add(cs: *i64, g: *i64, L: i64, a: i64, b: i64) -> i64 |
| 616 | func cs_gbd_rmax(g: *i64, hs: i64, es: i64) -> i64 called by 1: cs_gbd_permil |
| 641 | func cs_gbd_unsampled(g: *i64) -> i64 called by 1: main |
| 649 | func cs_gbd_permil(cs: *i64, g: *i64, L: i64, a: i64, b: i64) -> i64 |
| 658 | func cs_gbd_from_profile(cs: *i64, p: *i64, g: *i64, steps: i64) -> i64 |
| 691 | func cs_icc_gamut(cs: *i64, path: *u8, hs: i64, es: i64, steps: i64) -> *i64 |
| 700 | func cs_gamut_clip(cs: *i64, g: *i64, L: i64, a: i64, b: i64, iters: i64, out3: *i64) -> i64 |
| 729 | func cs_prof_lab_to_lin(cs: *i64, p: *i64, L: i64, a: i64, b: i64, out3: *i64) -> i64 |
| 740 | func cs_prof_gamut_permil(cs: *i64, p: *i64, L: i64, a: i64, b: i64) -> i64 called by 3: maincs_prof_gamut_clippt_gamut_report calls 5: sys_mmapcs_prof_lab_to_linfq_to_intfq_mulfq_from_int |
| 756 | func cs_cat_q(v: i64) -> i64 { return fq_div(fq_from_int(v), fq_from_int(CS_CAT_SCALE)) } |
| 760 | func cs_cat_cone(which: i64, out9: *i64) -> i64 |
| 788 | func cs_cat_mul3(a: *i64, b: *i64, out9: *i64) -> i64 |
| 807 | func cs_cat_apply(m9: *i64, X: i64, Y: i64, Z: i64, out3: *i64) -> i64 |
| 817 | func cs_cat_build(Xs: i64, Ys: i64, Zs: i64, Xd: i64, Yd: i64, Zd: i64, which: i64, out9: *i64) -> i64 |
| 846 | func cs_cat_xyz(X: i64, Y: i64, Z: i64, Xs: i64, Ys: i64, Zs: i64, Xd: i64, Yd: i64, Zd: i64, which: i64, out3: *i64) -> i64 |
| 854 | func cs_prof_gamut_clip(cs: *i64, p: *i64, L: i64, a: i64, b: i64, iters: i64, out3: *i64) -> i64 |