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}