From bb5b7b0274db96d0bb304bbde981591bc10ebea0 Mon Sep 17 00:00:00 2001 From: Qwerasd Date: Fri, 11 Oct 2024 13:55:43 -0400 Subject: [PATCH] font/sprite(Box): refactor smooth mosaic rendering + un-`comptime` the line spec and make it a packed struct, to reduce codegen size. --- src/font/sprite/Box.zig | 880 +++++++++++++++++-------------- src/font/sprite/testdata/Box.ppm | Bin 1048593 -> 1048593 bytes 2 files changed, 470 insertions(+), 410 deletions(-) diff --git a/src/font/sprite/Box.zig b/src/font/sprite/Box.zig index 4343f0be1..15c5ad64a 100644 --- a/src/font/sprite/Box.zig +++ b/src/font/sprite/Box.zig @@ -56,13 +56,13 @@ const Thickness = enum { /// Specification of a traditional intersection-style line/box-drawing char, /// which can have a different style of line from each edge to the center. -const Lines = struct { +const Lines = packed struct(u8) { up: Style = .none, right: Style = .none, down: Style = .none, left: Style = .none, - const Style = enum { + const Style = enum(u2) { none, light, heavy, @@ -122,13 +122,63 @@ const Alignment = struct { const bottom_right = lower_right; }; -const Corner = enum { +const Corner = enum(u2) { tl, tr, bl, br, }; +const Edge = enum(u2) { + top, + left, + bottom, + right, +}; + +const SmoothMosaic = packed struct(u10) { + tl: bool, + ul: bool, + ll: bool, + bl: bool, + bc: bool, + br: bool, + lr: bool, + ur: bool, + tr: bool, + tc: bool, + + fn from(comptime pattern: *const [15:0]u8) SmoothMosaic { + return .{ + .tl = pattern[0] == '#', + + .ul = pattern[4] == '#' and + (pattern[0] != '#' or pattern[8] != '#'), + + .ll = pattern[8] == '#' and + (pattern[4] != '#' or pattern[12] != '#'), + + .bl = pattern[12] == '#', + + .bc = pattern[13] == '#' and + (pattern[12] != '#' or pattern[14] != '#'), + + .br = pattern[14] == '#', + + .lr = pattern[10] == '#' and + (pattern[14] != '#' or pattern[6] != '#'), + + .ur = pattern[6] == '#' and + (pattern[10] != '#' or pattern[2] != '#'), + + .tr = pattern[2] == '#', + + .tc = pattern[1] == '#' and + (pattern[2] != '#' or pattern[0] != '#'), + }; + } +}; + // Utility names for common fractions const one_eighth: f64 = 0.125; const one_quarter: f64 = 0.25; @@ -533,41 +583,350 @@ fn draw(self: Box, alloc: Allocator, canvas: *font.sprite.Canvas, cp: u32) !void 0x1fb00...0x1fb3b => self.draw_sextant(canvas, cp), - 0x1fb3c...0x1fb40, - 0x1fb47...0x1fb4b, - 0x1fb57...0x1fb5b, - 0x1fb62...0x1fb66, - 0x1fb6c...0x1fb6f, - => try self.draw_wedge_triangle(canvas, cp), - - 0x1fb41...0x1fb45, - 0x1fb4c...0x1fb50, - 0x1fb52...0x1fb56, - 0x1fb5d...0x1fb61, - 0x1fb68...0x1fb6b, - => try self.draw_wedge_triangle_inverted(canvas, cp), + // '๐Ÿฌผ' + 0x1fb3c => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\... + \\... + \\#.. + \\##. + )), + // '๐Ÿฌฝ' + 0x1fb3d => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\... + \\... + \\#\. + \\### + )), + // '๐Ÿฌพ' + 0x1fb3e => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\... + \\#.. + \\#\. + \\##. + )), + // '๐Ÿฌฟ' + 0x1fb3f => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\... + \\#.. + \\##. + \\### + )), + // '๐Ÿญ€' + 0x1fb40 => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\#.. + \\#.. + \\##. + \\##. + )), + // '๐Ÿญ' + 0x1fb41 => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\/## + \\### + \\### + \\### + )), + // '๐Ÿญ‚' + 0x1fb42 => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\./# + \\### + \\### + \\### + )), + // '๐Ÿญƒ' + 0x1fb43 => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\.## + \\.## + \\### + \\### + )), + // '๐Ÿญ„' + 0x1fb44 => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\..# + \\.## + \\### + \\### + )), + // '๐Ÿญ…' + 0x1fb45 => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\.## + \\.## + \\.## + \\### + )), // '๐Ÿญ†' - 0x1fb46, + 0x1fb46 => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\... + \\./# + \\### + \\### + )), + + // '๐Ÿญ‡' + 0x1fb47 => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\... + \\... + \\..# + \\.## + )), + // '๐Ÿญˆ' + 0x1fb48 => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\... + \\... + \\./# + \\### + )), + // '๐Ÿญ‰' + 0x1fb49 => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\... + \\..# + \\./# + \\.## + )), + // '๐ŸญŠ' + 0x1fb4a => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\... + \\..# + \\.## + \\### + )), + // '๐Ÿญ‹' + 0x1fb4b => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\..# + \\..# + \\.## + \\.## + )), + + // '๐ŸญŒ' + 0x1fb4c => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\##\ + \\### + \\### + \\### + )), + // '๐Ÿญ' + 0x1fb4d => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\#\. + \\### + \\### + \\### + )), + // '๐ŸญŽ' + 0x1fb4e => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\##. + \\##. + \\### + \\### + )), + // '๐Ÿญ' + 0x1fb4f => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\#.. + \\##. + \\### + \\### + )), + // '๐Ÿญ' + 0x1fb50 => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\##. + \\##. + \\##. + \\### + )), // '๐Ÿญ‘' - 0x1fb51, + 0x1fb51 => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\... + \\#\. + \\### + \\### + )), + + // '๐Ÿญ’' + 0x1fb52 => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\### + \\### + \\### + \\\## + )), + // '๐Ÿญ“' + 0x1fb53 => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\### + \\### + \\### + \\.\# + )), + // '๐Ÿญ”' + 0x1fb54 => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\### + \\### + \\.## + \\.## + )), + // '๐Ÿญ•' + 0x1fb55 => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\### + \\### + \\.## + \\..# + )), + // '๐Ÿญ–' + 0x1fb56 => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\### + \\.## + \\.## + \\.## + )), + + // '๐Ÿญ—' + 0x1fb57 => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\##. + \\#.. + \\... + \\... + )), + // '๐Ÿญ˜' + 0x1fb58 => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\### + \\#/. + \\... + \\... + )), + // '๐Ÿญ™' + 0x1fb59 => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\##. + \\#/. + \\#.. + \\... + )), + // '๐Ÿญš' + 0x1fb5a => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\### + \\##. + \\#.. + \\... + )), + // '๐Ÿญ›' + 0x1fb5b => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\##. + \\##. + \\#.. + \\#.. + )), + // '๐Ÿญœ' - 0x1fb5c, + 0x1fb5c => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\### + \\### + \\#/. + \\... + )), + // '๐Ÿญ' + 0x1fb5d => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\### + \\### + \\### + \\##/ + )), + // '๐Ÿญž' + 0x1fb5e => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\### + \\### + \\### + \\#/. + )), + // '๐ŸญŸ' + 0x1fb5f => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\### + \\### + \\##. + \\##. + )), + // '๐Ÿญ ' + 0x1fb60 => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\### + \\### + \\##. + \\#.. + )), + // '๐Ÿญก' + 0x1fb61 => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\### + \\##. + \\##. + \\##. + )), + + // '๐Ÿญข' + 0x1fb62 => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\.## + \\..# + \\... + \\... + )), + // '๐Ÿญฃ' + 0x1fb63 => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\### + \\.\# + \\... + \\... + )), + // '๐Ÿญค' + 0x1fb64 => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\.## + \\.\# + \\..# + \\... + )), + // '๐Ÿญฅ' + 0x1fb65 => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\### + \\.## + \\..# + \\... + )), + // '๐Ÿญฆ' + 0x1fb66 => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\.## + \\.## + \\..# + \\..# + )), // '๐Ÿญง' - 0x1fb67, - => try self.draw_wedge_triangle_and_box(canvas, cp), + 0x1fb67 => try self.draw_smooth_mosaic(canvas, SmoothMosaic.from( + \\### + \\### + \\.\# + \\... + )), - // '๐Ÿฎš' - 0x1fb9a => { - try self.draw_wedge_triangle(canvas, 0x1fb6d); - try self.draw_wedge_triangle(canvas, 0x1fb6f); + // '๐Ÿญจ' + 0x1fb68 => { + try self.draw_edge_triangle(canvas, .left); + canvas.invert(); }, - - // '๐Ÿฎ›' - 0x1fb9b => { - try self.draw_wedge_triangle(canvas, 0x1fb6c); - try self.draw_wedge_triangle(canvas, 0x1fb6e); + // '๐Ÿญฉ' + 0x1fb69 => { + try self.draw_edge_triangle(canvas, .top); + canvas.invert(); }, + // '๐Ÿญช' + 0x1fb6a => { + try self.draw_edge_triangle(canvas, .right); + canvas.invert(); + }, + // '๐Ÿญซ' + 0x1fb6b => { + try self.draw_edge_triangle(canvas, .bottom); + canvas.invert(); + }, + // '๐Ÿญฌ' + 0x1fb6c => try self.draw_edge_triangle(canvas, .left), + // '๐Ÿญญ' + 0x1fb6d => try self.draw_edge_triangle(canvas, .top), + // '๐Ÿญฎ' + 0x1fb6e => try self.draw_edge_triangle(canvas, .right), + // '๐Ÿญฏ' + 0x1fb6f => try self.draw_edge_triangle(canvas, .bottom), // '๐Ÿญฐ' 0x1fb70 => self.draw_vertical_one_eighth_block_n(canvas, 1), @@ -685,6 +1044,16 @@ fn draw(self: Box, alloc: Allocator, canvas: *font.sprite.Canvas, cp: u32) !void 0x1fb98 => self.draw_upper_left_to_lower_right_fill(canvas), // '๐Ÿฎ™' 0x1fb99 => self.draw_upper_right_to_lower_left_fill(canvas), + // '๐Ÿฎš' + 0x1fb9a => { + try self.draw_edge_triangle(canvas, .top); + try self.draw_edge_triangle(canvas, .bottom); + }, + // '๐Ÿฎ›' + 0x1fb9b => { + try self.draw_edge_triangle(canvas, .left); + try self.draw_edge_triangle(canvas, .right); + }, // '๐Ÿฎœ' 0x1fb9c => self.draw_corner_triangle_shade(canvas, .tl, .medium), // '๐Ÿฎ' @@ -946,7 +1315,7 @@ fn draw(self: Box, alloc: Allocator, canvas: *font.sprite.Canvas, cp: u32) !void fn draw_lines( self: Box, canvas: *font.sprite.Canvas, - comptime lines: Lines, + lines: Lines, ) void { const light_px = Thickness.light.height(self.thickness); const heavy_px = Thickness.heavy.height(self.thickness); @@ -1669,393 +2038,84 @@ fn yThirds(self: Box) [2]u32 { }; } -fn draw_wedge_triangle(self: Box, canvas: *font.sprite.Canvas, cp: u32) !void { - const width = self.width; - const height = self.height; - - const x_halfs = self.xHalfs(); - const y_thirds = self.yThirds(); - const halfs0 = x_halfs[0]; - const halfs1 = x_halfs[1]; - const thirds0 = y_thirds[0]; - const thirds1 = y_thirds[1]; - - var p1_x: u32 = 0; - var p2_x: u32 = 0; - var p3_x: u32 = 0; - var p1_y: u32 = 0; - var p2_y: u32 = 0; - var p3_y: u32 = 0; - - switch (cp) { - 0x1fb3c => { - p3_x = halfs0; - p1_y = thirds1; - p2_y = height; - p3_y = height; - }, - - 0x1fb52 => { - p3_x = halfs0; - p1_y = thirds1; - p2_y = height; - p3_y = height; - }, - - 0x1fb3d => { - p3_x = width; - p1_y = thirds1; - p2_y = height; - p3_y = height; - }, - - 0x1fb53 => { - p3_x = width; - p1_y = thirds1; - p2_y = height; - p3_y = height; - }, - - 0x1fb3e => { - p3_x = halfs0; - p1_y = thirds0; - p2_y = height; - p3_y = height; - }, - - 0x1fb54 => { - p3_x = halfs0; - p1_y = thirds0; - p2_y = height; - p3_y = height; - }, - - 0x1fb3f => { - p3_x = width; - p1_y = thirds0; - p2_y = height; - p3_y = height; - }, - - 0x1fb55 => { - p3_x = width; - p1_y = thirds0; - p2_y = height; - p3_y = height; - }, - - 0x1fb40, 0x1fb56 => { - p3_x = halfs0; - p1_y = 0; - p2_y = height; - p3_y = height; - }, - - 0x1fb47 => { - p1_x = width; - p2_x = width; - p3_x = halfs1; - p1_y = thirds1; - p2_y = height; - p3_y = height; - }, - - 0x1fb5d => { - p1_x = width; - p2_x = width; - p3_x = halfs1; - p1_y = thirds1; - p2_y = height; - p3_y = height; - }, - - 0x1fb48 => { - p1_x = width; - p2_x = width; - p3_x = 0; - p1_y = thirds1; - p2_y = height; - p3_y = height; - }, - - 0x1fb5e => { - p1_x = width; - p2_x = width; - p3_x = 0; - p1_y = thirds1; - p2_y = height; - p3_y = height; - }, - - 0x1fb49 => { - p1_x = width; - p2_x = width; - p3_x = halfs1; - p1_y = thirds0; - p2_y = height; - p3_y = height; - }, - - 0x1fb5f => { - p1_x = width; - p2_x = width; - p3_x = halfs1; - p1_y = thirds0; - p2_y = height; - p3_y = height; - }, - - 0x1fb4a => { - p1_x = width; - p2_x = width; - p3_x = 0; - p1_y = thirds0; - p2_y = height; - p3_y = height; - }, - - 0x1fb60 => { - p1_x = width; - p2_x = width; - p3_x = 0; - p1_y = thirds0; - p2_y = height; - p3_y = height; - }, - - 0x1fb4b, 0x1fb61 => { - p1_x = width; - p2_x = width; - p3_x = halfs1; - p1_y = 0; - p2_y = height; - p3_y = height; - }, - - 0x1fb57 => { - p3_x = halfs0; - p2_y = thirds0; - }, - - 0x1fb41 => { - p3_x = halfs0; - p2_y = thirds0; - }, - - 0x1fb58 => { - p3_x = width; - p2_y = thirds0; - }, - - 0x1fb42 => { - p3_x = width; - p2_y = thirds0; - }, - - 0x1fb59 => { - p3_x = halfs0; - p2_y = thirds1; - }, - - 0x1fb43 => { - p3_x = halfs0; - p2_y = thirds1; - }, - - 0x1fb5a => { - p3_x = width; - p2_y = thirds1; - }, - - 0x1fb44 => { - p3_x = width; - p2_y = thirds1; - }, - - 0x1fb5b, 0x1fb45 => { - p3_x = halfs0; - p2_y = height; - }, - - 0x1fb62 => { - p1_x = width; - p2_x = width; - p3_x = halfs1; - p2_y = thirds0; - }, - - 0x1fb4c => { - p1_x = width; - p2_x = width; - p3_x = halfs1; - p2_y = thirds0; - }, - - 0x1fb63 => { - p1_x = width; - p2_x = width; - p3_x = 0; - p2_y = thirds0; - }, - - 0x1fb4d => { - p1_x = width; - p2_x = width; - p3_x = 0; - p2_y = thirds0; - }, - - 0x1fb64 => { - p1_x = width; - p2_x = width; - p3_x = halfs1; - p2_y = thirds1; - }, - - 0x1fb4e => { - p1_x = width; - p2_x = width; - p3_x = halfs1; - p2_y = thirds1; - }, - - 0x1fb65 => { - p1_x = width; - p2_x = width; - p3_x = 0; - p2_y = thirds1; - }, - - 0x1fb4f => { - p1_x = width; - p2_x = width; - p3_x = 0; - p2_y = thirds1; - }, - - 0x1fb66, 0x1fb50 => { - p1_x = width; - p2_x = width; - p3_x = halfs1; - p2_y = height; - }, - - 0x1fb46 => { - p1_x = 0; - p1_y = thirds1; - p2_x = width; - p2_y = thirds0; - p3_x = width; - p3_y = p1_y; - }, - - 0x1fb51 => { - p1_x = 0; - p1_y = thirds0; - p2_x = 0; - p2_y = thirds1; - p3_x = width; - p3_y = p2_y; - }, - - 0x1fb5c => { - p1_x = 0; - p1_y = thirds0; - p2_x = 0; - p2_y = thirds1; - p3_x = width; - p3_y = p1_y; - }, - - 0x1fb67 => { - p1_x = 0; - p1_y = thirds0; - p2_x = width; - p2_y = p1_y; - p3_x = width; - p3_y = thirds1; - }, - - 0x1fb6c, 0x1fb68 => { - p1_x = 0; - p1_y = 0; - p2_x = halfs0; - p2_y = height / 2; - p3_x = 0; - p3_y = height; - }, - - 0x1fb6d, 0x1fb69 => { - p1_x = 0; - p1_y = 0; - p2_x = halfs1; - p2_y = height / 2; - p3_x = width; - p3_y = 0; - }, - - 0x1fb6e, 0x1fb6a => { - p1_x = width; - p1_y = 0; - p2_x = halfs1; - p2_y = height / 2; - p3_x = width; - p3_y = height; - }, - - 0x1fb6f, 0x1fb6b => { - p1_x = 0; - p1_y = height; - p2_x = halfs1; - p2_y = height / 2; - p3_x = width; - p3_y = height; - }, - - else => unreachable, - } - - try canvas.triangle(.{ - .p0 = .{ .x = @floatFromInt(p1_x), .y = @floatFromInt(p1_y) }, - .p1 = .{ .x = @floatFromInt(p2_x), .y = @floatFromInt(p2_y) }, - .p2 = .{ .x = @floatFromInt(p3_x), .y = @floatFromInt(p3_y) }, - }, .on); -} - -fn draw_wedge_triangle_inverted( +fn draw_smooth_mosaic( self: Box, canvas: *font.sprite.Canvas, - cp: u32, + mosaic: SmoothMosaic, ) !void { - try self.draw_wedge_triangle(canvas, cp); - canvas.invert(); -} - -fn draw_wedge_triangle_and_box(self: Box, canvas: *font.sprite.Canvas, cp: u32) !void { - try self.draw_wedge_triangle(canvas, cp); - const y_thirds = self.yThirds(); - const box: font.sprite.Box = switch (cp) { - 0x1fb46, 0x1fb51 => .{ - .p0 = .{ .x = 0, .y = @floatFromInt(y_thirds[1]) }, - .p1 = .{ - .x = @floatFromInt(self.width), - .y = @floatFromInt(self.height), + const top: f64 = 0.0; + const upper: f64 = @floatFromInt(y_thirds[0]); + const lower: f64 = @floatFromInt(y_thirds[1]); + const bottom: f64 = @floatFromInt(self.height); + const left: f64 = 0.0; + const center: f64 = @round(@as(f64, @floatFromInt(self.width)) / 2); + const right: f64 = @floatFromInt(self.width); + + var path = z2d.Path.init(canvas.alloc); + defer path.deinit(); + + if (mosaic.tl) try path.lineTo(left, top); + if (mosaic.ul) try path.lineTo(left, upper); + if (mosaic.ll) try path.lineTo(left, lower); + if (mosaic.bl) try path.lineTo(left, bottom); + if (mosaic.bc) try path.lineTo(center, bottom); + if (mosaic.br) try path.lineTo(right, bottom); + if (mosaic.lr) try path.lineTo(right, lower); + if (mosaic.ur) try path.lineTo(right, upper); + if (mosaic.tr) try path.lineTo(right, top); + if (mosaic.tc) try path.lineTo(center, top); + try path.close(); + + var ctx: z2d.Context = .{ + .surface = canvas.sfc, + .pattern = .{ + .opaque_pattern = .{ + .pixel = .{ .alpha8 = .{ .a = @intFromEnum(Shade.on) } }, }, }, - - 0x1fb5c, 0x1fb67 => .{ - .p0 = .{ .x = 0, .y = 0 }, - .p1 = .{ - .x = @floatFromInt(self.width), - .y = @floatFromInt(y_thirds[0]), - }, - }, - - else => unreachable, }; - canvas.rect(box.rect(), .on); + try ctx.fill(canvas.alloc, path); +} + +fn draw_edge_triangle( + self: Box, + canvas: *font.sprite.Canvas, + comptime edge: Edge, +) !void { + const upper: f64 = 0.0; + const middle: f64 = @round(@as(f64, @floatFromInt(self.height)) / 2); + const lower: f64 = @floatFromInt(self.height); + const left: f64 = 0.0; + const center: f64 = @round(@as(f64, @floatFromInt(self.width)) / 2); + const right: f64 = @floatFromInt(self.width); + + var path = z2d.Path.init(canvas.alloc); + defer path.deinit(); + + const x0, const y0, const x1, const y1 = switch (edge) { + .top => .{ right, upper, left, upper }, + .left => .{ left, upper, left, lower }, + .bottom => .{ left, lower, right, lower }, + .right => .{ right, lower, right, upper }, + }; + + try path.moveTo(center, middle); + try path.lineTo(x0, y0); + try path.lineTo(x1, y1); + try path.close(); + + var ctx: z2d.Context = .{ + .surface = canvas.sfc, + .pattern = .{ + .opaque_pattern = .{ + .pixel = .{ .alpha8 = .{ .a = @intFromEnum(Shade.on) } }, + }, + }, + }; + + try ctx.fill(canvas.alloc, path); } fn draw_light_arc( diff --git a/src/font/sprite/testdata/Box.ppm b/src/font/sprite/testdata/Box.ppm index c21952561269dd20ed23a27cc122b77191d3d959..fecf466d0f2cdd92d8086a29e9576d3e97b0b6e2 100644 GIT binary patch delta 4385 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 WMOt~