code wiki / (root) / nx_aa_raster.nx

nx_aa_raster.nx source

↩ module page · 86 lines · 3617 B

1// nx_aa_raster.nx -- SOVEREIGN anti-aliased contour rasterizer (the core of the AA font renderer). 2// Fills closed polygon contours into an 8-bit COVERAGE buffer (0..255) via ss x ss supersampling + 3// even-odd inside test -- exactly how real font engines anti-alias glyph outlines. Glyph coords live in 4// the SUPERSAMPLED grid (W*ss by H*ss). Holes (the counter of 'o','a','e') fall out of even-odd for free. 5// R1 of the AA-font arc. No floats (integer cross-multiply crossing test). license_tier: ORIGINAL 6import "nx_syscalls.nx" 7 8// NONZERO-winding point-in-contours at integer grid point (gx,gy) (the TTF fill rule): overlapping 9// same-wound strokes stay filled (no spurious holes), and a hole is a REVERSE-wound inner contour. 10// Counts signed crossings of a rightward ray; inside = winding != 0. Integer cross-multiply (no floats). 11func aa_inside(gx: i64, gy: i64, xs: *i64, ys: *i64, cstart: *i64, clen: *i64, ncont: i64) -> i64 { 12 var wind: i64 = 0 13 var c: i64 = 0 14 while c < ncont { 15 let s: i64 = cstart[c] 16 let n: i64 = clen[c] 17 var i: i64 = 0 18 var j: i64 = n - 1 19 while i < n { 20 let xi: i64 = xs[s+i]; let yi: i64 = ys[s+i] 21 let xj: i64 = xs[s+j]; let yj: i64 = ys[s+j] // edge P[j] -> P[i] 22 if yj <= gy { if yi > gy { // upward crossing 23 if (gx - xj) * (yi - yj) < (gy - yj) * (xi - xj) { wind = wind + 1 } 24 } } 25 if yi <= gy { if yj > gy { // downward crossing 26 if (gx - xj) * (yi - yj) > (gy - yj) * (xi - xj) { wind = wind - 1 } 27 } } 28 j = i 29 i = i + 1 30 } 31 c = c + 1 32 } 33 if wind != 0 { return 1 } 34 return 0 35} 36 37// rasterize filled contours into cov (W*H bytes, 0..255 coverage). ss x ss supersample per output pixel. 38func aa_render(xs: *i64, ys: *i64, cstart: *i64, clen: *i64, ncont: i64, cov: *u8, W: i64, H: i64, ss: i64) -> i64 { 39 let tot: i64 = ss * ss 40 var py: i64 = 0 41 while py < H { 42 var px: i64 = 0 43 while px < W { 44 var cnt: i64 = 0 45 var sy: i64 = 0 46 while sy < ss { 47 var sx: i64 = 0 48 while sx < ss { 49 if aa_inside(px*ss + sx, py*ss + sy, xs, ys, cstart, clen, ncont) == 1 { cnt = cnt + 1 } 50 sx = sx + 1 51 } 52 sy = sy + 1 53 } 54 cov[py*W + px] = ((cnt * 255) / tot) as u8 55 px = px + 1 56 } 57 py = py + 1 58 } 59 return 0 60} 61 62// alpha-blit a coverage glyph onto an RGB framebuffer at (ox,oy): out = cov*fg + (1-cov)*out, per channel. 63// fr/fg/fb = text color. fbw = framebuffer width in pixels. cov is gw x gh. 64func aa_blit(rgb: *u8, fbw: i64, fbh: i64, ox: i64, oy: i64, cov: *u8, gw: i64, gh: i64, fr: i64, fgc: i64, fb: i64) -> i64 { 65 var y: i64 = 0 66 while y < gh { 67 var x: i64 = 0 68 while x < gw { 69 let dy: i64 = oy + y 70 let dx: i64 = ox + x 71 if dy >= 0 { if dy < fbh { if dx >= 0 { if dx < fbw { 72 let a: i64 = cov[y*gw + x] & 0xff 73 if a > 0 { 74 let p: i64 = (dy*fbw + dx) * 3 75 let inv: i64 = 255 - a 76 rgb[p+0] = (((rgb[p+0]&0xff) * inv + fr * a) / 255) as u8 77 rgb[p+1] = (((rgb[p+1]&0xff) * inv + fgc * a) / 255) as u8 78 rgb[p+2] = (((rgb[p+2]&0xff) * inv + fb * a) / 255) as u8 79 } 80 } } } } 81 x = x + 1 82 } 83 y = y + 1 84 } 85 return 0 86}