diff --git a/src/font/sprite/draw/block.zig b/src/font/sprite/draw/block.zig index 27c6ae516..f7faacea7 100644 --- a/src/font/sprite/draw/block.zig +++ b/src/font/sprite/draw/block.zig @@ -15,6 +15,8 @@ const common = @import("common.zig"); const Shade = common.Shade; const Quads = common.Quads; const Alignment = common.Alignment; +const xHalfs = common.xHalfs; +const yHalfs = common.yHalfs; const rect = common.rect; const font = @import("../../main.zig"); @@ -174,11 +176,11 @@ fn quadrant( canvas: *font.sprite.Canvas, comptime quads: Quads, ) void { - const center_x = metrics.cell_width / 2 + metrics.cell_width % 2; - const center_y = metrics.cell_height / 2 + metrics.cell_height % 2; + const x_halfs = xHalfs(metrics); + const y_halfs = yHalfs(metrics); - if (quads.tl) rect(metrics, canvas, 0, 0, center_x, center_y); - if (quads.tr) rect(metrics, canvas, center_x, 0, metrics.cell_width, center_y); - if (quads.bl) rect(metrics, canvas, 0, center_y, center_x, metrics.cell_height); - if (quads.br) rect(metrics, canvas, center_x, center_y, metrics.cell_width, metrics.cell_height); + if (quads.tl) rect(metrics, canvas, 0, 0, x_halfs[0], y_halfs[0]); + if (quads.tr) rect(metrics, canvas, x_halfs[1], 0, metrics.cell_width, y_halfs[0]); + if (quads.bl) rect(metrics, canvas, 0, y_halfs[1], x_halfs[0], metrics.cell_height); + if (quads.br) rect(metrics, canvas, x_halfs[1], y_halfs[1], metrics.cell_width, metrics.cell_height); } diff --git a/src/font/sprite/draw/common.zig b/src/font/sprite/draw/common.zig index 2f608180e..d10128cdf 100644 --- a/src/font/sprite/draw/common.zig +++ b/src/font/sprite/draw/common.zig @@ -204,6 +204,14 @@ pub fn xHalfs(metrics: font.Metrics) [2]u32 { return .{ half_width, metrics.cell_width - half_width }; } +/// yHalfs[0] should be used as the bottom edge of a top-aligned half. +/// yHalfs[1] should be used as the top edge of a bottom-aligned half. +pub fn yHalfs(metrics: font.Metrics) [2]u32 { + const float_height: f64 = @floatFromInt(metrics.cell_height); + const half_height: u32 = @intFromFloat(@round(0.5 * float_height)); + return .{ half_height, metrics.cell_height - half_height }; +} + /// Use these values as such: /// yThirds[0] bottom edge of the first third. /// yThirds[1] top edge of the second third. diff --git a/src/font/sprite/draw/symbols_for_legacy_computing_supplement.zig b/src/font/sprite/draw/symbols_for_legacy_computing_supplement.zig index 9f7e8815d..9ae92cc72 100644 --- a/src/font/sprite/draw/symbols_for_legacy_computing_supplement.zig +++ b/src/font/sprite/draw/symbols_for_legacy_computing_supplement.zig @@ -61,6 +61,8 @@ const xHalfs = common.xHalfs; const yQuads = common.yQuads; const rect = common.rect; +const box = @import("box.zig"); + const font = @import("../../main.zig"); const octant_min = 0x1cd00; @@ -192,6 +194,135 @@ pub fn draw1CC21_1CC2F( ); } +/// Twelfth and Quarter circle pieces. +/// 饻鞍 饻氨 饻安 饻俺 饻按 饻暗 饻岸 饻胺 饻案 饻肮 饻昂 饻盎 饻凹 饻敖 饻熬 饻翱 +/// +/// 饻鞍饻氨饻安饻俺 +/// 饻按饻暗饻岸饻胺 +/// 饻案饻肮饻昂饻盎 +/// 饻凹饻敖饻熬饻翱 +/// +/// These are actually ellipses, sized to touch +/// the edge of their enclosing set of cells. +pub fn draw1CC30_1CC3F( + cp: u32, + canvas: *font.sprite.Canvas, + width: u32, + height: u32, + metrics: font.Metrics, +) !void { + switch (cp) { + // 饻鞍 UPPER LEFT TWELFTH CIRCLE + 0x1CC30 => try circlePiece(canvas, width, height, metrics, 0, 0, 2, 2), + // 饻氨 UPPER CENTRE LEFT TWELFTH CIRCLE + 0x1CC31 => try circlePiece(canvas, width, height, metrics, 1, 0, 2, 2), + // 饻安 UPPER CENTRE RIGHT TWELFTH CIRCLE + 0x1CC32 => try circlePiece(canvas, width, height, metrics, 2, 0, 2, 2), + // 饻俺 UPPER RIGHT TWELFTH CIRCLE + 0x1CC33 => try circlePiece(canvas, width, height, metrics, 3, 0, 2, 2), + // 饻按 UPPER MIDDLE LEFT TWELFTH CIRCLE + 0x1CC34 => try circlePiece(canvas, width, height, metrics, 0, 1, 2, 2), + // 饻暗 UPPER LEFT QUARTER CIRCLE + 0x1CC35 => try circlePiece(canvas, width, height, metrics, 0, 0, 1, 1), + // 饻岸 UPPER RIGHT QUARTER CIRCLE + 0x1CC36 => try circlePiece(canvas, width, height, metrics, 1, 0, 1, 1), + // 饻胺 UPPER MIDDLE RIGHT TWELFTH CIRCLE + 0x1CC37 => try circlePiece(canvas, width, height, metrics, 3, 1, 2, 2), + // 饻案 LOWER MIDDLE LEFT TWELFTH CIRCLE + 0x1CC38 => try circlePiece(canvas, width, height, metrics, 0, 2, 2, 2), + // 饻肮 LOWER LEFT QUARTER CIRCLE + 0x1CC39 => try circlePiece(canvas, width, height, metrics, 0, 1, 1, 1), + // 饻昂 LOWER RIGHT QUARTER CIRCLE + 0x1CC3A => try circlePiece(canvas, width, height, metrics, 1, 1, 1, 1), + // 饻盎 LOWER MIDDLE RIGHT TWELFTH CIRCLE + 0x1CC3B => try circlePiece(canvas, width, height, metrics, 3, 2, 2, 2), + // 饻凹 LOWER LEFT TWELFTH CIRCLE + 0x1CC3C => try circlePiece(canvas, width, height, metrics, 0, 3, 2, 2), + // 饻敖 LOWER CENTRE LEFT TWELFTH CIRCLE + 0x1CC3D => try circlePiece(canvas, width, height, metrics, 1, 3, 2, 2), + // 饻熬 LOWER CENTRE RIGHT TWELFTH CIRCLE + 0x1CC3E => try circlePiece(canvas, width, height, metrics, 2, 3, 2, 2), + // 饻翱 LOWER RIGHT TWELFTH CIRCLE + 0x1CC3F => try circlePiece(canvas, width, height, metrics, 3, 3, 2, 2), + else => unreachable, + } +} + +/// TODO: These two characters should be easy, but it's not clear how they're +/// meant to align with adjacent cells, what characters they're meant to +/// be used with: +/// - 1CC1F 饻盁 BOX DRAWINGS DOUBLE DIAGONAL UPPER RIGHT TO LOWER LEFT +/// - 1CC20 饻盃 BOX DRAWINGS DOUBLE DIAGONAL UPPER LEFT TO LOWER RIGHT +pub fn draw1CC1B_1CC1E( + cp: u32, + canvas: *font.sprite.Canvas, + width: u32, + height: u32, + metrics: font.Metrics, +) !void { + const w: i32 = @intCast(width); + const h: i32 = @intCast(height); + const t: i32 = @intCast(metrics.box_thickness); + switch (cp) { + // 饻皼 BOX DRAWINGS LIGHT HORIZONTAL AND UPPER RIGHT + 0x1CC1B => { + box.linesChar(metrics, canvas, .{ .left = .light, .right = .light }); + canvas.box(w - t, 0, w, @divFloor(h, 2), .on); + }, + // 饻皽 BOX DRAWINGS LIGHT HORIZONTAL AND LOWER RIGHT + 0x1CC1C => { + box.linesChar(metrics, canvas, .{ .left = .light, .right = .light }); + canvas.box(w - t, @divFloor(h, 2), w, h, .on); + }, + // 饻皾 BOX DRAWINGS LIGHT TOP AND UPPER LEFT + 0x1CC1D => { + canvas.box(0, 0, w, t, .on); + canvas.box(0, 0, t, @divFloor(h, 2), .on); + }, + // 饻盀 BOX DRAWINGS LIGHT BOTTOM AND LOWER LEFT + 0x1CC1E => { + canvas.box(0, h - t, w, h, .on); + canvas.box(0, @divFloor(h, 2), t, h, .on); + }, + else => unreachable, + } +} + +pub fn draw1CE16_1CE19( + cp: u32, + canvas: *font.sprite.Canvas, + width: u32, + height: u32, + metrics: font.Metrics, +) !void { + const w: i32 = @intCast(width); + const h: i32 = @intCast(height); + const t: i32 = @intCast(metrics.box_thickness); + switch (cp) { + // 饻笘 BOX DRAWINGS LIGHT VERTICAL AND TOP RIGHT + 0x1CE16 => { + box.linesChar(metrics, canvas, .{ .up = .light, .down = .light }); + canvas.box(@divFloor(w, 2), 0, w, t, .on); + }, + // 饻笚 BOX DRAWINGS LIGHT VERTICAL AND BOTTOM RIGHT + 0x1CE17 => { + box.linesChar(metrics, canvas, .{ .up = .light, .down = .light }); + canvas.box(@divFloor(w, 2), h - t, w, h, .on); + }, + // 饻笜 BOX DRAWINGS LIGHT VERTICAL AND TOP LEFT + 0x1CE18 => { + box.linesChar(metrics, canvas, .{ .up = .light, .down = .light }); + canvas.box(0, 0, @divFloor(w, 2), t, .on); + }, + // 饻笝 BOX DRAWINGS LIGHT VERTICAL AND BOTTOM LEFT + 0x1CE19 => { + box.linesChar(metrics, canvas, .{ .up = .light, .down = .light }); + canvas.box(0, h - t, @divFloor(w, 2), h, .on); + }, + else => unreachable, + } +} + /// Separated Block Sextants pub fn draw1CE51_1CE8F( cp: u32, @@ -271,3 +402,93 @@ pub fn draw1CE51_1CE8F( .on, ); } + +fn circlePiece( + canvas: *font.sprite.Canvas, + width: u32, + height: u32, + metrics: font.Metrics, + x: f64, + y: f64, + w: f64, + h: f64, +) !void { + // Radius in pixels of the arc we need to draw. + const wdth: f64 = @as(f64, @floatFromInt(width)) * w; + const hght: f64 = @as(f64, @floatFromInt(height)) * h; + + // Position in pixels (rather than cells) for x/y + const xp: f64 = @as(f64, @floatFromInt(width)) * x; + const yp: f64 = @as(f64, @floatFromInt(height)) * y; + + // Set the clip so we don't include anything outside of the cell. + canvas.clip_left = canvas.padding_x; + canvas.clip_right = canvas.padding_x; + canvas.clip_top = canvas.padding_y; + canvas.clip_bottom = canvas.padding_y; + + // Coefficient for approximating a circular arc. + const c: f64 = (std.math.sqrt2 - 1.0) * 4.0 / 3.0; + const cw = c * wdth; + const ch = c * hght; + + const thick: f64 = @floatFromInt(metrics.box_thickness); + const ht = thick * 0.5; + + var path = canvas.staticPath(2); + + if (xp < wdth) { + if (yp < hght) { + // Upper left arc. + path.moveTo(wdth - xp, ht - yp); + path.curveTo( + wdth - cw - xp, + ht - yp, + ht - xp, + hght - ch - yp, + ht - xp, + hght - yp, + ); + } else { + // Lower left arc. + path.moveTo(ht - xp, hght - yp); + path.curveTo( + ht - xp, + hght + ch - yp, + wdth - cw - xp, + hght * 2 - ht - yp, + wdth - xp, + hght * 2 - ht - yp, + ); + } + } else { + if (yp < hght) { + // Upper right arc. + path.moveTo(wdth - xp, ht - yp); + path.curveTo( + wdth + cw - xp, + ht - yp, + wdth * 2 - ht - xp, + hght - ch - yp, + wdth * 2 - ht - xp, + hght - yp, + ); + } else { + // Lower right arc. + path.moveTo(wdth * 2 - ht - xp, hght - yp); + path.curveTo( + wdth * 2 - ht - xp, + hght + ch - yp, + wdth + cw - xp, + hght * 2 - ht - yp, + wdth - xp, + hght * 2 - ht - yp, + ); + } + } + + try canvas.strokePath(path.wrapped_path, .{ + .line_cap_mode = .butt, + .line_width = @floatFromInt(metrics.box_thickness), + }, .on); +} diff --git a/src/font/sprite/testdata/U+1CC00...U+1CCFF-11x21+2.png b/src/font/sprite/testdata/U+1CC00...U+1CCFF-11x21+2.png index 581b0bbf0..e04e7726b 100644 Binary files a/src/font/sprite/testdata/U+1CC00...U+1CCFF-11x21+2.png and b/src/font/sprite/testdata/U+1CC00...U+1CCFF-11x21+2.png differ diff --git a/src/font/sprite/testdata/U+1CC00...U+1CCFF-12x24+3.png b/src/font/sprite/testdata/U+1CC00...U+1CCFF-12x24+3.png index 852fc999b..ce1b1c422 100644 Binary files a/src/font/sprite/testdata/U+1CC00...U+1CCFF-12x24+3.png and b/src/font/sprite/testdata/U+1CC00...U+1CCFF-12x24+3.png differ diff --git a/src/font/sprite/testdata/U+1CC00...U+1CCFF-18x36+4.png b/src/font/sprite/testdata/U+1CC00...U+1CCFF-18x36+4.png index a14051cb5..1c21f688a 100644 Binary files a/src/font/sprite/testdata/U+1CC00...U+1CCFF-18x36+4.png and b/src/font/sprite/testdata/U+1CC00...U+1CCFF-18x36+4.png differ diff --git a/src/font/sprite/testdata/U+1CC00...U+1CCFF-9x17+1.png b/src/font/sprite/testdata/U+1CC00...U+1CCFF-9x17+1.png index ecdb2ce10..459822e63 100644 Binary files a/src/font/sprite/testdata/U+1CC00...U+1CCFF-9x17+1.png and b/src/font/sprite/testdata/U+1CC00...U+1CCFF-9x17+1.png differ diff --git a/src/font/sprite/testdata/U+1CE00...U+1CEFF-11x21+2.png b/src/font/sprite/testdata/U+1CE00...U+1CEFF-11x21+2.png index ed0e83816..03305c81c 100644 Binary files a/src/font/sprite/testdata/U+1CE00...U+1CEFF-11x21+2.png and b/src/font/sprite/testdata/U+1CE00...U+1CEFF-11x21+2.png differ diff --git a/src/font/sprite/testdata/U+1CE00...U+1CEFF-12x24+3.png b/src/font/sprite/testdata/U+1CE00...U+1CEFF-12x24+3.png index bfc788215..c17e08c39 100644 Binary files a/src/font/sprite/testdata/U+1CE00...U+1CEFF-12x24+3.png and b/src/font/sprite/testdata/U+1CE00...U+1CEFF-12x24+3.png differ diff --git a/src/font/sprite/testdata/U+1CE00...U+1CEFF-18x36+4.png b/src/font/sprite/testdata/U+1CE00...U+1CEFF-18x36+4.png index 2dae7ab4a..6f4a004d4 100644 Binary files a/src/font/sprite/testdata/U+1CE00...U+1CEFF-18x36+4.png and b/src/font/sprite/testdata/U+1CE00...U+1CEFF-18x36+4.png differ diff --git a/src/font/sprite/testdata/U+1CE00...U+1CEFF-9x17+1.png b/src/font/sprite/testdata/U+1CE00...U+1CEFF-9x17+1.png index cf8d5afc7..a822c4f58 100644 Binary files a/src/font/sprite/testdata/U+1CE00...U+1CEFF-9x17+1.png and b/src/font/sprite/testdata/U+1CE00...U+1CEFF-9x17+1.png differ diff --git a/src/font/sprite/testdata/U+2500...U+25FF-11x21+2.png b/src/font/sprite/testdata/U+2500...U+25FF-11x21+2.png index 00b508dcb..9ae5b45ff 100644 Binary files a/src/font/sprite/testdata/U+2500...U+25FF-11x21+2.png and b/src/font/sprite/testdata/U+2500...U+25FF-11x21+2.png differ diff --git a/src/font/sprite/testdata/U+2500...U+25FF-9x17+1.png b/src/font/sprite/testdata/U+2500...U+25FF-9x17+1.png index ea100d7f1..cf96b7d15 100644 Binary files a/src/font/sprite/testdata/U+2500...U+25FF-9x17+1.png and b/src/font/sprite/testdata/U+2500...U+25FF-9x17+1.png differ