code wiki / (root) / nx_paint_box_borders.nx

nx_paint_box_borders.nx source

↩ module page · 338 lines · 15595 B

1// nx_paint_box_borders.nx -- paint a box's 4 borders (top / right / 2// bottom / left) into a Framebuffer. Phase 4 third primitive of 3// NISHI_BROWSER_ROADMAP. Composes nx_css_apply (cascade) + 4// nx_css_dimension_to_px (border-width) + nx_css_color_decode 5// (border-color) + nx_paint_solid_rect (actual pixel fill). 6// 7// Per W3C CSS Backgrounds and Borders Module Level 3, each border 8// has independent width + color + style. This primitive supports 9// solid borders only (style: solid). Other border-styles (dashed, 10// dotted, double) are Phase 4b queued. 11// 12// Geometry: borders are painted OUTSIDE the box's content/padding 13// rect in the CSS box model, but since nx_layout_block currently 14// uses content-box width (b.w = content_w) and mixed-axis height 15// (b.h = padding_top + content_h + padding_bottom), border placement 16// here treats b as the BORDER box (top edge at b.y - border_top_w, 17// etc.). Until nx_layout_block grows border-box semantics in 18// Phase 3b, the simpler convention is borders drawn INSIDE b's 19// rectangle along each edge. 20// 21// What it does NOT handle yet (Phase 4b queued, explicit in header): 22// - border-style values other than solid (dashed, dotted, double, 23// groove, ridge, inset, outset) 24// - border-radius (rounded corners) 25// - border-image 26// - corner blending where two adjacent borders meet (currently 27// the right/bottom borders paint OVER the top/left at corners -- 28// last-wins, source-order; W3C spec wants mitred joins) 29// - independent border-width directional shorthand parsing 30// (border-width: 2px 1px 3px 1px) -- only longhands today 31// 32// Per cardinal feedback-honest-perf-verdict-no-aspirational-claims: 33// gap list is EXACT. 34// 35// genealogy_id: w3c_css_backgrounds_borders_module_level_3 + 36// substrate_browser_phase_4_box_borders 37// lineage_id: nishi_browser_paint_box_borders_v1 38 39import "nx_syscalls.nx" 40import "nx_css_tokenize.nx" 41import "nx_css_apply.nx" 42import "nx_css_color_decode.nx" 43import "nx_css_number_parse.nx" 44import "nx_css_dimension_to_px.nx" 45import "nx_layout_box.nx" 46import "nx_paint_solid_rect.nx" 47 48// Property-name table -- 8 entries (4 widths + 4 colors). Each 49// off/len pair points into the table.src buffer which the 50// nx_border_prop_table_init function populates. 51struct BorderPropTable { 52 src: *u8, 53 bt_width_off: i64, 54 bt_width_len: i64, 55 br_width_off: i64, 56 br_width_len: i64, 57 bb_width_off: i64, 58 bb_width_len: i64, 59 bl_width_off: i64, 60 bl_width_len: i64, 61 bt_color_off: i64, 62 bt_color_len: i64, 63 br_color_off: i64, 64 br_color_len: i64, 65 bb_color_off: i64, 66 bb_color_len: i64, 67 bl_color_off: i64, 68 bl_color_len: i64 69} 70 71const NX_BORDER_PROP_TABLE_BYTES: i64 = 136 72 73// ---- helpers ---- 74 75func _border_put_byte(buf: *u8, i: i64, v: i64) -> i64 { 76 buf[i] = v as u8 77 return 0 78} 79 80// Initialize the property-name table. Caller supplies a 192-byte 81// buffer; this function writes the 8 canonical property names and 82// records their offsets/lengths. 83func nx_border_prop_table_init(t: *BorderPropTable, buf: *u8) -> i64 { 84 t.src = buf 85 86 // "border-top-width" 16 bytes at offset 0 87 let bt_w_str: *u8 = buf 88 _border_put_byte(bt_w_str, 0, 98) _border_put_byte(bt_w_str, 1, 111) 89 _border_put_byte(bt_w_str, 2, 114) _border_put_byte(bt_w_str, 3, 100) 90 _border_put_byte(bt_w_str, 4, 101) _border_put_byte(bt_w_str, 5, 114) 91 _border_put_byte(bt_w_str, 6, 45) _border_put_byte(bt_w_str, 7, 116) 92 _border_put_byte(bt_w_str, 8, 111) _border_put_byte(bt_w_str, 9, 112) 93 _border_put_byte(bt_w_str, 10, 45) _border_put_byte(bt_w_str, 11, 119) 94 _border_put_byte(bt_w_str, 12, 105) _border_put_byte(bt_w_str, 13, 100) 95 _border_put_byte(bt_w_str, 14, 116) _border_put_byte(bt_w_str, 15, 104) 96 t.bt_width_off = 0 97 t.bt_width_len = 16 98 99 // "border-right-width" 18 bytes at offset 16 100 let off1: i64 = 16 101 _border_put_byte(buf, off1 + 0, 98) _border_put_byte(buf, off1 + 1, 111) 102 _border_put_byte(buf, off1 + 2, 114) _border_put_byte(buf, off1 + 3, 100) 103 _border_put_byte(buf, off1 + 4, 101) _border_put_byte(buf, off1 + 5, 114) 104 _border_put_byte(buf, off1 + 6, 45) _border_put_byte(buf, off1 + 7, 114) 105 _border_put_byte(buf, off1 + 8, 105) _border_put_byte(buf, off1 + 9, 103) 106 _border_put_byte(buf, off1 + 10, 104) _border_put_byte(buf, off1 + 11, 116) 107 _border_put_byte(buf, off1 + 12, 45) _border_put_byte(buf, off1 + 13, 119) 108 _border_put_byte(buf, off1 + 14, 105) _border_put_byte(buf, off1 + 15, 100) 109 _border_put_byte(buf, off1 + 16, 116) _border_put_byte(buf, off1 + 17, 104) 110 t.br_width_off = off1 111 t.br_width_len = 18 112 113 // "border-bottom-width" 19 bytes at offset 34 114 let off2: i64 = 34 115 _border_put_byte(buf, off2 + 0, 98) _border_put_byte(buf, off2 + 1, 111) 116 _border_put_byte(buf, off2 + 2, 114) _border_put_byte(buf, off2 + 3, 100) 117 _border_put_byte(buf, off2 + 4, 101) _border_put_byte(buf, off2 + 5, 114) 118 _border_put_byte(buf, off2 + 6, 45) _border_put_byte(buf, off2 + 7, 98) 119 _border_put_byte(buf, off2 + 8, 111) _border_put_byte(buf, off2 + 9, 116) 120 _border_put_byte(buf, off2 + 10, 116) _border_put_byte(buf, off2 + 11, 111) 121 _border_put_byte(buf, off2 + 12, 109) _border_put_byte(buf, off2 + 13, 45) 122 _border_put_byte(buf, off2 + 14, 119) _border_put_byte(buf, off2 + 15, 105) 123 _border_put_byte(buf, off2 + 16, 100) _border_put_byte(buf, off2 + 17, 116) 124 _border_put_byte(buf, off2 + 18, 104) 125 t.bb_width_off = off2 126 t.bb_width_len = 19 127 128 // "border-left-width" 17 bytes at offset 53 129 let off3: i64 = 53 130 _border_put_byte(buf, off3 + 0, 98) _border_put_byte(buf, off3 + 1, 111) 131 _border_put_byte(buf, off3 + 2, 114) _border_put_byte(buf, off3 + 3, 100) 132 _border_put_byte(buf, off3 + 4, 101) _border_put_byte(buf, off3 + 5, 114) 133 _border_put_byte(buf, off3 + 6, 45) _border_put_byte(buf, off3 + 7, 108) 134 _border_put_byte(buf, off3 + 8, 101) _border_put_byte(buf, off3 + 9, 102) 135 _border_put_byte(buf, off3 + 10, 116) _border_put_byte(buf, off3 + 11, 45) 136 _border_put_byte(buf, off3 + 12, 119) _border_put_byte(buf, off3 + 13, 105) 137 _border_put_byte(buf, off3 + 14, 100) _border_put_byte(buf, off3 + 15, 116) 138 _border_put_byte(buf, off3 + 16, 104) 139 t.bl_width_off = off3 140 t.bl_width_len = 17 141 142 // "border-top-color" 16 bytes at offset 70 143 let off4: i64 = 70 144 _border_put_byte(buf, off4 + 0, 98) _border_put_byte(buf, off4 + 1, 111) 145 _border_put_byte(buf, off4 + 2, 114) _border_put_byte(buf, off4 + 3, 100) 146 _border_put_byte(buf, off4 + 4, 101) _border_put_byte(buf, off4 + 5, 114) 147 _border_put_byte(buf, off4 + 6, 45) _border_put_byte(buf, off4 + 7, 116) 148 _border_put_byte(buf, off4 + 8, 111) _border_put_byte(buf, off4 + 9, 112) 149 _border_put_byte(buf, off4 + 10, 45) _border_put_byte(buf, off4 + 11, 99) 150 _border_put_byte(buf, off4 + 12, 111) _border_put_byte(buf, off4 + 13, 108) 151 _border_put_byte(buf, off4 + 14, 111) _border_put_byte(buf, off4 + 15, 114) 152 t.bt_color_off = off4 153 t.bt_color_len = 16 154 155 // "border-right-color" 18 bytes at offset 86 156 let off5: i64 = 86 157 _border_put_byte(buf, off5 + 0, 98) _border_put_byte(buf, off5 + 1, 111) 158 _border_put_byte(buf, off5 + 2, 114) _border_put_byte(buf, off5 + 3, 100) 159 _border_put_byte(buf, off5 + 4, 101) _border_put_byte(buf, off5 + 5, 114) 160 _border_put_byte(buf, off5 + 6, 45) _border_put_byte(buf, off5 + 7, 114) 161 _border_put_byte(buf, off5 + 8, 105) _border_put_byte(buf, off5 + 9, 103) 162 _border_put_byte(buf, off5 + 10, 104) _border_put_byte(buf, off5 + 11, 116) 163 _border_put_byte(buf, off5 + 12, 45) _border_put_byte(buf, off5 + 13, 99) 164 _border_put_byte(buf, off5 + 14, 111) _border_put_byte(buf, off5 + 15, 108) 165 _border_put_byte(buf, off5 + 16, 111) _border_put_byte(buf, off5 + 17, 114) 166 t.br_color_off = off5 167 t.br_color_len = 18 168 169 // "border-bottom-color" 19 bytes at offset 104 170 let off6: i64 = 104 171 _border_put_byte(buf, off6 + 0, 98) _border_put_byte(buf, off6 + 1, 111) 172 _border_put_byte(buf, off6 + 2, 114) _border_put_byte(buf, off6 + 3, 100) 173 _border_put_byte(buf, off6 + 4, 101) _border_put_byte(buf, off6 + 5, 114) 174 _border_put_byte(buf, off6 + 6, 45) _border_put_byte(buf, off6 + 7, 98) 175 _border_put_byte(buf, off6 + 8, 111) _border_put_byte(buf, off6 + 9, 116) 176 _border_put_byte(buf, off6 + 10, 116) _border_put_byte(buf, off6 + 11, 111) 177 _border_put_byte(buf, off6 + 12, 109) _border_put_byte(buf, off6 + 13, 45) 178 _border_put_byte(buf, off6 + 14, 99) _border_put_byte(buf, off6 + 15, 111) 179 _border_put_byte(buf, off6 + 16, 108) _border_put_byte(buf, off6 + 17, 111) 180 _border_put_byte(buf, off6 + 18, 114) 181 t.bb_color_off = off6 182 t.bb_color_len = 19 183 184 // "border-left-color" 17 bytes at offset 123 185 let off7: i64 = 123 186 _border_put_byte(buf, off7 + 0, 98) _border_put_byte(buf, off7 + 1, 111) 187 _border_put_byte(buf, off7 + 2, 114) _border_put_byte(buf, off7 + 3, 100) 188 _border_put_byte(buf, off7 + 4, 101) _border_put_byte(buf, off7 + 5, 114) 189 _border_put_byte(buf, off7 + 6, 45) _border_put_byte(buf, off7 + 7, 108) 190 _border_put_byte(buf, off7 + 8, 101) _border_put_byte(buf, off7 + 9, 102) 191 _border_put_byte(buf, off7 + 10, 116) _border_put_byte(buf, off7 + 11, 45) 192 _border_put_byte(buf, off7 + 12, 99) _border_put_byte(buf, off7 + 13, 111) 193 _border_put_byte(buf, off7 + 14, 108) _border_put_byte(buf, off7 + 15, 111) 194 _border_put_byte(buf, off7 + 16, 114) 195 t.bl_color_off = off7 196 t.bl_color_len = 17 197 198 return 0 199} 200 201// Byte-equal compare across two source buffers. 202func _border_bytes_equal(a_src: *u8, a_off: i64, a_len: i64, 203 b_src: *u8, b_off: i64, b_len: i64) -> i64 { 204 if a_len != b_len { return 0 } 205 var i: i64 = 0 206 while i < a_len { 207 let ca: i64 = (a_src[a_off + i] as i64) & 255 208 let cb: i64 = (b_src[b_off + i] as i64) & 255 209 if ca != cb { return 0 } 210 i = i + 1 211 } 212 return 1 213} 214 215// Look up the last CssComputedDecl matching (element_idx, property) 216// across two source buffers. 217func _border_lookup(computed: *CssComputedDecl, n_computed: i64, 218 cascade_src: *u8, 219 table_src: *u8, prop_off: i64, prop_len: i64, 220 element_idx: i64) -> *CssComputedDecl { 221 var i: i64 = n_computed - 1 222 while i >= 0 { 223 let cd: *CssComputedDecl = (computed as *u8 + (i as nx_size) * (NX_CSS_COMPUTED_DECL_BYTES as nx_size)) as *CssComputedDecl 224 if cd.element_idx == element_idx { 225 if _border_bytes_equal(cascade_src, cd.prop_off, cd.prop_len, 226 table_src, prop_off, prop_len) == 1 { 227 return cd 228 } 229 } 230 i = i - 1 231 } 232 return 0 as *CssComputedDecl 233} 234 235// Read a border-width property: cascade lookup -> DIMENSION decode. 236// Returns the integer px width, or 0 if not declared / wrong kind. 237func _border_width(computed: *CssComputedDecl, n_computed: i64, 238 cascade_src: *u8, table_src: *u8, 239 prop_off: i64, prop_len: i64, 240 element_idx: i64, 241 resolve_ctx: *CssResolveCtx) -> i64 { 242 let cd: *CssComputedDecl = _border_lookup(computed, n_computed, 243 cascade_src, table_src, prop_off, prop_len, element_idx) 244 if cd == (0 as *CssComputedDecl) { return 0 } 245 if cd.val_kind != NX_CSS_VAL_DIMENSION { return 0 } 246 let px: i64 = nx_css_dimension_to_px(cascade_src, 247 cd.val_off, cd.val_len, cd.unit_off, cd.unit_len, resolve_ctx) 248 if px < 0 { return 0 } 249 return px 250} 251 252// Read a border-color property: cascade lookup -> HEXCOLOR decode. 253// Returns 1 on success and fills *out; 0 if not declared / wrong kind. 254func _border_color(computed: *CssComputedDecl, n_computed: i64, 255 cascade_src: *u8, table_src: *u8, 256 prop_off: i64, prop_len: i64, 257 element_idx: i64, 258 out: *CssColor) -> i64 { 259 let cd: *CssComputedDecl = _border_lookup(computed, n_computed, 260 cascade_src, table_src, prop_off, prop_len, element_idx) 261 if cd == (0 as *CssComputedDecl) { return 0 } 262 if cd.val_kind != NX_CSS_VAL_HEXCOLOR { return 0 } 263 return nx_css_color_decode(cascade_src, cd.val_off, cd.val_len, out) 264} 265 266// ---- public API ---- 267 268// Paint one box's 4 borders. Returns total pixels written. Each side 269// only paints if BOTH its width AND its color are declared. 270// 271// Paint order: top, left, right, bottom -- right/bottom corners are 272// last-wins (W3C mitred-join semantics deferred to Phase 4b). 273func nx_paint_box_borders(fb: *Framebuffer, 274 box: *LayoutBox, 275 computed: *CssComputedDecl, n_computed: i64, 276 cascade_src: *u8, 277 table: *BorderPropTable, 278 element_idx: i64, 279 resolve_ctx: *CssResolveCtx) -> i64 { 280 let bt_w: i64 = _border_width(computed, n_computed, cascade_src, 281 table.src, table.bt_width_off, table.bt_width_len, element_idx, resolve_ctx) 282 let br_w: i64 = _border_width(computed, n_computed, cascade_src, 283 table.src, table.br_width_off, table.br_width_len, element_idx, resolve_ctx) 284 let bb_w: i64 = _border_width(computed, n_computed, cascade_src, 285 table.src, table.bb_width_off, table.bb_width_len, element_idx, resolve_ctx) 286 let bl_w: i64 = _border_width(computed, n_computed, cascade_src, 287 table.src, table.bl_width_off, table.bl_width_len, element_idx, resolve_ctx) 288 289 let bt_color: *CssColor = (sys_mmap(NX_CSS_COLOR_BYTES as nx_size)) as *CssColor 290 let br_color: *CssColor = (sys_mmap(NX_CSS_COLOR_BYTES as nx_size)) as *CssColor 291 let bb_color: *CssColor = (sys_mmap(NX_CSS_COLOR_BYTES as nx_size)) as *CssColor 292 let bl_color: *CssColor = (sys_mmap(NX_CSS_COLOR_BYTES as nx_size)) as *CssColor 293 294 let bt_ok: i64 = _border_color(computed, n_computed, cascade_src, 295 table.src, table.bt_color_off, table.bt_color_len, element_idx, bt_color) 296 let br_ok: i64 = _border_color(computed, n_computed, cascade_src, 297 table.src, table.br_color_off, table.br_color_len, element_idx, br_color) 298 let bb_ok: i64 = _border_color(computed, n_computed, cascade_src, 299 table.src, table.bb_color_off, table.bb_color_len, element_idx, bb_color) 300 let bl_ok: i64 = _border_color(computed, n_computed, cascade_src, 301 table.src, table.bl_color_off, table.bl_color_len, element_idx, bl_color) 302 303 var total: i64 = 0 304 305 // Top border (full width, height = bt_w). 306 if bt_w > 0 { 307 if bt_ok == 1 { 308 total = total + nx_paint_solid_rect(fb, box.x, box.y, 309 box.w, bt_w, bt_color) 310 } 311 } 312 313 // Left border (full height, width = bl_w). 314 if bl_w > 0 { 315 if bl_ok == 1 { 316 total = total + nx_paint_solid_rect(fb, box.x, box.y, 317 bl_w, box.h, bl_color) 318 } 319 } 320 321 // Right border (right edge inset by br_w). 322 if br_w > 0 { 323 if br_ok == 1 { 324 total = total + nx_paint_solid_rect(fb, 325 box.x + box.w - br_w, box.y, br_w, box.h, br_color) 326 } 327 } 328 329 // Bottom border (bottom edge inset by bb_w). 330 if bb_w > 0 { 331 if bb_ok == 1 { 332 total = total + nx_paint_solid_rect(fb, 333 box.x, box.y + box.h - bb_w, box.w, bb_w, bb_color) 334 } 335 } 336 337 return total 338}