nx_eink_tone.nx
buildroot/runtime/nx_eink_tone.nx
about
nx_eink_tone.nx -- PERCEPTUAL TONE QUANTIZER for e-ink-style rendering. Integer-exact, no float.
WHY: an e-ink panel has ~16 grey levels, not 256. Getting from a photograph or a rendered page down to 16
levels is where the "ink on paper" look is won or lost, and it turns on TWO decisions that are usually got
wrong:
1. QUANTIZE IN PERCEPTUAL LIGHTNESS, NOT IN sRGB OR LINEAR. sRGB code values are not perceptually
spaced, so 16 evenly-spaced sRGB steps put most of their resolution where the eye cannot use it and
band visibly in the shadows. CIE L* is defined to be perceptually uniform, so 16 evenly-spaced L*
steps look evenly spaced. This is the difference between "16 greys" and "16 USEFUL greys".
2. DITHER WITH A BLUE-NOISE MASK, NOT BY ROUNDING. Rounding to the nearest level produces flat bands.
A blue-noise threshold (nx_bluenoise) breaks the band into a texture the eye integrates back to the
true tone without ever resolving structure.
The sRGB->linear table is BAKED DATA, computed from the exact piecewise sRGB transfer function and scaled
to 1e5. It is a table rather than a runtime power because x^2.4 in integer arithmetic is either wrong or
expensive, and a transfer curve that differs per host makes every render irreproducible.
KNOWN-ANSWER ANCHOR: sRGB 128 -> linear 21586 -> L* 53.59, the textbook value for mid-grey. The gate
asserts it, so a silent regression in the table or the cube root cannot pass.
nx_eink_tone lstar <r> <g> <b> -- CIE L* x1000 for one sRGB colour
nx_eink_tone ramp <levels> -- the quantization ladder, in L* and in sRGB
nx_eink_tone dither <w> <h> <gray> <levels> <mask> <masksize> -- dither a FLAT tone, report the mix
nx_eink_tone selftest -- the gate
license_tier: ORIGINAL
dependencies 2 imports · 0 importers
imports: nx_gate_verdict.nxnx_syscalls.nx
imported by: nobody (leaf or entry point)
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 28 | const ET_MAGIC_1033: i64 = 1033 |
| 29 | const ET_MAGIC_1096: i64 = 1096 |
| 30 | const ET_MAGIC_1161: i64 = 1161 |
| 31 | const ET_MAGIC_1229: i64 = 1229 |
| 32 | const ET_MAGIC_1298: i64 = 1298 |
| 33 | const ET_MAGIC_1370: i64 = 1370 |
| 34 | const ET_MAGIC_1444: i64 = 1444 |
| 35 | const ET_MAGIC_1521: i64 = 1521 |
| 36 | const ET_MAGIC_1600: i64 = 1600 |
| 37 | const ET_MAGIC_1681: i64 = 1681 |
| 38 | const ET_MAGIC_1764: i64 = 1764 |
| 39 | const ET_MAGIC_1850: i64 = 1850 |
| 40 | const ET_MAGIC_1938: i64 = 1938 |
| 41 | const ET_MAGIC_2029: i64 = 2029 |
| 42 | const ET_MAGIC_2122: i64 = 2122 |
| 43 | const ET_MAGIC_2217: i64 = 2217 |
| 44 | const ET_MAGIC_2315: i64 = 2315 |
| 45 | const ET_MAGIC_2416: i64 = 2416 |
| 46 | const ET_MAGIC_2519: i64 = 2519 |
| 47 | const ET_MAGIC_2624: i64 = 2624 |
| 48 | const ET_MAGIC_2732: i64 = 2732 |
| 49 | const ET_MAGIC_2843: i64 = 2843 |
| 50 | const ET_MAGIC_2956: i64 = 2956 |
| 51 | const ET_MAGIC_3071: i64 = 3071 |
| 52 | const ET_MAGIC_3190: i64 = 3190 |
| 53 | const ET_MAGIC_3310: i64 = 3310 |
| 54 | const ET_MAGIC_3434: i64 = 3434 |
| 55 | const ET_MAGIC_3560: i64 = 3560 |
| 56 | const ET_MAGIC_3689: i64 = 3689 |
| 57 | const ET_MAGIC_3820: i64 = 3820 |
| 58 | const ET_MAGIC_3955: i64 = 3955 |
| 59 | const ET_MAGIC_4092: i64 = 4092 |
| 60 | const ET_MAGIC_4231: i64 = 4231 |
| 61 | const ET_MAGIC_4374: i64 = 4374 |
| 62 | const ET_MAGIC_4519: i64 = 4519 |
| 63 | const ET_MAGIC_4667: i64 = 4667 |
| 64 | const ET_MAGIC_4817: i64 = 4817 |
| 65 | const ET_MAGIC_4971: i64 = 4971 |
| 66 | const ET_MAGIC_5127: i64 = 5127 |
| 67 | const ET_MAGIC_5286: i64 = 5286 |
| 68 | const ET_MAGIC_5448: i64 = 5448 |
| 69 | const ET_MAGIC_5613: i64 = 5613 |
| 70 | const ET_MAGIC_5781: i64 = 5781 |
| 71 | const ET_MAGIC_5951: i64 = 5951 |
| 72 | const ET_MAGIC_6125: i64 = 6125 |
| 73 | const ET_MAGIC_6301: i64 = 6301 |
| 74 | const ET_MAGIC_6480: i64 = 6480 |
| 75 | const ET_MAGIC_6663: i64 = 6663 |
| 76 | const ET_MAGIC_6848: i64 = 6848 |
| 77 | const ET_MAGIC_7036: i64 = 7036 |
| 78 | const ET_MAGIC_7227: i64 = 7227 |
| 79 | const ET_MAGIC_7421: i64 = 7421 |
| 80 | const ET_MAGIC_7619: i64 = 7619 |
| 81 | const ET_MAGIC_7819: i64 = 7819 |
| 82 | const ET_MAGIC_8022: i64 = 8022 |
| 83 | const ET_MAGIC_8228: i64 = 8228 |
| 84 | const ET_MAGIC_8438: i64 = 8438 |
| 85 | const ET_MAGIC_8650: i64 = 8650 |
| 86 | const ET_MAGIC_8866: i64 = 8866 |
| 87 | const ET_MAGIC_9084: i64 = 9084 |
| 88 | const ET_MAGIC_9306: i64 = 9306 |
| 89 | const ET_MAGIC_9531: i64 = 9531 |
| 90 | const ET_MAGIC_9759: i64 = 9759 |
| 91 | const ET_MAGIC_9990: i64 = 9990 |
| 92 | const ET_MAGIC_10224: i64 = 10224 |
| 93 | const ET_MAGIC_10462: i64 = 10462 |
| 94 | const ET_MAGIC_10702: i64 = 10702 |
| 95 | const ET_MAGIC_10946: i64 = 10946 |
| 96 | const ET_MAGIC_11193: i64 = 11193 |
| 97 | const ET_MAGIC_11444: i64 = 11444 |
| 98 | const ET_MAGIC_11697: i64 = 11697 |
| 99 | const ET_MAGIC_11954: i64 = 11954 |
| 100 | const ET_MAGIC_12214: i64 = 12214 |
| 101 | const ET_MAGIC_12477: i64 = 12477 |
| 102 | const ET_MAGIC_12744: i64 = 12744 |
| 103 | const ET_MAGIC_13014: i64 = 13014 |
| 104 | const ET_MAGIC_13287: i64 = 13287 |
| 105 | const ET_MAGIC_13563: i64 = 13563 |
| 106 | const ET_MAGIC_13843: i64 = 13843 |
| 107 | const ET_MAGIC_14126: i64 = 14126 |
| 108 | const ET_MAGIC_14413: i64 = 14413 |
| 109 | const ET_MAGIC_14703: i64 = 14703 |
| 110 | const ET_MAGIC_14996: i64 = 14996 |
| 111 | const ET_MAGIC_15293: i64 = 15293 |
| 112 | const ET_MAGIC_15593: i64 = 15593 |
| 113 | const ET_MAGIC_15896: i64 = 15896 |
| 114 | const ET_MAGIC_16203: i64 = 16203 |
| 115 | const ET_MAGIC_16513: i64 = 16513 |
| 116 | const ET_MAGIC_16827: i64 = 16827 |
| 117 | const ET_MAGIC_17144: i64 = 17144 |
| 118 | const ET_MAGIC_17465: i64 = 17465 |
| 119 | const ET_MAGIC_17789: i64 = 17789 |
| 120 | const ET_MAGIC_18116: i64 = 18116 |
| 121 | const ET_MAGIC_18447: i64 = 18447 |
| 122 | const ET_MAGIC_18782: i64 = 18782 |
| 123 | const ET_MAGIC_19120: i64 = 19120 |
| 124 | const ET_MAGIC_19462: i64 = 19462 |
| 125 | const ET_MAGIC_19807: i64 = 19807 |
| 126 | const ET_MAGIC_20156: i64 = 20156 |
| 127 | const ET_MAGIC_20508: i64 = 20508 |
| 128 | const ET_MAGIC_20864: i64 = 20864 |
| 129 | const ET_MAGIC_21223: i64 = 21223 |
| 130 | const ET_MAGIC_21586: i64 = 21586 |
| 131 | const ET_MAGIC_21953: i64 = 21953 |
| 132 | const ET_MAGIC_22323: i64 = 22323 |
| 133 | const ET_MAGIC_22697: i64 = 22697 |
| 134 | const ET_MAGIC_23074: i64 = 23074 |
| 135 | const ET_MAGIC_23455: i64 = 23455 |
| 136 | const ET_MAGIC_23840: i64 = 23840 |
| 137 | const ET_MAGIC_24228: i64 = 24228 |
| 138 | const ET_MAGIC_24620: i64 = 24620 |
| 139 | const ET_MAGIC_25016: i64 = 25016 |
| 140 | const ET_MAGIC_25415: i64 = 25415 |
| 141 | const ET_MAGIC_25818: i64 = 25818 |
| 142 | const ET_MAGIC_26225: i64 = 26225 |
| 143 | const ET_MAGIC_26636: i64 = 26636 |
| 144 | const ET_MAGIC_27050: i64 = 27050 |
| 145 | const ET_MAGIC_27468: i64 = 27468 |
| 146 | const ET_MAGIC_27889: i64 = 27889 |
| 147 | const ET_MAGIC_28315: i64 = 28315 |
| 148 | const ET_MAGIC_28744: i64 = 28744 |
| 149 | const ET_MAGIC_29177: i64 = 29177 |
| 150 | const ET_MAGIC_29614: i64 = 29614 |
| 151 | const ET_MAGIC_30054: i64 = 30054 |
| 152 | const ET_MAGIC_30499: i64 = 30499 |
| 153 | const ET_MAGIC_30947: i64 = 30947 |
| 154 | const ET_MAGIC_31399: i64 = 31399 |
| 155 | const ET_MAGIC_31855: i64 = 31855 |
| 156 | const ET_MAGIC_32314: i64 = 32314 |
| 157 | const ET_MAGIC_32778: i64 = 32778 |
| 158 | const ET_MAGIC_33245: i64 = 33245 |
| 159 | const ET_MAGIC_33716: i64 = 33716 |
| 160 | const ET_MAGIC_34191: i64 = 34191 |
| 161 | const ET_MAGIC_34670: i64 = 34670 |
| 162 | const ET_MAGIC_35153: i64 = 35153 |
| 163 | const ET_MAGIC_35640: i64 = 35640 |
| 164 | const ET_MAGIC_36131: i64 = 36131 |
| 165 | const ET_MAGIC_36625: i64 = 36625 |
| 166 | const ET_MAGIC_37124: i64 = 37124 |
| 167 | const ET_MAGIC_37626: i64 = 37626 |
| 168 | const ET_MAGIC_38133: i64 = 38133 |
| 169 | const ET_MAGIC_38643: i64 = 38643 |
| 170 | const ET_MAGIC_39157: i64 = 39157 |
| 171 | const ET_MAGIC_39676: i64 = 39676 |
| 172 | const ET_MAGIC_40198: i64 = 40198 |
| 173 | const ET_MAGIC_40724: i64 = 40724 |
| 174 | const ET_MAGIC_41254: i64 = 41254 |
| 175 | const ET_MAGIC_41789: i64 = 41789 |
| 176 | const ET_MAGIC_42327: i64 = 42327 |
| 177 | const ET_MAGIC_42869: i64 = 42869 |
| 178 | const ET_MAGIC_43415: i64 = 43415 |
| 179 | const ET_MAGIC_43966: i64 = 43966 |
| 180 | const ET_MAGIC_44520: i64 = 44520 |
| 181 | const ET_MAGIC_45079: i64 = 45079 |
| 182 | const ET_MAGIC_45641: i64 = 45641 |
| 183 | const ET_MAGIC_46208: i64 = 46208 |
| 184 | const ET_MAGIC_46778: i64 = 46778 |
| 185 | const ET_MAGIC_47353: i64 = 47353 |
| 186 | const ET_MAGIC_47932: i64 = 47932 |
| 187 | const ET_MAGIC_48515: i64 = 48515 |
| 188 | const ET_MAGIC_49102: i64 = 49102 |
| 189 | const ET_MAGIC_49693: i64 = 49693 |
| 190 | const ET_MAGIC_50289: i64 = 50289 |
| 191 | const ET_MAGIC_50888: i64 = 50888 |
| 192 | const ET_MAGIC_51492: i64 = 51492 |
| 193 | const ET_MAGIC_52100: i64 = 52100 |
| 194 | const ET_MAGIC_52712: i64 = 52712 |
| 195 | const ET_MAGIC_53328: i64 = 53328 |
| 196 | const ET_MAGIC_53948: i64 = 53948 |
| 197 | const ET_MAGIC_54572: i64 = 54572 |
| 198 | const ET_MAGIC_55201: i64 = 55201 |
| 199 | const ET_MAGIC_55834: i64 = 55834 |
| 200 | const ET_MAGIC_56471: i64 = 56471 |
| 201 | const ET_MAGIC_57112: i64 = 57112 |
| 202 | const ET_MAGIC_57758: i64 = 57758 |
| 203 | const ET_MAGIC_58408: i64 = 58408 |
| 204 | const ET_MAGIC_59062: i64 = 59062 |
| 205 | const ET_MAGIC_59720: i64 = 59720 |
| 206 | const ET_MAGIC_60383: i64 = 60383 |
| 207 | const ET_MAGIC_61050: i64 = 61050 |
| 208 | const ET_MAGIC_61721: i64 = 61721 |
| 209 | const ET_MAGIC_62396: i64 = 62396 |
| 210 | const ET_MAGIC_63076: i64 = 63076 |
| 211 | const ET_MAGIC_63760: i64 = 63760 |
| 212 | const ET_MAGIC_64448: i64 = 64448 |
| 213 | const ET_MAGIC_65141: i64 = 65141 |
| 214 | const ET_MAGIC_65837: i64 = 65837 |
| 215 | const ET_MAGIC_66539: i64 = 66539 |
| 216 | const ET_MAGIC_67244: i64 = 67244 |
| 217 | const ET_MAGIC_67954: i64 = 67954 |
| 218 | const ET_MAGIC_68669: i64 = 68669 |
| 219 | const ET_MAGIC_69387: i64 = 69387 |
| 220 | const ET_MAGIC_70110: i64 = 70110 |
| 221 | const ET_MAGIC_70838: i64 = 70838 |
| 222 | const ET_MAGIC_71569: i64 = 71569 |
| 223 | const ET_MAGIC_72306: i64 = 72306 |
| 224 | const ET_MAGIC_73046: i64 = 73046 |
| 225 | const ET_MAGIC_73791: i64 = 73791 |
| 226 | const ET_MAGIC_74540: i64 = 74540 |
| 227 | const ET_MAGIC_75294: i64 = 75294 |
| 228 | const ET_MAGIC_76052: i64 = 76052 |
| 229 | const ET_MAGIC_76815: i64 = 76815 |
| 230 | const ET_MAGIC_77582: i64 = 77582 |
| 231 | const ET_MAGIC_78354: i64 = 78354 |
| 232 | const ET_MAGIC_79130: i64 = 79130 |
| 233 | const ET_MAGIC_79910: i64 = 79910 |
| 234 | const ET_MAGIC_80695: i64 = 80695 |
| 235 | const ET_MAGIC_81485: i64 = 81485 |
| 236 | const ET_MAGIC_82279: i64 = 82279 |
| 237 | const ET_MAGIC_83077: i64 = 83077 |
| 238 | const ET_MAGIC_83880: i64 = 83880 |
| 239 | const ET_MAGIC_84687: i64 = 84687 |
| 240 | const ET_MAGIC_85499: i64 = 85499 |
| 241 | const ET_MAGIC_86316: i64 = 86316 |
| 242 | const ET_MAGIC_87137: i64 = 87137 |
| 243 | const ET_MAGIC_87962: i64 = 87962 |
| 244 | const ET_MAGIC_88792: i64 = 88792 |
| 245 | const ET_MAGIC_89627: i64 = 89627 |
| 246 | const ET_MAGIC_90466: i64 = 90466 |
| 247 | const ET_MAGIC_91310: i64 = 91310 |
| 248 | const ET_MAGIC_92158: i64 = 92158 |
| 249 | const ET_MAGIC_93011: i64 = 93011 |
| 250 | const ET_MAGIC_93869: i64 = 93869 |
| 251 | const ET_MAGIC_94731: i64 = 94731 |
| 252 | const ET_MAGIC_95597: i64 = 95597 |
| 253 | const ET_MAGIC_96469: i64 = 96469 |
| 254 | const ET_MAGIC_97345: i64 = 97345 |
| 255 | const ET_MAGIC_98225: i64 = 98225 |
| 256 | const ET_MAGIC_99110: i64 = 99110 |
| 257 | const ET_MAGIC_100000: i64 = 100000 |
| 258 | const ET_MAGIC_53000: i64 = 53000 |
| 259 | const ET_MAGIC_54200: i64 = 54200 |
| 260 | const ET_MAGIC_1500: i64 = 1500 |
| 261 | const ET_MAGIC_5000: i64 = 5000 |
| 263 | const ET_LIN_SCALE: i64 = 100000 // linear light fixed-point scale (1.0 == 100000) |
| 264 | const ET_LSTAR_SCALE: i64 = 1000 // L* fixed-point scale (L*=100 -> 100000) |
| 265 | const ET_LSTAR_MAX: i64 = 100000 // L* 100.000 |
| 266 | const ET_KAPPA: i64 = 9033 // CIE kappa 903.3, x10 -- the linear branch slope |
| 267 | const ET_KAPPA_DIV: i64 = 1000 // pairs with ET_KAPPA to land L* on the x1000 scale (see et_lstar_from_y) |
| 268 | const ET_EPS_LIN: i64 = 886 // CIE epsilon 0.008856 expressed on ET_LIN_SCALE |
| 269 | const ET_CBRT_IN: i64 = 10000 // cbrt input scaling: cbrt(Y*1e4) lands f on 0..1000 |
| 270 | const ET_F_SCALE: i64 = 1000 |
| 271 | const ET_L_A: i64 = 116 // L* = 116*f - 16 |
| 272 | const ET_L_B: i64 = 16000 // 16 on the x1000 scale |
| 273 | const ET_RW: i64 = 2126 // Rec.709 luminance weights x10000 (sum 10000 exactly) |
| 274 | const ET_GW: i64 = 7152 |
| 275 | const ET_BW: i64 = 722 |
| 276 | const ET_WSUM: i64 = 10000 |
| 277 | const ET_BYTE_MAX: i64 = 255 |
| 278 | const ET_BYTE_MAX_P1: i64 = 256 // divisor for mapping a 0..255 mask byte onto a 0..999 threshold |
| 279 | const ET_MIN_LEVELS: i64 = 2 |
| 280 | const ET_MAX_LEVELS: i64 = 256 |
| 281 | const ET_EINK_LEVELS: i64 = 16 // what an E Ink Carta panel actually resolves |
functions
| 283 | func et_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } |
| 284 | func et_num(v: i64) -> i64 |
| 294 | func et_atoi(s: *u8) -> i64 called by 1: main |
| 299 | func et_seq(a: *u8, b: *u8) -> i64 called by 1: main |
| 306 | func et_srgb_lin(c: i64) -> i64 |
| 346 | func et_icbrt(v: i64) -> i64 called by 1: et_lstar_from_y |
| 361 | func et_lstar_from_y(y: i64) -> i64 |
| 373 | func et_lstar_rgb(r: i64, g: i64, b: i64) -> i64 |
| 379 | func et_srgb_from_lstar(target: i64) -> i64 |
| 396 | func et_quant(lstar: i64, levels: i64, thresh_permil: i64) -> i64 |
| 409 | func et_selftest() -> i64 |
| 545 | func main(argc: i64, argv: *i64) -> i64 |