nx_img_dims.nx source
↩ module page · 85 lines · 5268 B
1// nx_img_dims.nx -- read an image's pixel WIDTH/HEIGHT straight from its header bytes (no decode). Powers the
2// media importer's junk filter: reject nav icons / spacer gifs / 1x1 tracking pixels by their REAL dimensions
3// (URL heuristics lie; header bytes don't). Handles PNG (IHDR), GIF (screen descriptor), JPEG (SOF marker walk),
4// and WEBP (VP8X extended / VP8 lossy / VP8L lossless). Unknown/short -> returns 0 (caller decides: we KEEP on
5// unknown so a novel format is never silently dropped). Integer-only, deterministic. license_tier: INDEPENDENT_REDERIVE
6import "nx_syscalls.nx"
7
8func _be16(b: *u8, o: i64) -> i64 { return ((b[o] as i64 & 0xff) << 8) | (b[o+1] as i64 & 0xff) }
9func _be32(b: *u8, o: i64) -> i64 { return ((b[o] as i64 & 0xff) << 24) | ((b[o+1] as i64 & 0xff) << 16) | ((b[o+2] as i64 & 0xff) << 8) | (b[o+3] as i64 & 0xff) }
10func _le16(b: *u8, o: i64) -> i64 { return (b[o] as i64 & 0xff) | ((b[o+1] as i64 & 0xff) << 8) }
11func _le24(b: *u8, o: i64) -> i64 { return (b[o] as i64 & 0xff) | ((b[o+1] as i64 & 0xff) << 8) | ((b[o+2] as i64 & 0xff) << 16) }
12
13// width into wh[0], height into wh[1]. returns 1 if parsed, 0 if unknown.
14func nx_img_dims(buf: *u8, n: i64, wh: *i64) -> i64 {
15 wh[0] = 0; wh[1] = 0
16 if n < 16 { return 0 }
17 let b0: i64 = buf[0] as i64 & 0xff
18 // ---- PNG: 89 50 4E 47 0D 0A 1A 0A, then len(4) "IHDR"(4) W(4) H(4) => W@16 H@20 ----
19 if b0 == 0x89 { if (buf[1] as i64 & 0xff)==0x50 { if (buf[2] as i64 & 0xff)==0x4e { if (buf[3] as i64 & 0xff)==0x47 {
20 wh[0] = _be32(buf, 16); wh[1] = _be32(buf, 20); return 1 } } } }
21 // ---- GIF: "GIF8" then W(2 LE)@6 H(2 LE)@8 ----
22 if b0 == 0x47 { if (buf[1] as i64 & 0xff)==0x49 { if (buf[2] as i64 & 0xff)==0x46 { if (buf[3] as i64 & 0xff)==0x38 {
23 wh[0] = _le16(buf, 6); wh[1] = _le16(buf, 8); return 1 } } } }
24 // ---- WEBP: "RIFF"....."WEBP" then a format chunk ----
25 if b0 == 0x52 { if (buf[1] as i64 & 0xff)==0x49 { if (buf[2] as i64 & 0xff)==0x46 { if (buf[3] as i64 & 0xff)==0x46 {
26 if n >= 30 { if (buf[8] as i64 & 0xff)==0x57 { if (buf[9] as i64 & 0xff)==0x45 { // "WE"
27 let f3: i64 = buf[15] as i64 & 0xff // 4th char of the fourcc at 12..16
28 // VP8X (extended): "VP8X" -> canvas W-1 @24 (24 LE), H-1 @27 (24 LE)
29 if f3 == 0x58 { wh[0] = _le24(buf, 24) + 1; wh[1] = _le24(buf, 27) + 1; return 1 }
30 // VP8 (lossy) : "VP8 " -> after RIFF(4)+size(4)+WEBP(4)+VP8 (4)+chunksz(4)+frametag(3)+startcode(3) => W@26 H@28 (14 bits)
31 if f3 == 0x20 { wh[0] = _le16(buf, 26) & 0x3fff; wh[1] = _le16(buf, 28) & 0x3fff; return 1 }
32 // VP8L (lossless): "VP8L" -> sig 0x2f @20, then 14b W-1 then 14b H-1
33 if f3 == 0x4c { if (buf[20] as i64 & 0xff)==0x2f {
34 let bits0: i64 = _le32b(buf, 21)
35 wh[0] = (bits0 & 0x3fff) + 1
36 wh[1] = ((bits0 >> 14) & 0x3fff) + 1
37 return 1 } }
38 } } }
39 } } } }
40 // ---- JPEG: FF D8, walk markers to a SOF ----
41 if b0 == 0xff { if (buf[1] as i64 & 0xff)==0xd8 {
42 var p: i64 = 2
43 while p + 9 < n {
44 if (buf[p] as i64 & 0xff) != 0xff { p = p + 1 } else {
45 let m: i64 = buf[p+1] as i64 & 0xff
46 if m == 0xd8 { p = p + 2 } else {
47 if m == 0xd9 { return 0 }
48 if m == 0x01 { p = p + 2 } else {
49 if m >= 0xd0 { if m <= 0xd7 { p = p + 2 } else {
50 // a segment with a length
51 var is_sof: i64 = 0
52 if m >= 0xc0 { if m <= 0xcf { if m != 0xc4 { if m != 0xc8 { if m != 0xcc { is_sof = 1 } } } } }
53 if is_sof == 1 { wh[1] = _be16(buf, p+5); wh[0] = _be16(buf, p+7); return 1 }
54 let seglen: i64 = _be16(buf, p+2)
55 if seglen < 2 { return 0 }
56 p = p + 2 + seglen
57 } } else {
58 var is_sof2: i64 = 0
59 if m >= 0xc0 { if m <= 0xcf { if m != 0xc4 { if m != 0xc8 { if m != 0xcc { is_sof2 = 1 } } } } }
60 if is_sof2 == 1 { wh[1] = _be16(buf, p+5); wh[0] = _be16(buf, p+7); return 1 }
61 let seglen2: i64 = _be16(buf, p+2)
62 if seglen2 < 2 { return 0 }
63 p = p + 2 + seglen2
64 }
65 }
66 }
67 }
68 }
69 return 0
70 } }
71 return 0
72}
73
74// little helper: 32-bit LE read (for VP8L bit unpack)
75func _le32b(b: *u8, o: i64) -> i64 { return (b[o] as i64 & 0xff) | ((b[o+1] as i64 & 0xff) << 8) | ((b[o+2] as i64 & 0xff) << 16) | ((b[o+3] as i64 & 0xff) << 24) }
76
77// convenience: the LONGER edge in pixels, or 0x7fffffff if unknown (so an unknown format is KEPT, not dropped).
78func nx_img_long_edge(buf: *u8, n: i64) -> i64 {
79 let wh: *i64 = sys_mmap(16) as *i64
80 if nx_img_dims(buf, n, wh) == 0 { return 0x7fffffff }
81 var mx: i64 = wh[0]
82 if wh[1] > mx { mx = wh[1] }
83 if mx <= 0 { return 0x7fffffff } // parsed as 0 (corrupt) -> don't use dims to drop
84 return mx
85}