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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