Skip to content

Commit fedf1e1

Browse files
committed
fix(renderer): keep boxed glyphs visible
1 parent 2837763 commit fedf1e1

3 files changed

Lines changed: 119 additions & 31 deletions

File tree

‎neomacs-renderer-wgpu/src/renderer/content.rs‎

Lines changed: 26 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -283,9 +283,9 @@ impl WgpuRenderer {
283283
//
284284
// Rendering order:
285285
// 1. Backgrounds (window bg, stretches, char bg)
286-
// 2. Text (mask glyphs, color glyphs, composed)
287-
// 3. Decorations (underline, overline, strikethrough)
288-
// 4. Box borders (sharp and rounded)
286+
// 2. Box borders (sharp and rounded)
287+
// 3. Text (mask glyphs, color glyphs, composed)
288+
// 4. Decorations (underline, overline, strikethrough)
289289
// 5. Inline media (images, videos, webkit)
290290
// 6. Cursors, borders, scroll bars (on top)
291291
let mut bg_vertices: Vec<RectVertex> = Vec::new();
@@ -1449,6 +1449,29 @@ impl WgpuRenderer {
14491449
pass.draw(0..rounded_fill_vertices.len() as u32, 0..1);
14501450
}
14511451

1452+
// GNU draws character/composition box relief before the glyph so
1453+
// thick inset borders cannot cover a narrow character cell.
1454+
if let Some(upload) =
1455+
self.arenas
1456+
.rect
1457+
.upload(&self.device, &self.queue, &sharp_border_vertices)
1458+
{
1459+
pass.set_pipeline(rect_pl);
1460+
pass.set_bind_group(0, &self.uniform_bind_group, &[]);
1461+
pass.set_vertex_buffer(0, upload.buffer_slice());
1462+
pass.draw(0..sharp_border_vertices.len() as u32, 0..1);
1463+
}
1464+
if let Some(upload) =
1465+
self.arenas
1466+
.rounded
1467+
.upload(&self.device, &self.queue, &rounded_border_vertices)
1468+
{
1469+
pass.set_pipeline(rounded_rect_pl);
1470+
pass.set_bind_group(0, &self.uniform_bind_group, &[]);
1471+
pass.set_vertex_buffer(0, upload.buffer_slice());
1472+
pass.draw(0..rounded_border_vertices.len() as u32, 0..1);
1473+
}
1474+
14521475
// --- Draw mask text glyphs ---
14531476
if !mask_data.is_empty() {
14541477
let all_vertices: Vec<GlyphVertex> = mask_data
@@ -1593,30 +1616,6 @@ impl WgpuRenderer {
15931616
pass.draw(0..decoration_vertices.len() as u32, 0..1);
15941617
}
15951618

1596-
// --- Draw sharp box borders ---
1597-
if let Some(upload) =
1598-
self.arenas
1599-
.rect
1600-
.upload(&self.device, &self.queue, &sharp_border_vertices)
1601-
{
1602-
pass.set_pipeline(rect_pl);
1603-
pass.set_bind_group(0, &self.uniform_bind_group, &[]);
1604-
pass.set_vertex_buffer(0, upload.buffer_slice());
1605-
pass.draw(0..sharp_border_vertices.len() as u32, 0..1);
1606-
}
1607-
1608-
// --- Draw rounded box borders ---
1609-
if let Some(upload) =
1610-
self.arenas
1611-
.rounded
1612-
.upload(&self.device, &self.queue, &rounded_border_vertices)
1613-
{
1614-
pass.set_pipeline(rounded_rect_pl);
1615-
pass.set_bind_group(0, &self.uniform_bind_group, &[]);
1616-
pass.set_vertex_buffer(0, upload.buffer_slice());
1617-
pass.draw(0..rounded_border_vertices.len() as u32, 0..1);
1618-
}
1619-
16201619
// --- Draw inline images ---
16211620
pass.set_pipeline(image_pl);
16221621
pass.set_bind_group(0, &self.uniform_bind_group, &[]);

‎neomacs-renderer-wgpu/src/renderer/layer_text.rs‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
//! Text phases of `render_frame_glyphs` (z-order steps 4-6): glyph batch
2-
//! building, batched text draws, text decorations, and box borders, run once
3-
//! for buffer text and once for overlay (mode-line/echo) text.
1+
//! Text phases of `render_frame_glyphs` (z-order steps 4-6): box borders,
2+
//! glyph batches, and text decorations, run once for buffer text and once for
3+
//! overlay (mode-line/echo) text.
44
55
use neomacs_display_protocol::types::{FaceId, Rect};
66
use std::collections::HashSet;
@@ -78,9 +78,9 @@ impl WgpuRenderer {
7878
&batches.rendered_char_bounds,
7979
);
8080

81+
self.draw_box_borders(ctx, want_overlay, spans);
8182
self.draw_text_glyph_batches(ctx, want_overlay, glyph_atlas, &batches, stats);
8283
self.draw_text_decorations(ctx, want_overlay);
83-
self.draw_box_borders(ctx, want_overlay, spans);
8484
}
8585

8686
// Both passes ran: promote this frame's captured rows to the cache.

‎neomacs-renderer-wgpu/tests/offscreen_frame.rs‎

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,95 @@ fn negative_box_line_width_paints_an_inset_border() {
319319
);
320320
}
321321

322+
fn boxed_one_cell_frame() -> FrameGlyphBuffer {
323+
let mut frame = FrameGlyphBuffer::with_size(W as f32, H as f32);
324+
frame.background = Color::BLACK;
325+
let face_id = FaceId::new(21);
326+
frame.set_face(
327+
face_id,
328+
Color::WHITE,
329+
Some(Color::rgb(0.2, 0.2, 0.2)),
330+
400,
331+
false,
332+
0,
333+
None,
334+
0,
335+
None,
336+
0,
337+
None,
338+
);
339+
let face = frame.faces.get_mut(&face_id).unwrap();
340+
face.attributes |= FaceAttributes::BOX;
341+
face.box_type = BoxType::Line;
342+
face.box_color = Some(Color::BLACK);
343+
face.box_line_width = (-4).into();
344+
frame.set_draw_context(DisplayWindowId::new(1), GlyphRowRole::Text, None);
345+
frame.add_char('p', 20.0, 20.0, 8.0, 18.0, 14.0, false);
346+
frame
347+
}
348+
349+
fn boxed_p_is_visible(buf: &[u8]) -> bool {
350+
(20..28).any(|x| {
351+
(20..38).any(|y| {
352+
let pixel = px(buf, x, y);
353+
pixel[0] > 120 && pixel[1] > 120 && pixel[2] > 120
354+
})
355+
})
356+
}
357+
358+
#[test]
359+
fn negative_box_border_does_not_cover_a_one_cell_glyph() {
360+
let Some(mut h) = try_harness() else {
361+
return;
362+
};
363+
let frame = boxed_one_cell_frame();
364+
h.renderer.render_frame_glyphs(
365+
&h.view,
366+
&frame,
367+
&mut h.atlas,
368+
W,
369+
H,
370+
false,
371+
None,
372+
(0.0, 0.0),
373+
None,
374+
None,
375+
None,
376+
);
377+
let buf = read_back(&h);
378+
assert!(
379+
boxed_p_is_visible(&buf),
380+
"GNU draws character glyphs over their box; the boxed `p` must remain visible"
381+
);
382+
}
383+
384+
#[test]
385+
fn child_frame_negative_box_border_does_not_cover_a_one_cell_glyph() {
386+
let Some(mut h) = try_harness() else {
387+
return;
388+
};
389+
let frame = boxed_one_cell_frame();
390+
h.renderer.render_frame_content(
391+
&h.view,
392+
&frame,
393+
&mut h.atlas,
394+
W,
395+
H,
396+
0.0,
397+
0.0,
398+
false,
399+
None,
400+
0.0,
401+
None,
402+
None,
403+
);
404+
let buf = read_back(&h);
405+
assert!(
406+
boxed_p_is_visible(&buf),
407+
"child-frame glyphs must be drawn over their boxes like GNU Emacs"
408+
);
409+
}
410+
322411
#[test]
323412
fn cursor_visible_false_suppresses_cursor() {
324413
let Some(mut h) = try_harness() else {

0 commit comments

Comments
 (0)