nx_bmp_decode.nx source
↩ module page · 138 lines · 5528 B
1// nx_bmp_decode.nx -- sovereign BMP READER (the 1990s Windows workhorse; the tree had only the
2// writer nx_bmp.nx). bmp_decode_rgb(raw,n,out_wh) -> w*h*3 packed RGB, TOP-DOWN row order.
3// Covers: BITMAPINFOHEADER (biSize>=40), bit depths 1/4/8 (palette), 24 (BGR), 32 (BGRX);
4// BI_RGB (0) for all depths + BI_RLE8 (1) for 8-bit (runs, absolute mode, EOL/EOD/delta escapes);
5// positive height = bottom-up (flipped here), negative = top-down. REFUSED (0 return, never a
6// wrong image): BITMAPCOREHEADER, BI_RLE4, BI_BITFIELDS, 16-bit. license_tier: ORIGINAL
7import "nx_syscalls.nx"
8const K_MAGIC_2147483648: i64 = 2147483648
9const K_MAGIC_4294967296: i64 = 4294967296
10const K_MAGIC_32767: i64 = 32767
11
12func bd_u16(b: *u8, o: i64) -> i64 { return (b[o] as i64) | ((b[o+1] as i64)<<8) }
13func bd_u32(b: *u8, o: i64) -> i64 { return (b[o] as i64) | ((b[o+1] as i64)<<8) | ((b[o+2] as i64)<<16) | ((b[o+3] as i64)<<24) }
14func bd_s32(b: *u8, o: i64) -> i64 { var v: i64=bd_u32(b,o); if v>=K_MAGIC_2147483648 { v=v-K_MAGIC_4294967296 } return v }
15
16func bmp_decode_rgb(raw: *u8, n: i64, out_wh: *i64) -> *u8 {
17 if n<54 { return 0 as *u8 }
18 if (raw[0] as i64)!=0x42 { return 0 as *u8 }
19 if (raw[1] as i64)!=0x4D { return 0 as *u8 }
20 let data_off: i64 = bd_u32(raw, 10)
21 let hsz: i64 = bd_u32(raw, 14)
22 if hsz<40 { return 0 as *u8 }
23 let w: i64 = bd_s32(raw, 18)
24 var h: i64 = bd_s32(raw, 22)
25 let bits: i64 = bd_u16(raw, 28)
26 let comp: i64 = bd_u32(raw, 30)
27 var clr_used: i64 = bd_u32(raw, 46)
28 if w<=0 { return 0 as *u8 }
29 var topdown: i64 = 0
30 if h<0 { topdown=1; h=0-h }
31 if h<=0 { return 0 as *u8 }
32 if w>K_MAGIC_32767 { return 0 as *u8 }
33 if h>K_MAGIC_32767 { return 0 as *u8 }
34 var depth_ok: i64 = 0
35 if bits==1 { depth_ok=1 }
36 if bits==4 { depth_ok=1 }
37 if bits==8 { depth_ok=1 }
38 if bits==24 { depth_ok=1 }
39 if bits==32 { depth_ok=1 }
40 if depth_ok==0 { return 0 as *u8 }
41 if comp!=0 { if comp!=1 { return 0 as *u8 } }
42 if comp==1 { if bits!=8 { return 0 as *u8 } }
43 // palette (BGRA quads) sits after the info header
44 let pal_off: i64 = 14 + hsz
45 var pal_n: i64 = 0
46 if bits<=8 {
47 pal_n = clr_used
48 if pal_n==0 { pal_n = 1<<bits }
49 if pal_off + pal_n*4 > n { return 0 as *u8 }
50 }
51 let npix: i64 = w*h
52 let rgb: *u8 = sys_mmap(npix*3+16)
53 if comp==1 {
54 // BI_RLE8: decode into an index plane in BOTTOM-UP coordinate space,
55 // then emit top-down through the palette.
56 let idxp: *u8 = sys_mmap(npix+16)
57 var x: i64=0
58 var y: i64=0
59 var p: i64=data_off
60 var run: i64=1
61 while run==1 {
62 if p+1>=n { run=0 } else {
63 let c0: i64 = raw[p] as i64
64 let c1: i64 = raw[p+1] as i64
65 p=p+2
66 if c0>0 {
67 var k: i64=0
68 while k<c0 { if x<w { if y<h { idxp[y*w+x]=c1 as u8 } } x=x+1; k=k+1 }
69 } else {
70 if c1==0 { x=0; y=y+1 } else {
71 if c1==1 { run=0 } else {
72 if c1==2 { if p+1<n { x=x+(raw[p] as i64); y=y+(raw[p+1] as i64); p=p+2 } else { run=0 } } else {
73 var k2: i64=0
74 while k2<c1 { if p<n { if x<w { if y<h { idxp[y*w+x]=raw[p] } } x=x+1; p=p+1 } k2=k2+1 }
75 if (c1%2)==1 { p=p+1 }
76 } } }
77 }
78 if y>h { run=0 }
79 }
80 }
81 var oy: i64=0
82 while oy<h {
83 var sy: i64=h-1-oy
84 if topdown==1 { sy=oy }
85 var ox: i64=0
86 while ox<w {
87 var ci: i64=idxp[sy*w+ox] as i64
88 if ci>=pal_n { ci=pal_n-1 }
89 let po: i64=pal_off+ci*4
90 rgb[(oy*w+ox)*3] =raw[po+2]
91 rgb[(oy*w+ox)*3+1]=raw[po+1]
92 rgb[(oy*w+ox)*3+2]=raw[po]
93 ox=ox+1
94 }
95 oy=oy+1
96 }
97 out_wh[0]=w; out_wh[1]=h
98 return rgb
99 }
100 // uncompressed paths: row stride padded to 4 bytes
101 let rowbits: i64 = w*bits
102 let stride: i64 = ((rowbits+31)/32)*4
103 if data_off + stride*h > n { return 0 as *u8 }
104 var oy2: i64=0
105 while oy2<h {
106 var sy2: i64=h-1-oy2
107 if topdown==1 { sy2=oy2 }
108 let ro: i64=data_off+sy2*stride
109 var ox2: i64=0
110 while ox2<w {
111 var rv: i64=0
112 var gv: i64=0
113 var bv: i64=0
114 if bits==24 {
115 let po2: i64=ro+ox2*3
116 bv=raw[po2] as i64; gv=raw[po2+1] as i64; rv=raw[po2+2] as i64
117 } else { if bits==32 {
118 let po3: i64=ro+ox2*4
119 bv=raw[po3] as i64; gv=raw[po3+1] as i64; rv=raw[po3+2] as i64
120 } else {
121 var ci2: i64=0
122 if bits==8 { ci2=raw[ro+ox2] as i64 }
123 if bits==4 { let bb4: i64=raw[ro+ox2/2] as i64; if (ox2%2)==0 { ci2=(bb4>>4)&15 } else { ci2=bb4&15 } }
124 if bits==1 { let bb1: i64=raw[ro+ox2/8] as i64; ci2=(bb1>>(7-(ox2%8)))&1 }
125 if ci2>=pal_n { ci2=pal_n-1 }
126 let po4: i64=pal_off+ci2*4
127 bv=raw[po4] as i64; gv=raw[po4+1] as i64; rv=raw[po4+2] as i64
128 } }
129 rgb[(oy2*w+ox2)*3] =rv as u8
130 rgb[(oy2*w+ox2)*3+1]=gv as u8
131 rgb[(oy2*w+ox2)*3+2]=bv as u8
132 ox2=ox2+1
133 }
134 oy2=oy2+1
135 }
136 out_wh[0]=w; out_wh[1]=h
137 return rgb
138}