nx_color_meter.nx source
↩ module page · 74 lines · 3337 B
1const NX_MAGIC_1000000: i64 = 1000000
2const NX_MAGIC_50000: i64 = 50000
3const NX_MAGIC_10000: i64 = 10000
4// nx_color_meter.nx -- per-color (CMYK) toner coverage measurement = the COLOR analog of the black-toner
5// real-coverage exceed. The operator's ask: "color toner monitoring, not just black." We own the bitmap, so
6// we measure REAL per-channel toner consumption with GCR under-color removal (black pulled into the K channel,
7// exactly as a color laser separates), giving honest C/M/Y/K consumption per page -- the per-color truth the
8// printer's lying per-color gauge can't be trusted for (HP "fake empty" = $5M settlement; per the sovereign
9// researcher's Ink/Toner-cartridge sources). PURE integer, NO float, NO syscalls -> never-brick #26.
10// Grounded: ISO/IEC 19798 (color toner page yield) + ISO/IEC 19752 (mono), 5%-coverage standard page.
11// genealogy_id: project-printer-management-ipp-sclass-2026-06-20 ; license_tier: ORIGINAL
12
13const NX_CMY_C: i64 = 0
14const NX_CMY_M: i64 = 1
15const NX_CMY_Y: i64 = 2
16const NX_CMY_K: i64 = 3
17
18// Per-channel coverage in ppm (1e6 = 100% of that colorant over the page) from an RGB bitmap (3 bytes/pixel).
19// out4 receives [C, M, Y, K]. CMY demand = 255 - channel; GCR: K = min(C,M,Y) -> black toner, C/M/Y reduced
20// by K (so pure gray/black consumes K toner, not a muddy CMY mix -- how a real color laser prints).
21func nx_color_coverage(rgb: *u8, npix: i64, out4: *i64) -> i64 {
22 if npix <= 0 { out4[0] = 0; out4[1] = 0; out4[2] = 0; out4[3] = 0; return 0 }
23 var sc: i64 = 0
24 var sm: i64 = 0
25 var sy: i64 = 0
26 var sk: i64 = 0
27 var i: i64 = 0
28 while i < npix {
29 let base: i64 = i * 3
30 let r: i64 = rgb[base] as i64
31 let g: i64 = rgb[base + 1] as i64
32 let bb: i64 = rgb[base + 2] as i64
33 let c: i64 = 255 - r
34 let m: i64 = 255 - g
35 let y: i64 = 255 - bb
36 var k: i64 = c
37 if m < k { k = m }
38 if y < k { k = y }
39 sc = sc + (c - k)
40 sm = sm + (m - k)
41 sy = sy + (y - k)
42 sk = sk + k
43 i = i + 1
44 }
45 let denom: i64 = 255 * npix
46 out4[0] = (sc * NX_MAGIC_1000000) / denom
47 out4[1] = (sm * NX_MAGIC_1000000) / denom
48 out4[2] = (sy * NX_MAGIC_1000000) / denom
49 out4[3] = (sk * NX_MAGIC_1000000) / denom
50 return 0
51}
52
53// Per-color remaining toner in basis points (10000 = 100%) given that color's ISO-19798 page yield and the
54// cumulative coverage consumed (ppm summed over pages). capacity = yield_pages * 50000 ppm (the 5% ISO page).
55// -1 if yield unknown. Mirrors the mono nx_toner_ledger, per channel.
56func nx_color_remaining_bp(yield_pages: i64, consumed_ppm: i64) -> i64 {
57 if yield_pages <= 0 { return 0 - 1 }
58 let cap: i64 = yield_pages * NX_MAGIC_50000
59 if cap <= 0 { return 0 - 1 }
60 if consumed_ppm >= cap { return 0 }
61 return ((cap - consumed_ppm) * NX_MAGIC_10000) / cap
62}
63
64// sealed per-color status from a remaining-bp value: OK / LOW (<10%) / EMPTY (0) / UNKNOWN (-1).
65const NX_CSUP_OK: i64 = 1
66const NX_CSUP_LOW: i64 = 2
67const NX_CSUP_EMPTY: i64 = 3
68const NX_CSUP_UNKNOWN: i64 = 4
69func nx_color_status(remaining_bp: i64) -> i64 {
70 if remaining_bp < 0 { return NX_CSUP_UNKNOWN }
71 if remaining_bp == 0 { return NX_CSUP_EMPTY }
72 if remaining_bp < 1000 { return NX_CSUP_LOW }
73 return NX_CSUP_OK
74}