nx_img_bytes_to_rgb.nx source
↩ module page · 226 lines · 9633 B
1// nx_img_bytes_to_rgb.nx -- decode an IN-MEMORY encoded image buffer -> interleaved 8-bit RGB (w*h*3,
2// row-major). THE single decode chokepoint: the bytes-in core shared by nx_img_to_rgb (path variant),
3// the Windows GUI (read_local + this), the reader/tiler, and -- via the nx_image_gray projection --
4// the whole reverse-image search plane. FORMAT ROSTER (2026-08-05, each gate-proven): JPEG (baseline +
5// progressive + DRI/RST restart markers) · GIF87a/89a (palette colour, de-interlaced, first frame) ·
6// WebP VP8L lossless (lossy VP8 refuses -- C3 pending) · BMP (BI_RGB 1/4/8/24/32 + RLE8, both row
7// orders) · TIFF (baseline strips, none+PackBits, gray/RGB/palette, LE+BE) · PNG (all colour types
8// incl palette, sub-byte depths, Adam7 interlace; 16-bit high byte) . TGA (types 1/2/3 + RLE 9/10/11,
9// origin bit honoured) . PCX (8-bit palette, 24-bit planar, 1-bit) . ICO/CUR (largest frame; DIB or
10// PNG body) . PNM P1-P6. Returns the rgb buffer + sets
11// out_wh[0]=w, out_wh[1]=h; returns 0 on failure/unsupported -- NEVER a wrong image. license_tier: ORIGINAL
12import "nx_syscalls.nx"
13import "nx_png_decoder.nx"
14import "nx_jpeg_ascii.nx"
15import "nx_gif_decode.nx"
16import "nx_webp.nx" // webp_decode: VP8L LOSSLESS only (lossy VP8 returns 0)
17import "nx_bmp_decode.nx" // bmp_decode_rgb: BI_RGB 1/4/8/24/32 + RLE8 (2026-08-05)
18import "nx_tiff_decode.nx" // tiff_decode_rgb: baseline strips, none+PackBits (2026-08-05)
19import "nx_tga_decode.nx" // tga_decode_rgb: types 1/2/3 + RLE 9/10/11 (2026-08-05)
20import "nx_pcx_decode.nx" // pcx_decode_rgb: 8-bit palette, 24-bit planar, 1-bit (2026-08-05)
21import "nx_ico_decode.nx" // ico_decode_rgb: largest frame, DIB or PNG body (2026-08-05)
22import "nx_pnm_decode.nx" // pnm_decode_rgb: P1-P6 netpbm (2026-08-05)
23
24// 4-byte tag compare, kept local so the chokepoint does not pull in the 52KB sniffer module.
25func ib_fourcc(b: *u8, off: i64, a: i64, c: i64, d: i64, e: i64) -> i64 {
26 if (b[off] as i64) != a { return 0 }
27 if (b[off+1] as i64) != c { return 0 }
28 if (b[off+2] as i64) != d { return 0 }
29 if (b[off+3] as i64) != e { return 0 }
30 return 1
31}
32
33func nx_img_bytes_to_rgb(raw: *u8, rawlen: i64, out_wh: *i64) -> *u8 {
34 if raw==(0 as *u8) { return 0 as *u8 }
35 // JPEG (magic FF D8 FF) -> sovereign baseline decoder -> packed w*h*3 RGB (YCbCr->RGB inside).
36 if rawlen>=3 { if raw[0]==(255 as u8) { if raw[1]==(216 as u8) { if raw[2]==(255 as u8) {
37 let jr: *i64=sys_mmap(8) as *i64
38 let jw: *i64=sys_mmap(8) as *i64
39 let jh: *i64=sys_mmap(8) as *i64
40 if nx_jpeg_decode_rgb(raw, rawlen, jr, jw, jh)!=0 { return 0 as *u8 }
41 out_wh[0]=jw[0]; out_wh[1]=jh[0]
42 return jr[0] as *u8
43 } } } }
44 // GIF87a/89a (magic "GIF8") -> sovereign LZW decoder, palette COLOR + de-interlaced
45 // (gif_decode_rgb). The old path collapsed to grayscale (R=G=B expand) and left
46 // interlaced rows in pass order -- color and row order both survive now.
47 if rawlen>=6 { if raw[0]==(71 as u8) { if raw[1]==(73 as u8) { if raw[2]==(70 as u8) { if raw[3]==(56 as u8) {
48 let gwh: *i64=sys_mmap(32) as *i64
49 gwh[0]=0
50 gwh[1]=0
51 let grgb: *u8=gif_decode_rgb(raw, rawlen, gwh)
52 if grgb==(0 as *u8) { return 0 as *u8 }
53 if gwh[0]<=0 { return 0 as *u8 }
54 if gwh[1]<=0 { return 0 as *u8 }
55 out_wh[0]=gwh[0]; out_wh[1]=gwh[1]
56 return grgb
57 } } } } }
58
59 // WebP: RIFF....WEBP. webp_decode handles the VP8L LOSSLESS variant and returns ARGB as one i64
60 // per pixel; lossy VP8 returns 0 and we fall through to an honest failure rather than a wrong image.
61 // The decoder and its gates existed all along with no image-path caller -- this is the wiring.
62 var iswebp: i64 = 0
63 if rawlen >= 16 { if ib_fourcc(raw,0,82,73,70,70) == 1 { if ib_fourcc(raw,8,87,69,66,80) == 1 { iswebp = 1 } } }
64 if iswebp == 1 {
65 let wwh: *i64 = sys_mmap(32) as *i64
66 wwh[0]=0
67 wwh[1]=0
68 let argb: *i64 = webp_decode(raw, rawlen, wwh)
69 if argb == (0 as *i64) { return 0 as *u8 }
70 let ww: i64 = wwh[0]
71 let wh2: i64 = wwh[1]
72 if ww <= 0 { return 0 as *u8 }
73 if wh2 <= 0 { return 0 as *u8 }
74 let wrgb: *u8 = sys_mmap(ww*wh2*3+16)
75 var wi: i64 = 0
76 while wi < ww*wh2 {
77 let px: i64 = argb[wi]
78 wrgb[wi*3] = ((px>>16)&255) as u8
79 wrgb[wi*3+1] = ((px>>8)&255) as u8
80 wrgb[wi*3+2] = (px&255) as u8
81 wi = wi + 1
82 }
83 out_wh[0]=ww; out_wh[1]=wh2
84 return wrgb
85 }
86
87 // BMP ('BM'): the 1990s Windows bitmap -- reader added 2026-08-05 (the tree
88 // had only the writer). Refusals inside the decoder return 0 -> honest fail.
89 if rawlen>=54 { if raw[0]==(66 as u8) { if raw[1]==(77 as u8) {
90 let bwh: *i64=sys_mmap(32) as *i64
91 bwh[0]=0
92 bwh[1]=0
93 let brgb: *u8=bmp_decode_rgb(raw, rawlen, bwh)
94 if brgb==(0 as *u8) { return 0 as *u8 }
95 if bwh[0]<=0 { return 0 as *u8 }
96 if bwh[1]<=0 { return 0 as *u8 }
97 out_wh[0]=bwh[0]; out_wh[1]=bwh[1]
98 return brgb
99 } } }
100 // TIFF (II*\0 / MM\0*): baseline reader added 2026-08-05.
101 var istiff: i64 = 0
102 if rawlen>=8 {
103 if raw[0]==(73 as u8) { if raw[1]==(73 as u8) { if raw[2]==(42 as u8) { if raw[3]==(0 as u8) { istiff=1 } } } }
104 if raw[0]==(77 as u8) { if raw[1]==(77 as u8) { if raw[2]==(0 as u8) { if raw[3]==(42 as u8) { istiff=1 } } } }
105 }
106 if istiff==1 {
107 let twh: *i64=sys_mmap(32) as *i64
108 twh[0]=0
109 twh[1]=0
110 let trgb: *u8=tiff_decode_rgb(raw, rawlen, twh)
111 if trgb==(0 as *u8) { return 0 as *u8 }
112 if twh[0]<=0 { return 0 as *u8 }
113 if twh[1]<=0 { return 0 as *u8 }
114 out_wh[0]=twh[0]; out_wh[1]=twh[1]
115 return trgb
116 }
117 // ---- 1990s roster (added 2026-08-05). Each sniff is a MAGIC test, never an
118 // extension test: the chokepoint receives bytes, not filenames. ICO/PNM/PCX
119 // carry distinctive magic; TGA has NONE, so it is deliberately LAST and is
120 // accepted only when its header fields are self-consistent.
121 // ICO/CUR: 00 00 <01|02> 00 <count>
122 if rawlen>=22 { if raw[0]==(0 as u8) { if raw[1]==(0 as u8) { if raw[3]==(0 as u8) {
123 var icok: i64 = 0
124 if raw[2]==(1 as u8) { icok = 1 }
125 if raw[2]==(2 as u8) { icok = 1 }
126 if icok == 1 {
127 let iwh: *i64=sys_mmap(32) as *i64
128 iwh[0]=0
129 iwh[1]=0
130 let irgb: *u8=ico_decode_rgb(raw, rawlen, iwh)
131 if irgb!=(0 as *u8) { if iwh[0]>0 { if iwh[1]>0 {
132 out_wh[0]=iwh[0]; out_wh[1]=iwh[1]
133 return irgb
134 } } }
135 }
136 } } } }
137 // PCX: manufacturer 0x0A, encoding 0x01 at offset 2
138 if rawlen>=129 { if raw[0]==(10 as u8) { if raw[2]==(1 as u8) {
139 let cwh: *i64=sys_mmap(32) as *i64
140 cwh[0]=0
141 cwh[1]=0
142 let crgb: *u8=pcx_decode_rgb(raw, rawlen, cwh)
143 if crgb!=(0 as *u8) { if cwh[0]>0 { if cwh[1]>0 {
144 out_wh[0]=cwh[0]; out_wh[1]=cwh[1]
145 return crgb
146 } } }
147 } } }
148 // PNM: 'P' followed by '1'..'6'
149 if rawlen>=8 { if raw[0]==(80 as u8) { if raw[1]>=(49 as u8) { if raw[1]<=(54 as u8) {
150 let nwh: *i64=sys_mmap(32) as *i64
151 nwh[0]=0
152 nwh[1]=0
153 let nrgb: *u8=pnm_decode_rgb(raw, rawlen, nwh)
154 if nrgb!=(0 as *u8) { if nwh[0]>0 { if nwh[1]>0 {
155 out_wh[0]=nwh[0]; out_wh[1]=nwh[1]
156 return nrgb
157 } } }
158 } } } }
159 // TGA is MAGICLESS -- it must be tried last, and only when the header is
160 // self-consistent (known image type + a colour-map byte that is 0 or 1).
161 if rawlen>=18 {
162 let tt: i64 = (raw[2] as i64) & 255
163 let tc: i64 = (raw[1] as i64) & 255
164 var tok: i64 = 0
165 if tc <= 1 {
166 if tt==1 { tok=1 }
167 if tt==2 { tok=1 }
168 if tt==3 { tok=1 }
169 if tt==9 { tok=1 }
170 if tt==10 { tok=1 }
171 if tt==11 { tok=1 }
172 }
173 if tok == 1 {
174 let twh2: *i64=sys_mmap(32) as *i64
175 twh2[0]=0
176 twh2[1]=0
177 let trgb2: *u8=tga_decode_rgb(raw, rawlen, twh2)
178 if trgb2!=(0 as *u8) { if twh2[0]>0 { if twh2[1]>0 {
179 out_wh[0]=twh2[0]; out_wh[1]=twh2[1]
180 return trgb2
181 } } }
182 }
183 }
184 let res: *NxPngResult=nx_png_decode(raw, rawlen)
185 if res==(0 as *NxPngResult) { return 0 as *u8 }
186 if res.error_code!=0 { return 0 as *u8 }
187 if res.header==(0 as *NxPngHeader) { return 0 as *u8 }
188 let w: i64=res.header.width
189 let h: i64=res.header.height
190 if w<=0 { return 0 as *u8 }
191 if h<=0 { return 0 as *u8 }
192 let ch: i64=res.n_channels
193 let bpp: i64=res.bytes_per_pix
194 var bps: i64=1
195 if ch>0 { bps=bpp/ch }
196 if bps<1 { bps=1 }
197 let px: *u8=res.pixels
198 let rgb: *u8=sys_mmap(w*h*3)
199 var y: i64=0
200 while y<h {
201 var x: i64=0
202 while x<w {
203 let base: i64=(y*w+x)*bpp
204 var r: i64=0
205 var g: i64=0
206 var b: i64=0
207 if ch>=3 {
208 r=px[base] as i64
209 g=px[base+bps] as i64
210 b=px[base+2*bps] as i64
211 } else {
212 r=px[base] as i64
213 g=r
214 b=r
215 }
216 let o: i64=(y*w+x)*3
217 rgb[o]=r as u8
218 rgb[o+1]=g as u8
219 rgb[o+2]=b as u8
220 x=x+1
221 }
222 y=y+1
223 }
224 out_wh[0]=w; out_wh[1]=h
225 return rgb
226}