code wiki / (root) / nx_skintex_probe.nx

nx_skintex_probe.nx source

↩ module page · 89 lines · 3836 B

1// nx_skintex_probe.nx -- decode a real skin texture and report what is actually in it. 2// 3// The arousal skin model computes reflectance from melanin + haemoglobin and was generating flat 4// colour with hash-noise micro-variation standing in for pores. A real Std_Skin_*_Diffuse map IS the 5// melanin/albedo term, measured off an actual character asset instead of invented -- so the 6// physiology can MODULATE a real albedo rather than replace it. 7// 8// This probe exists before any wiring: prove the decode, learn the resolution, and measure the tone 9// distribution. Building the shader path on an unverified decode would be building on a guess. 10// 11// usage: nx_skintex_probe <file.jpeg> 12// license_tier: ORIGINAL expect_exit: 0 13// ⚠NOT nx_jpeg_ascii: that module's decode_rgb only probes the SOF for dimensions (it backs the 14// JPEG->ASCII-art path). It returned rc=0 with correct 2048x2048 dims and ONE non-black pixel out of 15// 16384 sampled -- a success code over an empty buffer. nx_jpeg_decoder is the real baseline DCT 16// decoder (DQT/DHT/SOS/Huffman). Picking the first symbol that matched the name cost a build cycle. 17import "nx_jpeg_decoder.nx" 18 19func sp_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 20func sp_putn(v: i64) -> i64 { 21 if v == 0 { sys_write(1, "0" as *u8, 1); return 0 } 22 var m: i64 = v 23 if m < 0 { sys_write(1, "-" as *u8, 1); m = 0 - m } 24 let d: *u8 = sys_mmap(24); var k: i64 = 0 25 while m > 0 { d[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 26 let o: *u8 = sys_mmap(24); var w: i64 = 0 27 while w < k { o[w] = d[k-1-w]; w = w + 1 } 28 sys_write(1, o, k) 29 sys_munmap(d, 24); sys_munmap(o, 24) 30 return 0 31} 32 33func main(argc: i64, argv: *i64) -> i64 { 34 if argc < 2 { sp_puts("REFUSED reason=need_jpeg_path\n" as *u8); return 2 } 35 let path: *u8 = argv[1] as *u8 36 let jl: *i64 = sys_mmap(16) as *i64 37 jl[0] = 0 38 let jbuf: *u8 = sys_read_file(path, jl) 39 let jn: i64 = jl[0] 40 if (jbuf as i64) == 0 { sp_puts("REFUSED reason=unreadable\n" as *u8); return 3 } 41 if jn <= 0 { sp_puts("REFUSED reason=empty\n" as *u8); return 3 } 42 43 let res: *NxJpgResult = nx_jpeg_decode(jbuf, jn) 44 let rc: i64 = res.error_code 45 let w: i64 = res.width 46 let h: i64 = res.height 47 let rgb: *u8 = res.rgb 48 sp_puts("bytes=" as *u8); sp_putn(jn) 49 sp_puts(" rc=" as *u8); sp_putn(rc) 50 sp_puts(" w=" as *u8); sp_putn(w) 51 sp_puts(" h=" as *u8); sp_putn(h) 52 sp_puts(" rgb_size=" as *u8); sp_putn(res.rgb_size) 53 if rc != 0 { sp_puts(" DECODE_FAILED\n" as *u8); return 4 } 54 if w <= 0 { sp_puts(" NO_PIXELS\n" as *u8); return 4 } 55 if (rgb as i64) == 0 { sp_puts(" NULL_RGB\n" as *u8); return 4 } 56 57 // mean tone over a coarse grid, plus the R:G ratio the Beer-Lambert model reasons in 58 var sr: i64 = 0 59 var sg: i64 = 0 60 var sb: i64 = 0 61 var n: i64 = 0 62 var y: i64 = 0 63 while y < h { 64 var x: i64 = 0 65 while x < w { 66 let o: i64 = (y * w + x) * 3 67 let r: i64 = rgb[o] as i64 68 let g: i64 = rgb[o+1] as i64 69 let b: i64 = rgb[o+2] as i64 70 // ignore the pure-black gutters an atlas leaves between UV islands 71 if r + g + b > 30 { sr = sr + r; sg = sg + g; sb = sb + b; n = n + 1 } 72 x = x + 16 73 } 74 y = y + 16 75 } 76 if n <= 0 { sp_puts(" ALL_BACKGROUND\n" as *u8); return 5 } 77 let mr: i64 = sr / n 78 let mg: i64 = sg / n 79 let mb: i64 = sb / n 80 sp_puts(" samples=" as *u8); sp_putn(n) 81 sp_puts(" mean_r=" as *u8); sp_putn(mr) 82 sp_puts(" mean_g=" as *u8); sp_putn(mg) 83 sp_puts(" mean_b=" as *u8); sp_putn(mb) 84 var rg: i64 = 0 85 if mg > 0 { rg = (mr * 1000) / mg } 86 sp_puts(" rg_permil=" as *u8); sp_putn(rg) 87 sp_puts("\n" as *u8) 88 return 0 89}