diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-30 00:02:25 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:17 -0300 |
| commit | e38d236225b9da40f08bcb8d0ec72259cfe6647c (patch) | |
| tree | ae1fa1d46698e0f1771c873bd372683a3deec70c /src/gui/gui.zig | |
| parent | 7784ac98a09c0f7185d12b66307d67361e7de69d (diff) | |
| download | pardes-e38d236225b9da40f08bcb8d0ec72259cfe6647c.tar.gz pardes-e38d236225b9da40f08bcb8d0ec72259cfe6647c.zip | |
A lapis plaque paints each pixel once: its band's ground, shadow and frame are drawn only where the layers over them leave them showing
The four opaque layers of every tag's plaque were whole rectangles stacked on each other. At 4K (scale 2, 120x40, RTX 3050) a lapis draw now takes 491 us of GPU, down from 601 (forge: 463); pixels identical in hidden captures at 1x and 2x.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/gui/gui.zig')
| -rw-r--r-- | src/gui/gui.zig | 57 |
1 files changed, 52 insertions, 5 deletions
diff --git a/src/gui/gui.zig b/src/gui/gui.zig index c737450b..be961477 100644 --- a/src/gui/gui.zig +++ b/src/gui/gui.zig @@ -9330,12 +9330,15 @@ fn tagPlaques(g: *Gui, gpa: std.mem.Allocator, regions: []const pardes.Region, g const b = plaque.border; const sh = plaque.shadow; const fill = if (tag.active) chrome.tag_focus_bg else chrome.tag_bg; - // The band's own ground, then the shadow, the frame and the fill. - try addDecor(g, gpa, plaque.band[0], plaque.band[1], plaque.band[2], plaque.band[3], page, 1, track, win_w, win_h); - const shadow_rgb = chrome.decor_box_shadow orelse border_rgb; - try addDecor(g, gpa, plaque.box[0] + sh[0], plaque.box[1] + sh[1], plaque.box[2] + sh[0], plaque.box[3] + sh[1], if (pane) shadow_rgb else mixRgb(shadow_rgb, page), 1, track, win_w, win_h); - try addDecor(g, gpa, plaque.box[0], plaque.box[1], plaque.box[2], plaque.box[3], border_rgb, 1, track, win_w, win_h); + // The band's own ground, then the shadow, the frame and the fill, + // each only where the ones over it leave it showing: all four are + // opaque, so that is the same picture drawn once, not four times. + const shadow: [4]f32 = .{ plaque.box[0] + sh[0], plaque.box[1] + sh[1], plaque.box[2] + sh[0], plaque.box[3] + sh[1] }; const inner: [4]f32 = .{ plaque.box[0] + b, plaque.box[1] + b, plaque.box[2] - b, plaque.box[3] - b }; + try addDecorExcept(g, gpa, plaque.band, &.{ plaque.box, shadow }, page, track, win_w, win_h); + const shadow_rgb = chrome.decor_box_shadow orelse border_rgb; + try addDecorExcept(g, gpa, shadow, &.{plaque.box}, if (pane) shadow_rgb else mixRgb(shadow_rgb, page), track, win_w, win_h); + try addDecorExcept(g, gpa, plaque.box, &.{inner}, border_rgb, track, win_w, win_h); if (pane and tag.active and chrome.decor_box_stripe != null) try addPattern(g, gpa, inner[0], inner[1], inner[2], inner[3], chrome.decor_box_stripe.?, fill, dp(@floatFromInt(@max(1, chrome.decor_box_stripe_px))), true, track, win_w, win_h) else @@ -9343,6 +9346,50 @@ fn tagPlaques(g: *Gui, gpa: std.mem.Allocator, regions: []const pardes.Region, g }; } +/// Flat decor over `rect` (window pixels) less each of `holes`: up to four +/// strips round the first hole's part of it, each less the rest. +fn addDecorExcept(g: *Gui, gpa: std.mem.Allocator, rect: [4]f32, holes: []const [4]f32, rgb: [3]u8, track: ?pardes.animation.Track, win_w: f32, win_h: f32) std.mem.Allocator.Error!void { + if (rect[0] >= rect[2] or rect[1] >= rect[3]) return; + if (holes.len == 0) return addDecor(g, gpa, rect[0], rect[1], rect[2], rect[3], rgb, 1, track, win_w, win_h); + const hole = holes[0]; + const x0 = @max(rect[0], hole[0]); + const y0 = @max(rect[1], hole[1]); + const x1 = @min(rect[2], hole[2]); + const y1 = @min(rect[3], hole[3]); + if (x0 >= x1 or y0 >= y1) return addDecorExcept(g, gpa, rect, holes[1..], rgb, track, win_w, win_h); + for ([_][4]f32{ + .{ rect[0], rect[1], rect[2], y0 }, + .{ rect[0], y1, rect[2], rect[3] }, + .{ rect[0], y0, x0, y1 }, + .{ x1, y0, rect[2], y1 }, + }) |strip| try addDecorExcept(g, gpa, strip, holes[1..], rgb, track, win_w, win_h); +} + +test "decor less its holes covers each pixel of the rest once and none of the holes" { + const gpa = std.testing.allocator; + var g: Gui = undefined; + g.cell_w = 1; + g.cell_h = 1; + g.decor = .empty; + defer g.decor.deinit(gpa); + const holes = [_][4]f32{ .{ 3, 2, 9, 6 }, .{ 5, 4, 12, 8 }, .{ 30, 30, 40, 40 } }; + try addDecorExcept(&g, gpa, .{ 1, 1, 11, 10 }, &holes, .{ 1, 2, 3 }, null, 16, 12); + for (0..12) |y| for (0..16) |x| { + const px: f32 = @as(f32, @floatFromInt(x)) + 0.5; + const py: f32 = @as(f32, @floatFromInt(y)) + 0.5; + var hits: usize = 0; + for (g.decor.items) |item| { + const r: [4]f32 = .{ (item.x0 + 1) / 2 * 16, (1 - item.y0) / 2 * 12, (item.x1 + 1) / 2 * 16, (1 - item.y1) / 2 * 12 }; + hits += @intFromBool(px > r[0] and px < r[2] and py > r[1] and py < r[3]); + } + const in_rect = px > 1 and px < 11 and py > 1 and py < 10; + const in_hole = for (holes) |h| { + if (px > h[0] and px < h[2] and py > h[1] and py < h[3]) break true; + } else false; + try std.testing.expectEqual(@as(usize, @intFromBool(in_rect and !in_hole)), hits); + }; +} + const Plaque = struct { band: [4]f32, box: [4]f32, border: f32, shadow: [2]f32 }; /// A tag's plaque in window pixels: its band (the rows, from after the |
