code wiki / (root) / nx_ico_decode.nx

nx_ico_decode.nx source

↩ module page · 167 lines · 6338 B

1// nx_ico_decode.nx -- Windows ICO/CUR reader. The one legacy format with 2// LIVE web demand: every favicon.ico a crawler meets is this. 3// 4// AN ICO IS A CONTAINER, NOT AN IMAGE. It holds N frames at different sizes, 5// and the entry's width/height byte is 0 for 256 -- a reader that takes the 6// byte literally reports a 0x0 image for the largest frame there is. We pick 7// the LARGEST frame by area (that is what a fingerprint wants) and decode it. 8// 9// A frame is EITHER a PNG (modern, and the only way 256x256 is normally 10// stored) OR a headerless BITMAPINFOHEADER DIB. The DIB's declared height is 11// DOUBLE the real height because an AND (transparency) mask is appended below 12// the XOR (colour) rows; halving it is not a heuristic, it is the format. 13// The mask itself is ignored: a fingerprint wants pixels, not alpha. 14// 15// genealogy_id: msdn_ico_format 16// lineage_id: nx_ico_v1 17// license_tier: ORIGINAL 18 19import "nx_syscalls.nx" 20import "nx_png_decoder.nx" 21 22func ico_b(d: *u8, o: i64) -> i64 { return (d[o] as i64) & 255 } 23func ico_u16(d: *u8, o: i64) -> i64 { return ico_b(d,o) | (ico_b(d,o+1) << 8) } 24func ico_u32(d: *u8, o: i64) -> i64 { 25 return ico_b(d,o) | (ico_b(d,o+1) << 8) | (ico_b(d,o+2) << 16) | (ico_b(d,o+3) << 24) 26} 27 28// a headerless DIB (BITMAPINFOHEADER + optional palette + XOR rows + AND mask) 29func ico_dib_rgb(raw: *u8, n: i64, off: i64, len: i64, out_wh: *i64) -> *u8 { 30 if off + 40 > n { return 0 as *u8 } 31 let hs: i64 = ico_u32(raw, off) 32 if hs < 40 { return 0 as *u8 } 33 let w: i64 = ico_u32(raw, off + 4) 34 let h2: i64 = ico_u32(raw, off + 8) 35 let bits: i64 = ico_u16(raw, off + 14) 36 let comp: i64 = ico_u32(raw, off + 16) 37 if comp != 0 { return 0 as *u8 } // BI_RGB only; no RLE/PNG-in-DIB 38 if w <= 0 { return 0 as *u8 } 39 if h2 <= 0 { return 0 as *u8 } 40 let h: i64 = h2 / 2 // XOR rows only; the AND mask is the rest 41 if h <= 0 { return 0 as *u8 } 42 if bits != 32 { if bits != 24 { if bits != 8 { if bits != 4 { return 0 as *u8 } } } } 43 44 var ncol: i64 = ico_u32(raw, off + 32) 45 if ncol == 0 { if bits <= 8 { ncol = 1 << bits } } 46 let pal: i64 = off + hs 47 var px: i64 = pal 48 if bits <= 8 { px = pal + ncol*4 } 49 // rows are 4-byte aligned, bottom-up 50 let stride: i64 = ((w * bits + 31) / 32) * 4 51 if px + stride*h > n { return 0 as *u8 } 52 if px + stride*h > off + len { return 0 as *u8 } 53 54 let rgb: *u8 = sys_mmap(w * h * 3 + 64) 55 var y: i64 = 0 56 while y < h { 57 let sy: i64 = h - 1 - y // stored bottom-up 58 var x: i64 = 0 59 while x < w { 60 let dof: i64 = (y*w + x) * 3 61 if bits == 32 { 62 let so: i64 = px + sy*stride + x*4 63 rgb[dof] = raw[so+2] 64 rgb[dof+1] = raw[so+1] 65 rgb[dof+2] = raw[so] 66 } else { 67 if bits == 24 { 68 let so: i64 = px + sy*stride + x*3 69 rgb[dof] = raw[so+2] 70 rgb[dof+1] = raw[so+1] 71 rgb[dof+2] = raw[so] 72 } else { 73 var idx: i64 = 0 74 if bits == 8 { 75 idx = ico_b(raw, px + sy*stride + x) 76 } else { 77 let byte: i64 = ico_b(raw, px + sy*stride + (x >> 1)) 78 if (x & 1) == 0 { idx = (byte >> 4) & 15 } else { idx = byte & 15 } 79 } 80 if idx >= ncol { idx = 0 } 81 let eo: i64 = pal + idx*4 // palette entries are BGRA 82 if eo + 3 > n { return 0 as *u8 } 83 rgb[dof] = raw[eo+2] 84 rgb[dof+1] = raw[eo+1] 85 rgb[dof+2] = raw[eo] 86 } } 87 x = x + 1 88 } 89 y = y + 1 90 } 91 out_wh[0] = w 92 out_wh[1] = h 93 return rgb 94} 95 96func ico_decode_rgb(raw: *u8, n: i64, out_wh: *i64) -> *u8 { 97 if n < 22 { return 0 as *u8 } 98 if ico_u16(raw, 0) != 0 { return 0 as *u8 } 99 let kind: i64 = ico_u16(raw, 2) 100 if kind != 1 { if kind != 2 { return 0 as *u8 } } // 1=icon 2=cursor 101 let cnt: i64 = ico_u16(raw, 4) 102 if cnt <= 0 { return 0 as *u8 } 103 if 6 + cnt*16 > n { return 0 as *u8 } 104 105 // pick the largest frame by area; a 0 dimension byte MEANS 256 106 var best: i64 = 0 - 1 107 var best_area: i64 = 0 - 1 108 var i: i64 = 0 109 while i < cnt { 110 let e: i64 = 6 + i*16 111 var ew: i64 = ico_b(raw, e) 112 var eh: i64 = ico_b(raw, e+1) 113 if ew == 0 { ew = 256 } 114 if eh == 0 { eh = 256 } 115 let area: i64 = ew * eh 116 if area > best_area { best_area = area; best = i } 117 i = i + 1 118 } 119 if best < 0 { return 0 as *u8 } 120 let e: i64 = 6 + best*16 121 let flen: i64 = ico_u32(raw, e + 8) 122 let foff: i64 = ico_u32(raw, e + 12) 123 if flen <= 0 { return 0 as *u8 } 124 if foff + flen > n { return 0 as *u8 } 125 126 // a PNG-bodied frame decodes through the existing PNG decoder 127 var ispng: i64 = 0 128 if flen >= 8 { 129 if ico_b(raw, foff) == 137 { if ico_b(raw, foff+1) == 80 { 130 if ico_b(raw, foff+2) == 78 { if ico_b(raw, foff+3) == 71 { ispng = 1 } } } } 131 } 132 if ispng == 1 { 133 let res: *NxPngResult = nx_png_decode(raw + foff, flen) 134 if res == (0 as *NxPngResult) { return 0 as *u8 } 135 if res.error_code != 0 { return 0 as *u8 } 136 if res.header == (0 as *NxPngHeader) { return 0 as *u8 } 137 let pw: i64 = res.header.width 138 let ph: i64 = res.header.height 139 if pw <= 0 { return 0 as *u8 } 140 if ph <= 0 { return 0 as *u8 } 141 let ch: i64 = res.n_channels 142 let bpp: i64 = res.bytes_per_pix 143 var bps: i64 = 1 144 if ch > 0 { bps = bpp / ch } 145 if bps < 1 { bps = 1 } 146 let src: *u8 = res.pixels 147 let prgb: *u8 = sys_mmap(pw * ph * 3 + 64) 148 var p: i64 = 0 149 while p < pw*ph { 150 let b: i64 = p * bpp 151 if ch >= 3 { 152 prgb[p*3] = src[b] 153 prgb[p*3+1] = src[b+bps] 154 prgb[p*3+2] = src[b+2*bps] 155 } else { 156 prgb[p*3] = src[b] 157 prgb[p*3+1] = src[b] 158 prgb[p*3+2] = src[b] 159 } 160 p = p + 1 161 } 162 out_wh[0] = pw 163 out_wh[1] = ph 164 return prgb 165 } 166 return ico_dib_rgb(raw, n, foff, flen, out_wh) 167}