code wiki / (root) / nx_paint_solid_rect.nx

nx_paint_solid_rect.nx source

↩ module page · 141 lines · 4700 B

1// nx_paint_solid_rect.nx -- fill a rectangle of pixels in a 2// caller-supplied framebuffer with a solid RGBA color. Phase 4 first 3// primitive of NISHI_BROWSER_ROADMAP -- bridges Phase 3 layout 4// (assigns x/y/w/h per box) to actual pixel output. 5// 6// Framebuffer layout: row-major RGBA u8 quads. 7// pixels[(y * width + x) * 4 + 0] = R 8// pixels[(y * width + x) * 4 + 1] = G 9// pixels[(y * width + x) * 4 + 2] = B 10// pixels[(y * width + x) * 4 + 3] = A 11// 12// Caller-owns-memory contract: the Framebuffer struct points at a 13// pre-allocated pixel array of width * height * 4 bytes. 14// 15// Clipping: rectangles outside the framebuffer bounds are clipped to 16// the visible region; out-of-bounds writes never happen. Zero-area 17// rectangles are silently skipped. 18// 19// What it does NOT handle yet (Phase 4b queued): 20// - alpha blending (currently overwrites destination -- assumes 21// opaque foreground; transparent fills via this primitive will 22// paint solid black behind transparent areas, wrong for layered 23// elements). Composes with future nx_paint_blend. 24// - sub-pixel positioning (integer-pixel only) 25// - anti-aliased edges (Phase 4b after sub-pixel) 26// - clip-rect stacking for overflow:hidden 27// - SIMD acceleration (Phase 4c when nx_simd lands) 28// 29// Per cardinal feedback-honest-perf-verdict-no-aspirational-claims: 30// gap list is EXACT. 31// 32// genealogy_id: substrate_browser_phase_4_paint_seed + 33// classical_2d_rect_fill 34// lineage_id: nishi_browser_paint_solid_rect_v1 35// 36// nx_safety_envelope: 37// intended_use: "Solid rect fill -- browser paint substrate" 38// sil_target: SIL1 39// evidence: [bounded_pixel_loop, 40// explicit_clipping, 41// no_internal_allocation, 42// no_FP_arithmetic] 43// verdict: NOT_YET_EVALUATED 44 45import "nx_syscalls.nx" 46import "nx_css_color_decode.nx" 47 48// Caller-supplied framebuffer wrapping a row-major RGBA8888 pixel 49// array. 50struct Framebuffer { 51 pixels: *u8, 52 width: i64, 53 height: i64 54} 55 56const NX_FRAMEBUFFER_BYTES: i64 = 24 57 58// ---- public API ---- 59 60// Initialize a framebuffer wrapper. Does NOT zero the pixel array. 61// Caller is responsible for clearing if desired. 62func nx_framebuffer_init(fb: *Framebuffer, pixels: *u8, 63 width: i64, height: i64) -> i64 { 64 fb.pixels = pixels 65 fb.width = width 66 fb.height = height 67 return 0 68} 69 70// Zero the framebuffer's pixel array (4 bytes per pixel). 71func nx_framebuffer_clear(fb: *Framebuffer) -> i64 { 72 let total: i64 = fb.width * fb.height * 4 73 var i: i64 = 0 74 while i < total { 75 fb.pixels[i] = 0 as u8 76 i = i + 1 77 } 78 return 0 79} 80 81// Read a single pixel's RGBA bytes into the caller-supplied CssColor 82// struct. Returns 1 on success, 0 if (x, y) is out of bounds. 83func nx_framebuffer_get_pixel(fb: *Framebuffer, x: i64, y: i64, 84 out: *CssColor) -> i64 { 85 if x < 0 { return 0 } 86 if y < 0 { return 0 } 87 if x >= fb.width { return 0 } 88 if y >= fb.height { return 0 } 89 let off: i64 = (y * fb.width + x) * 4 90 out.r = (fb.pixels[off + 0] as i64) & 255 91 out.g = (fb.pixels[off + 1] as i64) & 255 92 out.b = (fb.pixels[off + 2] as i64) & 255 93 out.a = (fb.pixels[off + 3] as i64) & 255 94 return 1 95} 96 97// Fill the rectangle (x, y, w, h) in `fb` with the solid color 98// `color`. Returns the number of pixels actually written (zero on 99// fully-clipped or zero-area rectangles). 100// 101// Clipping: rectangle is intersected with [0, fb.width) x [0, fb.height). 102func nx_paint_solid_rect(fb: *Framebuffer, 103 x: i64, y: i64, w: i64, h: i64, 104 color: *CssColor) -> i64 { 105 if w <= 0 { return 0 } 106 if h <= 0 { return 0 } 107 108 // Clip to framebuffer bounds. 109 var x0: i64 = x 110 var y0: i64 = y 111 var x1: i64 = x + w 112 var y1: i64 = y + h 113 if x0 < 0 { x0 = 0 } 114 if y0 < 0 { y0 = 0 } 115 if x1 > fb.width { x1 = fb.width } 116 if y1 > fb.height { y1 = fb.height } 117 if x0 >= x1 { return 0 } 118 if y0 >= y1 { return 0 } 119 120 let r: i64 = color.r & 255 121 let g: i64 = color.g & 255 122 let b: i64 = color.b & 255 123 let a: i64 = color.a & 255 124 125 var written: i64 = 0 126 var py: i64 = y0 127 while py < y1 { 128 var px: i64 = x0 129 while px < x1 { 130 let off: i64 = (py * fb.width + px) * 4 131 fb.pixels[off + 0] = r as u8 132 fb.pixels[off + 1] = g as u8 133 fb.pixels[off + 2] = b as u8 134 fb.pixels[off + 3] = a as u8 135 written = written + 1 136 px = px + 1 137 } 138 py = py + 1 139 } 140 return written 141}