nx_png_real_smoke.nx source
↩ module page · 235 lines · 8842 B
1// nx_png_real_smoke.nx -- end-to-end PNG decode verification.
2//
3// CAPABILITY_COMPLETENESS: FULL
4//
5// Constructs a valid 1x1 RGB PNG byte-for-byte at runtime
6// (computing chunk CRC32s and zlib Adler-32 via the shipped
7// substrate primitives), then decodes it via nx_png_decode + the
8// full v0 grader pipeline, asserting:
9// - decoder produces (1, 1) RGB pixel matching the source
10// - grader returns OK + correctly-classed verdict
11//
12// This is the smoke the user explicitly asked for: the bridge
13// from "compile-clean" to "actually works against a real PNG
14// file." No external file needed; the smoke is fully
15// self-contained.
16//
17// genealogy_id: substrate_png_e2e_verification_2026_05_16
18// lineage_id: nx_png_real_smoke_v1
19
20// nx_safety_envelope:
21// intended_use: AUTO_APPLIED -- primitive-specific tuning queued
22// sil_target: SIL1
23// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail]
24// verdict: NOT_YET_EVALUATED
25
26import "nx_syscalls.nx"
27import "nx_runtime.nx"
28import "nx_tier.nx"
29import "nx_crc32.nx"
30import "nx_adler32.nx"
31import "nx_png_decoder.nx"
32import "nx_image_grade_v0.nx"
33
34// ===== big-endian 4-byte writer ==================================
35
36func _png_smoke_write_u32_be(buf: *u8, off: nx_int, value: nx_int) -> nx_int {
37 buf[off] = ((value >> 24) & 255) as u8
38 buf[off + 1] = ((value >> 16) & 255) as u8
39 buf[off + 2] = ((value >> 8) & 255) as u8
40 buf[off + 3] = (value & 255) as u8
41 return 0
42}
43
44// ===== build a 1x1 RGB PNG with caller-chosen pixel color ========
45//
46// Returns (buf, total_size) by writing to out_buf and returning size.
47
48func _png_smoke_build(r_val: nx_int, g_val: nx_int, b_val: nx_int,
49 out_buf: *u8) -> nx_int {
50 // Signature (8 bytes).
51 out_buf[0] = 137 as u8
52 out_buf[1] = 80 as u8
53 out_buf[2] = 78 as u8
54 out_buf[3] = 71 as u8
55 out_buf[4] = 13 as u8
56 out_buf[5] = 10 as u8
57 out_buf[6] = 26 as u8
58 out_buf[7] = 10 as u8
59 var pos: nx_int = 8
60
61 // ---- IHDR chunk ----
62 // length = 13
63 _png_smoke_write_u32_be(out_buf, pos, 13)
64 pos = pos + 4
65 let ihdr_type_off: nx_int = pos
66 out_buf[pos] = 73 as u8 // 'I'
67 out_buf[pos + 1] = 72 as u8 // 'H'
68 out_buf[pos + 2] = 68 as u8 // 'D'
69 out_buf[pos + 3] = 82 as u8 // 'R'
70 pos = pos + 4
71 // Data: width(4)=1, height(4)=1, bd=8, ct=2 (RGB), cm=0, fm=0, il=0
72 _png_smoke_write_u32_be(out_buf, pos, 1)
73 pos = pos + 4
74 _png_smoke_write_u32_be(out_buf, pos, 1)
75 pos = pos + 4
76 out_buf[pos] = 8 as u8
77 out_buf[pos + 1] = 2 as u8
78 out_buf[pos + 2] = 0 as u8
79 out_buf[pos + 3] = 0 as u8
80 out_buf[pos + 4] = 0 as u8
81 pos = pos + 5
82 // CRC over type+data = 4+13 bytes
83 let ihdr_crc_buf: *u8 = (out_buf as nx_int + ihdr_type_off) as *u8
84 let ihdr_crc: nx_int = nx_crc32(ihdr_crc_buf, 17)
85 _png_smoke_write_u32_be(out_buf, pos, ihdr_crc)
86 pos = pos + 4
87
88 // ---- IDAT chunk ----
89 //
90 // zlib-wrapped DEFLATE stored block carrying [filter_byte, R, G, B]:
91 // CMF/FLG = 78 01 (2 bytes)
92 // DEFLATE stored header: BFINAL=1 BTYPE=00 (1 byte = 0x01),
93 // LEN_lo LEN_hi (LEN=4), NLEN_lo NLEN_hi (~LEN = 0xFFFB)
94 // -> 01 04 00 FB FF
95 // payload: 00 R G B
96 // Adler-32 BE: 4 bytes
97 // Total zlib stream = 2 + 5 + 4 + 4 = 15 bytes.
98 let zlib_len: nx_int = 15
99 _png_smoke_write_u32_be(out_buf, pos, zlib_len)
100 pos = pos + 4
101 let idat_type_off: nx_int = pos
102 out_buf[pos] = 73 as u8 // 'I'
103 out_buf[pos + 1] = 68 as u8 // 'D'
104 out_buf[pos + 2] = 65 as u8 // 'A'
105 out_buf[pos + 3] = 84 as u8 // 'T'
106 pos = pos + 4
107 // CMF/FLG
108 out_buf[pos] = 0x78 as u8
109 out_buf[pos + 1] = 0x01 as u8
110 pos = pos + 2
111 // DEFLATE stored header
112 out_buf[pos] = 0x01 as u8
113 out_buf[pos + 1] = 4 as u8
114 out_buf[pos + 2] = 0 as u8
115 out_buf[pos + 3] = 0xFB as u8
116 out_buf[pos + 4] = 0xFF as u8
117 pos = pos + 5
118 // Payload: filter byte 0 then RGB
119 let payload_off: nx_int = pos
120 out_buf[pos] = 0 as u8
121 out_buf[pos + 1] = r_val as u8
122 out_buf[pos + 2] = g_val as u8
123 out_buf[pos + 3] = b_val as u8
124 pos = pos + 4
125 // Compute Adler-32 over the 4-byte payload.
126 let payload_ptr: *u8 = (out_buf as nx_int + payload_off) as *u8
127 let adler: nx_int = adler32(payload_ptr, 4)
128 _png_smoke_write_u32_be(out_buf, pos, adler)
129 pos = pos + 4
130 // CRC over IDAT type+data = 4 + 15 = 19 bytes
131 let idat_crc_buf: *u8 = (out_buf as nx_int + idat_type_off) as *u8
132 let idat_crc: nx_int = nx_crc32(idat_crc_buf, 19)
133 _png_smoke_write_u32_be(out_buf, pos, idat_crc)
134 pos = pos + 4
135
136 // ---- IEND chunk ----
137 _png_smoke_write_u32_be(out_buf, pos, 0)
138 pos = pos + 4
139 let iend_type_off: nx_int = pos
140 out_buf[pos] = 73 as u8 // 'I'
141 out_buf[pos + 1] = 69 as u8 // 'E'
142 out_buf[pos + 2] = 78 as u8 // 'N'
143 out_buf[pos + 3] = 68 as u8 // 'D'
144 pos = pos + 4
145 let iend_crc_buf: *u8 = (out_buf as nx_int + iend_type_off) as *u8
146 let iend_crc: nx_int = nx_crc32(iend_crc_buf, 4)
147 _png_smoke_write_u32_be(out_buf, pos, iend_crc)
148 pos = pos + 4
149
150 return pos
151}
152
153// ===== self-test ==================================================
154
155func main() -> nx_int {
156 let png_buf: *u8 = (sys_mmap(128)) as *u8
157
158 // ---- pure red 1x1 PNG: decode through nx_png_decode ----
159 let red_size: nx_int = _png_smoke_build(255, 0, 0, png_buf)
160 if red_size < 50 { return 1 }
161 if red_size > 100 { return 2 }
162 let r_red: *NxPngResult = nx_png_decode(png_buf, red_size)
163 if r_red == (0 as *NxPngResult) { return 3 }
164 if r_red.error_code != NX_PNG_OK { return 4 }
165 if r_red.header == (0 as *NxPngHeader) { return 5 }
166 if r_red.header.width != 1 { return 6 }
167 if r_red.header.height != 1 { return 7 }
168 if r_red.n_channels != 3 { return 8 }
169 if r_red.bytes_per_pix != 3 { return 9 }
170 if r_red.pixels_size != 3 { return 10 }
171 let red_r: nx_int = (r_red.pixels[0] as nx_int) & 255
172 let red_g: nx_int = (r_red.pixels[1] as nx_int) & 255
173 let red_b: nx_int = (r_red.pixels[2] as nx_int) & 255
174 if red_r != 255 { return 11 }
175 if red_g != 0 { return 12 }
176 if red_b != 0 { return 13 }
177
178 // ---- pure green 1x1 PNG ----
179 let png_buf2: *u8 = (sys_mmap(128)) as *u8
180 let green_size: nx_int = _png_smoke_build(0, 255, 0, png_buf2)
181 let r_green: *NxPngResult = nx_png_decode(png_buf2, green_size)
182 if r_green.error_code != NX_PNG_OK { return 20 }
183 let g_r: nx_int = (r_green.pixels[0] as nx_int) & 255
184 let g_g: nx_int = (r_green.pixels[1] as nx_int) & 255
185 let g_b: nx_int = (r_green.pixels[2] as nx_int) & 255
186 if g_r != 0 { return 21 }
187 if g_g != 255 { return 22 }
188 if g_b != 0 { return 23 }
189
190 // ---- mid skin tone 1x1 PNG via the full v0 grader pipeline ----
191 //
192 // (200, 170, 150) is a warm-light skin tone. Pipeline should:
193 // - decode PNG -> 1x1 RGB pixel
194 // - scan_means -> mean R=200, G=170, B=150
195 // - lab_from_rgb -> Lab
196 // - skin_tone_ita -> light/intermediate band
197 // - undertone -> warm
198 let png_buf3: *u8 = (sys_mmap(128)) as *u8
199 let skin_size: nx_int = _png_smoke_build(200, 170, 150, png_buf3)
200 let rep: *NxIgv0Report = nx_image_grade_v0(png_buf3, skin_size)
201 if rep == (0 as *NxIgv0Report) { return 30 }
202 if rep.error_code != NX_IGV0_OK { return 31 }
203 if rep.source_format != NX_IGV0_FORMAT_PNG { return 32 }
204 if rep.width != 1 { return 33 }
205 if rep.height != 1 { return 34 }
206 if rep.mean_r != 200 { return 35 }
207 if rep.mean_g != 170 { return 36 }
208 if rep.mean_b != 150 { return 37 }
209
210 // Verify the per-axis verdicts are populated.
211 if rep.n_axes < 12 { return 40 }
212
213 // Skin-tone-ITA axis (index 0) should be OK.
214 let ax0: *NxIgv0AxisVerdict =
215 (rep.axes as *u8 + (0 as nx_size) * NX_IGV0_AXIS_VERDICT_BYTES) as *NxIgv0AxisVerdict
216 if ax0.status != NX_IGV0_AXIS_OK { return 41 }
217 // Band should be light or intermediate (1 or 2) for (200,170,150).
218 if ax0.verdict_value > 3 { return 42 }
219
220 // Undertone axis (index 1) should be OK + WARM (3) or NEUTRAL (2).
221 let ax1: *NxIgv0AxisVerdict =
222 (rep.axes as *u8 + (1 as nx_size) * NX_IGV0_AXIS_VERDICT_BYTES) as *NxIgv0AxisVerdict
223 if ax1.status != NX_IGV0_AXIS_OK { return 50 }
224 if ax1.verdict_value != 3 {
225 if ax1.verdict_value != 2 { return 51 }
226 }
227
228 // A PENDING axis (vein-signal at index 5) should be PENDING_SEG.
229 let ax_vein: *NxIgv0AxisVerdict =
230 (rep.axes as *u8 + (5 as nx_size) * NX_IGV0_AXIS_VERDICT_BYTES) as *NxIgv0AxisVerdict
231 if ax_vein.status != NX_IGV0_AXIS_PENDING_SEGMENTATION { return 60 }
232 if ax_vein.phrase_len < 10 { return 61 }
233
234 return 0
235}