From ef65b261de06d85e72144a6a256a3811cd67ab76 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 23 Nov 2022 12:53:27 -0800 Subject: [PATCH 01/40] vendor/pixman and basic building --- .gitmodules | 3 + pkg/pixman/build.zig | 137 ++++++++++++++++++++++++++++++++++++ pkg/pixman/main.zig | 5 ++ pkg/pixman/pixman-version.h | 54 ++++++++++++++ vendor/pixman | 1 + 5 files changed, 200 insertions(+) create mode 100644 pkg/pixman/build.zig create mode 100644 pkg/pixman/main.zig create mode 100644 pkg/pixman/pixman-version.h create mode 160000 vendor/pixman diff --git a/.gitmodules b/.gitmodules index eb3e87bf2..7250a6c86 100644 --- a/.gitmodules +++ b/.gitmodules @@ -31,3 +31,6 @@ [submodule "vendor/cimgui"] path = vendor/cimgui url = https://github.com/cimgui/cimgui.git +[submodule "vendor/pixman"] + path = vendor/pixman + url = https://github.com/freedesktop/pixman.git diff --git a/pkg/pixman/build.zig b/pkg/pixman/build.zig new file mode 100644 index 000000000..bb5be82dd --- /dev/null +++ b/pkg/pixman/build.zig @@ -0,0 +1,137 @@ +const std = @import("std"); +const builtin = @import("builtin"); + +/// Directories with our includes. +const root = thisDir() ++ "../../../vendor/pixman/"; +const include_path = root ++ "pixman/"; +const include_path_self = thisDir(); + +pub const include_paths = .{ include_path, include_path_self }; + +pub const pkg = std.build.Pkg{ + .name = "pixman", + .source = .{ .path = thisDir() ++ "/main.zig" }, +}; + +fn thisDir() []const u8 { + return std.fs.path.dirname(@src().file) orelse "."; +} + +pub const Options = struct {}; + +pub fn build(b: *std.build.Builder) !void { + const target = b.standardTargetOptions(.{}); + const mode = b.standardReleaseOptions(); + _ = target; + _ = mode; + + const tests = b.addTestExe("pixman-test", "main.zig"); + _ = try link(b, tests, .{}); + tests.install(); + + const test_step = b.step("test", "Run tests"); + const tests_run = tests.run(); + test_step.dependOn(&tests_run.step); +} + +pub fn link( + b: *std.build.Builder, + step: *std.build.LibExeObjStep, + opt: Options, +) !*std.build.LibExeObjStep { + const lib = try buildPixman(b, step, opt); + step.linkLibrary(lib); + step.addIncludePath(include_path); + step.addIncludePath(include_path_self); + return lib; +} + +pub fn buildPixman( + b: *std.build.Builder, + step: *std.build.LibExeObjStep, + opt: Options, +) !*std.build.LibExeObjStep { + _ = opt; + + const target = step.target; + const lib = b.addStaticLibrary("pixman", null); + lib.setTarget(step.target); + lib.setBuildMode(step.build_mode); + + // Include + lib.addIncludePath(include_path); + lib.addIncludePath(include_path_self); + + // Link + lib.linkLibC(); + if (!target.isWindows()) { + lib.linkSystemLibrary("pthread"); + } + + // Compile + var flags = std.ArrayList([]const u8).init(b.allocator); + defer flags.deinit(); + + try flags.appendSlice(&.{ + "-DHAVE_SIGACTION=1", + "-DHAVE_ALARM=1", + "-DHAVE_MPROTECT=1", + "-DHAVE_GETPAGESIZE=1", + "-DHAVE_MMAP=1", + "-DHAVE_GETISAX=1", + "-DHAVE_GETTIMEOFDAY=1", + + "-DHAVE_FENV_H=1", + "-DHAVE_SYS_MMAN_H=1", + "-DHAVE_UNISTD_H=1", + + "-DSIZEOF_LONG=8", + "-DPACKAGE=foo", + }); + + if (!target.isWindows()) { + try flags.appendSlice(&.{ + "-DHAVE_PTHREADS=1", + + "-DHAVE_POSIX_MEMALIGN=1", + }); + } + + // C files + lib.addCSourceFiles(srcs, flags.items); + + return lib; +} + +const srcs = &.{ + root ++ "pixman/pixman.c", + root ++ "pixman/pixman-access.c", + root ++ "pixman/pixman-access-accessors.c", + root ++ "pixman/pixman-bits-image.c", + root ++ "pixman/pixman-combine32.c", + root ++ "pixman/pixman-combine-float.c", + root ++ "pixman/pixman-conical-gradient.c", + root ++ "pixman/pixman-filter.c", + root ++ "pixman/pixman-x86.c", + root ++ "pixman/pixman-mips.c", + root ++ "pixman/pixman-arm.c", + root ++ "pixman/pixman-ppc.c", + root ++ "pixman/pixman-edge.c", + root ++ "pixman/pixman-edge-accessors.c", + root ++ "pixman/pixman-fast-path.c", + root ++ "pixman/pixman-glyph.c", + root ++ "pixman/pixman-general.c", + root ++ "pixman/pixman-gradient-walker.c", + root ++ "pixman/pixman-image.c", + root ++ "pixman/pixman-implementation.c", + root ++ "pixman/pixman-linear-gradient.c", + root ++ "pixman/pixman-matrix.c", + root ++ "pixman/pixman-noop.c", + root ++ "pixman/pixman-radial-gradient.c", + root ++ "pixman/pixman-region16.c", + root ++ "pixman/pixman-region32.c", + root ++ "pixman/pixman-solid-fill.c", + root ++ "pixman/pixman-timer.c", + root ++ "pixman/pixman-trap.c", + root ++ "pixman/pixman-utils.c", +}; diff --git a/pkg/pixman/main.zig b/pkg/pixman/main.zig new file mode 100644 index 000000000..c12b911fb --- /dev/null +++ b/pkg/pixman/main.zig @@ -0,0 +1,5 @@ +const std = @import("std"); + +test { + std.testing.refAllDecls(@This()); +} diff --git a/pkg/pixman/pixman-version.h b/pkg/pixman/pixman-version.h new file mode 100644 index 000000000..c2342d3d5 --- /dev/null +++ b/pkg/pixman/pixman-version.h @@ -0,0 +1,54 @@ +/* + * Copyright © 2008 Red Hat, Inc. + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Author: Carl D. Worth + */ + +#ifndef PIXMAN_VERSION_H__ +#define PIXMAN_VERSION_H__ + +#ifndef PIXMAN_H__ +# error pixman-version.h should only be included by pixman.h +#endif + +#define PIXMAN_VERSION_MAJOR 999 +#define PIXMAN_VERSION_MINOR 999 +#define PIXMAN_VERSION_MICRO 999 + +#define PIXMAN_VERSION_STRING "999.999.999" + +#define PIXMAN_VERSION_ENCODE(major, minor, micro) ( \ + ((major) * 10000) \ + + ((minor) * 100) \ + + ((micro) * 1)) + +#define PIXMAN_VERSION PIXMAN_VERSION_ENCODE( \ + PIXMAN_VERSION_MAJOR, \ + PIXMAN_VERSION_MINOR, \ + PIXMAN_VERSION_MICRO) + +#ifndef PIXMAN_API +# define PIXMAN_API +#endif + +#endif /* PIXMAN_VERSION_H__ */ diff --git a/vendor/pixman b/vendor/pixman new file mode 160000 index 000000000..713077d0a --- /dev/null +++ b/vendor/pixman @@ -0,0 +1 @@ +Subproject commit 713077d0a3c310ca1955bc331d46d55d0ae4a72b From 699370bd4ab3f429dc35c9b9c877298bd341ad04 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 23 Nov 2022 15:44:54 -0800 Subject: [PATCH 02/40] pkg/pixman: starting API --- build.zig | 5 ++ pkg/pixman/build.zig | 4 +- pkg/pixman/c.zig | 3 ++ pkg/pixman/format.zig | 109 ++++++++++++++++++++++++++++++++++++++++++ pkg/pixman/image.zig | 38 +++++++++++++++ pkg/pixman/main.zig | 3 ++ 6 files changed, 160 insertions(+), 2 deletions(-) create mode 100644 pkg/pixman/c.zig create mode 100644 pkg/pixman/format.zig create mode 100644 pkg/pixman/image.zig diff --git a/build.zig b/build.zig index 24a58d14c..c788e6e2e 100644 --- a/build.zig +++ b/build.zig @@ -12,6 +12,7 @@ const libuv = @import("pkg/libuv/build.zig"); const libpng = @import("pkg/libpng/build.zig"); const macos = @import("pkg/macos/build.zig"); const objc = @import("pkg/objc/build.zig"); +const pixman = @import("pkg/pixman/build.zig"); const stb_image_resize = @import("pkg/stb_image_resize/build.zig"); const utf8proc = @import("pkg/utf8proc/build.zig"); const zlib = @import("pkg/zlib/build.zig"); @@ -307,6 +308,10 @@ fn addDeps( }); system_sdk.include(b, harfbuzz_step, .{}); + // Pixman + const pixman_step = try pixman.link(b, step, .{}); + _ = pixman_step; + // Libuv const libuv_step = try libuv.link(b, step); system_sdk.include(b, libuv_step, .{}); diff --git a/pkg/pixman/build.zig b/pkg/pixman/build.zig index bb5be82dd..2ec6a0da6 100644 --- a/pkg/pixman/build.zig +++ b/pkg/pixman/build.zig @@ -22,10 +22,10 @@ pub const Options = struct {}; pub fn build(b: *std.build.Builder) !void { const target = b.standardTargetOptions(.{}); const mode = b.standardReleaseOptions(); - _ = target; - _ = mode; const tests = b.addTestExe("pixman-test", "main.zig"); + tests.setBuildMode(mode); + tests.setTarget(target); _ = try link(b, tests, .{}); tests.install(); diff --git a/pkg/pixman/c.zig b/pkg/pixman/c.zig new file mode 100644 index 000000000..04972bc40 --- /dev/null +++ b/pkg/pixman/c.zig @@ -0,0 +1,3 @@ +pub usingnamespace @cImport({ + @cInclude("pixman.h"); +}); diff --git a/pkg/pixman/format.zig b/pkg/pixman/format.zig new file mode 100644 index 000000000..ef949509c --- /dev/null +++ b/pkg/pixman/format.zig @@ -0,0 +1,109 @@ +const std = @import("std"); +const c = @import("c.zig"); +const pixman = @import("main.zig"); + +pub const FormatCode = enum(c_uint) { + // 128bpp formats + rgba_float = c.PIXMAN_FORMAT_BYTE(128, c.PIXMAN_TYPE_RGBA_FLOAT, 32, 32, 32, 32), + + // 96bpp formats + rgb_float = c.PIXMAN_FORMAT_BYTE(96, c.PIXMAN_TYPE_RGBA_FLOAT, 0, 32, 32, 32), + + // 32bpp formats + a8r8g8b8 = c.PIXMAN_FORMAT(32, c.PIXMAN_TYPE_ARGB, 8, 8, 8, 8), + x8r8g8b8 = c.PIXMAN_FORMAT(32, c.PIXMAN_TYPE_ARGB, 0, 8, 8, 8), + a8b8g8r8 = c.PIXMAN_FORMAT(32, c.PIXMAN_TYPE_ABGR, 8, 8, 8, 8), + x8b8g8r8 = c.PIXMAN_FORMAT(32, c.PIXMAN_TYPE_ABGR, 0, 8, 8, 8), + b8g8r8a8 = c.PIXMAN_FORMAT(32, c.PIXMAN_TYPE_BGRA, 8, 8, 8, 8), + b8g8r8x8 = c.PIXMAN_FORMAT(32, c.PIXMAN_TYPE_BGRA, 0, 8, 8, 8), + r8g8b8a8 = c.PIXMAN_FORMAT(32, c.PIXMAN_TYPE_RGBA, 8, 8, 8, 8), + r8g8b8x8 = c.PIXMAN_FORMAT(32, c.PIXMAN_TYPE_RGBA, 0, 8, 8, 8), + x14r6g6b6 = c.PIXMAN_FORMAT(32, c.PIXMAN_TYPE_ARGB, 0, 6, 6, 6), + x2r10g10b10 = c.PIXMAN_FORMAT(32, c.PIXMAN_TYPE_ARGB, 0, 10, 10, 10), + a2r10g10b10 = c.PIXMAN_FORMAT(32, c.PIXMAN_TYPE_ARGB, 2, 10, 10, 10), + x2b10g10r10 = c.PIXMAN_FORMAT(32, c.PIXMAN_TYPE_ABGR, 0, 10, 10, 10), + a2b10g10r10 = c.PIXMAN_FORMAT(32, c.PIXMAN_TYPE_ABGR, 2, 10, 10, 10), + + // sRGB formats + a8r8g8b8_sRGB = c.PIXMAN_FORMAT(32, c.PIXMAN_TYPE_ARGB_SRGB, 8, 8, 8, 8), + r8g8b8_sRGB = c.PIXMAN_FORMAT(24, c.PIXMAN_TYPE_ARGB_SRGB, 0, 8, 8, 8), + + // 24bpp formats + r8g8b8 = c.PIXMAN_FORMAT(24, c.PIXMAN_TYPE_ARGB, 0, 8, 8, 8), + b8g8r8 = c.PIXMAN_FORMAT(24, c.PIXMAN_TYPE_ABGR, 0, 8, 8, 8), + + // 16bpp formats + r5g6b5 = c.PIXMAN_FORMAT(16, c.PIXMAN_TYPE_ARGB, 0, 5, 6, 5), + b5g6r5 = c.PIXMAN_FORMAT(16, c.PIXMAN_TYPE_ABGR, 0, 5, 6, 5), + + a1r5g5b5 = c.PIXMAN_FORMAT(16, c.PIXMAN_TYPE_ARGB, 1, 5, 5, 5), + x1r5g5b5 = c.PIXMAN_FORMAT(16, c.PIXMAN_TYPE_ARGB, 0, 5, 5, 5), + a1b5g5r5 = c.PIXMAN_FORMAT(16, c.PIXMAN_TYPE_ABGR, 1, 5, 5, 5), + x1b5g5r5 = c.PIXMAN_FORMAT(16, c.PIXMAN_TYPE_ABGR, 0, 5, 5, 5), + a4r4g4b4 = c.PIXMAN_FORMAT(16, c.PIXMAN_TYPE_ARGB, 4, 4, 4, 4), + x4r4g4b4 = c.PIXMAN_FORMAT(16, c.PIXMAN_TYPE_ARGB, 0, 4, 4, 4), + a4b4g4r4 = c.PIXMAN_FORMAT(16, c.PIXMAN_TYPE_ABGR, 4, 4, 4, 4), + x4b4g4r4 = c.PIXMAN_FORMAT(16, c.PIXMAN_TYPE_ABGR, 0, 4, 4, 4), + + // 8bpp formats + a8 = c.PIXMAN_FORMAT(8, c.PIXMAN_TYPE_A, 8, 0, 0, 0), + r3g3b2 = c.PIXMAN_FORMAT(8, c.PIXMAN_TYPE_ARGB, 0, 3, 3, 2), + b2g3r3 = c.PIXMAN_FORMAT(8, c.PIXMAN_TYPE_ABGR, 0, 3, 3, 2), + a2r2g2b2 = c.PIXMAN_FORMAT(8, c.PIXMAN_TYPE_ARGB, 2, 2, 2, 2), + a2b2g2r2 = c.PIXMAN_FORMAT(8, c.PIXMAN_TYPE_ABGR, 2, 2, 2, 2), + + c8 = c.PIXMAN_FORMAT(8, c.PIXMAN_TYPE_COLOR, 0, 0, 0, 0), + g8 = c.PIXMAN_FORMAT(8, c.PIXMAN_TYPE_GRAY, 0, 0, 0, 0), + + x4a4 = c.PIXMAN_FORMAT(8, c.PIXMAN_TYPE_A, 4, 0, 0, 0), + + // c8/g8 equivalent + // x4c4 = c.PIXMAN_FORMAT(8, c.PIXMAN_TYPE_COLOR, 0, 0, 0, 0), + // x4g4 = c.PIXMAN_FORMAT(8, c.PIXMAN_TYPE_GRAY, 0, 0, 0, 0), + + // 4bpp formats + a4 = c.PIXMAN_FORMAT(4, c.PIXMAN_TYPE_A, 4, 0, 0, 0), + r1g2b1 = c.PIXMAN_FORMAT(4, c.PIXMAN_TYPE_ARGB, 0, 1, 2, 1), + b1g2r1 = c.PIXMAN_FORMAT(4, c.PIXMAN_TYPE_ABGR, 0, 1, 2, 1), + a1r1g1b1 = c.PIXMAN_FORMAT(4, c.PIXMAN_TYPE_ARGB, 1, 1, 1, 1), + a1b1g1r1 = c.PIXMAN_FORMAT(4, c.PIXMAN_TYPE_ABGR, 1, 1, 1, 1), + + c4 = c.PIXMAN_FORMAT(4, c.PIXMAN_TYPE_COLOR, 0, 0, 0, 0), + g4 = c.PIXMAN_FORMAT(4, c.PIXMAN_TYPE_GRAY, 0, 0, 0, 0), + + // 1bpp formats + a1 = c.PIXMAN_FORMAT(1, c.PIXMAN_TYPE_A, 1, 0, 0, 0), + + g1 = c.PIXMAN_FORMAT(1, c.PIXMAN_TYPE_GRAY, 0, 0, 0, 0), + + // YUV formats + yuy2 = c.PIXMAN_FORMAT(16, c.PIXMAN_TYPE_YUY2, 0, 0, 0, 0), + yv12 = c.PIXMAN_FORMAT(12, c.PIXMAN_TYPE_YV12, 0, 0, 0, 0), + + pub inline fn bpp(self: FormatCode) u32 { + return self.reshift(24, 8); + } + + /// Calculates a valid stride for the bpp and width. Based on Cairo. + pub fn strideForWidth(self: FormatCode, width: u32) c_int { + const alignment = @sizeOf(u32); + const val = @intCast(c_int, (self.bpp() * width + 7) / 8); + return val + (alignment - 1) & -alignment; + } + + // Converted from pixman.h + fn reshift(self: FormatCode, ofs: u5, num: u5) u32 { + const val = @enumToInt(self); + const v1 = val >> ofs; + const v2 = @as(c_uint, 1) << num; + const v3 = @intCast(u5, val >> 22); + return ((v1 & (v2 - 1)) << (v3 & 3)); + } +}; + +test "bpp" { + const testing = std.testing; + + try testing.expectEqual(@as(u32, 1), FormatCode.g1.bpp()); + try testing.expectEqual(@as(u32, 4), FormatCode.g4.bpp()); +} diff --git a/pkg/pixman/image.zig b/pkg/pixman/image.zig new file mode 100644 index 000000000..967701e17 --- /dev/null +++ b/pkg/pixman/image.zig @@ -0,0 +1,38 @@ +const std = @import("std"); +const c = @import("c.zig"); +const pixman = @import("main.zig"); + +pub const Image = opaque { + pub inline fn createBitsNoClear( + format: pixman.FormatCode, + width: c_int, + height: c_int, + bits: [*]u32, + stride: c_int, + ) ?*Image { + return @ptrCast(?*Image, c.pixman_image_create_bits_no_clear( + @enumToInt(format), + width, + height, + bits, + stride, + )); + } + + pub inline fn unref(self: *Image) bool { + return c.pixman_image_unref(@ptrCast(*c.pixman_image_t, self)) == 1; + } +}; + +test "create and destroy" { + const testing = std.testing; + + const width = 10; + const height = 10; + const format: pixman.FormatCode = .g1; + const stride = format.strideForWidth(width); + var bits: [width * height]u32 = undefined; + const img = Image.createBitsNoClear(.g1, width, height, &bits, stride); + try testing.expect(img != null); + try testing.expect(img.?.unref()); +} diff --git a/pkg/pixman/main.zig b/pkg/pixman/main.zig index c12b911fb..c14cb5c6c 100644 --- a/pkg/pixman/main.zig +++ b/pkg/pixman/main.zig @@ -1,4 +1,7 @@ const std = @import("std"); +pub const c = @import("c.zig"); +pub usingnamespace @import("format.zig"); +pub usingnamespace @import("image.zig"); test { std.testing.refAllDecls(@This()); From 5f77ac145cf98d88dc870cd07d2528267aaf9bf3 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 23 Nov 2022 16:08:26 -0800 Subject: [PATCH 03/40] pkg/pixman: some drawing --- pkg/pixman/build.zig | 4 +++ pkg/pixman/error.zig | 4 +++ pkg/pixman/image.zig | 66 +++++++++++++++++++++++++++++++++----- pkg/pixman/main.zig | 2 ++ pkg/pixman/types.zig | 76 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 144 insertions(+), 8 deletions(-) create mode 100644 pkg/pixman/error.zig create mode 100644 pkg/pixman/types.zig diff --git a/pkg/pixman/build.zig b/pkg/pixman/build.zig index 2ec6a0da6..6f5d35648 100644 --- a/pkg/pixman/build.zig +++ b/pkg/pixman/build.zig @@ -87,6 +87,10 @@ pub fn buildPixman( "-DSIZEOF_LONG=8", "-DPACKAGE=foo", + + // There is ubsan + "-fno-sanitize=undefined", + "-fno-sanitize-trap=undefined", }); if (!target.isWindows()) { diff --git a/pkg/pixman/error.zig b/pkg/pixman/error.zig new file mode 100644 index 000000000..2fa569aed --- /dev/null +++ b/pkg/pixman/error.zig @@ -0,0 +1,4 @@ +pub const Error = error{ + // Pixman doesn't really have errors so we just have a single error. + PixmanFailure, +}; diff --git a/pkg/pixman/image.zig b/pkg/pixman/image.zig index 967701e17..9dc9d8118 100644 --- a/pkg/pixman/image.zig +++ b/pkg/pixman/image.zig @@ -3,36 +3,86 @@ const c = @import("c.zig"); const pixman = @import("main.zig"); pub const Image = opaque { - pub inline fn createBitsNoClear( + pub fn createBitsNoClear( format: pixman.FormatCode, width: c_int, height: c_int, bits: [*]u32, stride: c_int, - ) ?*Image { + ) pixman.Error!*Image { return @ptrCast(?*Image, c.pixman_image_create_bits_no_clear( @enumToInt(format), width, height, bits, stride, - )); + )) orelse return pixman.Error.PixmanFailure; } - pub inline fn unref(self: *Image) bool { + pub fn unref(self: *Image) bool { return c.pixman_image_unref(@ptrCast(*c.pixman_image_t, self)) == 1; } + + pub fn fillBoxes( + self: *Image, + op: pixman.Op, + color: pixman.Color, + boxes: []const pixman.Box32, + ) pixman.Error!void { + if (c.pixman_image_fill_boxes( + @enumToInt(op), + @ptrCast(*c.pixman_image_t, self), + @ptrCast(*const c.pixman_color_t, &color), + @intCast(c_int, boxes.len), + @ptrCast([*c]const c.pixman_box32_t, boxes.ptr), + ) == 0) return pixman.Error.PixmanFailure; + } }; test "create and destroy" { const testing = std.testing; + const alloc = testing.allocator; const width = 10; const height = 10; const format: pixman.FormatCode = .g1; const stride = format.strideForWidth(width); - var bits: [width * height]u32 = undefined; - const img = Image.createBitsNoClear(.g1, width, height, &bits, stride); - try testing.expect(img != null); - try testing.expect(img.?.unref()); + + const len = height * @intCast(usize, stride); + var data = try alloc.alloc(u32, len); + defer alloc.free(data); + std.mem.set(u32, data, 0); + const img = try Image.createBitsNoClear(.g1, width, height, data.ptr, stride); + try testing.expect(img.unref()); +} + +test "fill boxes" { + const testing = std.testing; + const alloc = testing.allocator; + + // Dimensions + const width = 100; + const height = 100; + const format: pixman.FormatCode = .a1; + const stride = format.strideForWidth(width); + + // Image + const len = height * @intCast(usize, stride); + var data = try alloc.alloc(u32, len); + defer alloc.free(data); + std.mem.set(u32, data, 0); + const img = try Image.createBitsNoClear(format, width, height, data.ptr, stride); + defer _ = img.unref(); + + // Fill + const color: pixman.Color = .{ .red = 0xFFFF, .green = 0xFFFF, .blue = 0xFFFF, .alpha = 0xFFFF }; + const boxes = &[_]pixman.Box32{ + .{ + .x1 = 0, + .y1 = 0, + .x2 = width, + .y2 = height, + }, + }; + try img.fillBoxes(.src, color, boxes); } diff --git a/pkg/pixman/main.zig b/pkg/pixman/main.zig index c14cb5c6c..864736b2c 100644 --- a/pkg/pixman/main.zig +++ b/pkg/pixman/main.zig @@ -1,7 +1,9 @@ const std = @import("std"); pub const c = @import("c.zig"); +pub usingnamespace @import("error.zig"); pub usingnamespace @import("format.zig"); pub usingnamespace @import("image.zig"); +pub usingnamespace @import("types.zig"); test { std.testing.refAllDecls(@This()); diff --git a/pkg/pixman/types.zig b/pkg/pixman/types.zig new file mode 100644 index 000000000..fef250f25 --- /dev/null +++ b/pkg/pixman/types.zig @@ -0,0 +1,76 @@ +const std = @import("std"); +const c = @import("c.zig"); +const pixman = @import("main.zig"); + +pub const Op = enum(c_uint) { + clear = 0x00, + src = 0x01, + dst = 0x02, + over = 0x03, + over_reverse = 0x04, + in = 0x05, + in_reverse = 0x06, + out = 0x07, + out_reverse = 0x08, + atop = 0x09, + atop_reverse = 0x0a, + xor = 0x0b, + add = 0x0c, + saturate = 0x0d, + + disjoint_clear = 0x10, + disjoint_src = 0x11, + disjoint_dst = 0x12, + disjoint_over = 0x13, + disjoint_over_reverse = 0x14, + disjoint_in = 0x15, + disjoint_in_reverse = 0x16, + disjoint_out = 0x17, + disjoint_out_reverse = 0x18, + disjoint_atop = 0x19, + disjoint_atop_reverse = 0x1a, + disjoint_xor = 0x1b, + + conjoint_clear = 0x20, + conjoint_src = 0x21, + conjoint_dst = 0x22, + conjoint_over = 0x23, + conjoint_over_reverse = 0x24, + conjoint_in = 0x25, + conjoint_in_reverse = 0x26, + conjoint_out = 0x27, + conjoint_out_reverse = 0x28, + conjoint_atop = 0x29, + conjoint_atop_reverse = 0x2a, + conjoint_xor = 0x2b, + + multiply = 0x30, + screen = 0x31, + overlay = 0x32, + darken = 0x33, + lighten = 0x34, + color_dodge = 0x35, + color_burn = 0x36, + hard_light = 0x37, + soft_light = 0x38, + difference = 0x39, + exclusion = 0x3a, + hsl_hue = 0x3b, + hsl_saturation = 0x3c, + hsl_color = 0x3d, + hsl_luminosity = 0x3e, +}; + +pub const Color = extern struct { + red: u16, + green: u16, + blue: u16, + alpha: u16, +}; + +pub const Box32 = extern struct { + x1: i32, + y1: i32, + x2: i32, + y2: i32, +}; From 894d36f28abbf86098bdf9a923ea5a166c09629d Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 23 Nov 2022 16:35:36 -0800 Subject: [PATCH 04/40] build: support for dynamic linking pixman --- build.zig | 1 + nix/devshell.nix | 2 ++ 2 files changed, 3 insertions(+) diff --git a/build.zig b/build.zig index c788e6e2e..fb4279c5e 100644 --- a/build.zig +++ b/build.zig @@ -264,6 +264,7 @@ fn addDeps( step.linkSystemLibrary("harfbuzz"); step.linkSystemLibrary("libpng"); step.linkSystemLibrary("libuv"); + step.linkSystemLibrary("pixman-1"); step.linkSystemLibrary("zlib"); if (enable_fontconfig) step.linkSystemLibrary("fontconfig"); diff --git a/nix/devshell.nix b/nix/devshell.nix index ea35aa601..3fe3ff966 100644 --- a/nix/devshell.nix +++ b/nix/devshell.nix @@ -31,6 +31,7 @@ , libXi , libXinerama , libXrandr +, pixman , zlib }: let @@ -87,6 +88,7 @@ in mkShell rec { harfbuzz libpng libuv + pixman zlib libX11 From ac4a45bdf3c407e6befc853b4e64175df4ed1698 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 23 Nov 2022 20:20:52 -0800 Subject: [PATCH 05/40] pkg/pixman: fix math for bpp calculation --- pkg/pixman/format.zig | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkg/pixman/format.zig b/pkg/pixman/format.zig index ef949509c..541446805 100644 --- a/pkg/pixman/format.zig +++ b/pkg/pixman/format.zig @@ -96,8 +96,8 @@ pub const FormatCode = enum(c_uint) { const val = @enumToInt(self); const v1 = val >> ofs; const v2 = @as(c_uint, 1) << num; - const v3 = @intCast(u5, val >> 22); - return ((v1 & (v2 - 1)) << (v3 & 3)); + const v3 = @intCast(u5, (val >> 22) & 3); + return ((v1 & (v2 - 1)) << v3); } }; @@ -106,4 +106,5 @@ test "bpp" { try testing.expectEqual(@as(u32, 1), FormatCode.g1.bpp()); try testing.expectEqual(@as(u32, 4), FormatCode.g4.bpp()); + try testing.expectEqual(@as(u32, 8), FormatCode.g8.bpp()); } From 433d41fa9abeb40e022b39420862f6a884ecd8d1 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 23 Nov 2022 20:56:24 -0800 Subject: [PATCH 06/40] pkg/pixman: add more types --- pkg/pixman/format.zig | 8 ++++++++ pkg/pixman/image.zig | 2 +- pkg/pixman/types.zig | 10 ++++++++++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/pkg/pixman/format.zig b/pkg/pixman/format.zig index 541446805..39dc704f3 100644 --- a/pkg/pixman/format.zig +++ b/pkg/pixman/format.zig @@ -108,3 +108,11 @@ test "bpp" { try testing.expectEqual(@as(u32, 4), FormatCode.g4.bpp()); try testing.expectEqual(@as(u32, 8), FormatCode.g8.bpp()); } + +test "stride" { + const testing = std.testing; + + try testing.expectEqual(@as(c_int, 4), FormatCode.g1.strideForWidth(10)); + try testing.expectEqual(@as(c_int, 8), FormatCode.g4.strideForWidth(10)); + try testing.expectEqual(@as(c_int, 12), FormatCode.g8.strideForWidth(10)); +} diff --git a/pkg/pixman/image.zig b/pkg/pixman/image.zig index 9dc9d8118..dc888e7df 100644 --- a/pkg/pixman/image.zig +++ b/pkg/pixman/image.zig @@ -56,7 +56,7 @@ test "create and destroy" { try testing.expect(img.unref()); } -test "fill boxes" { +test "fill boxes a1" { const testing = std.testing; const alloc = testing.allocator; diff --git a/pkg/pixman/types.zig b/pkg/pixman/types.zig index fef250f25..d3cad88ae 100644 --- a/pkg/pixman/types.zig +++ b/pkg/pixman/types.zig @@ -74,3 +74,13 @@ pub const Box32 = extern struct { x2: i32, y2: i32, }; + +pub const Indexed = extern struct { + color: bool, + rgba: [256]u32, + ent: [32768]u8, +}; + +test { + std.testing.refAllDecls(@This()); +} From 5c58067489645edfcb075a3a7e23e173177ee9d5 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 23 Nov 2022 21:21:31 -0800 Subject: [PATCH 07/40] add pixman package --- build.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/build.zig b/build.zig index fb4279c5e..eba845825 100644 --- a/build.zig +++ b/build.zig @@ -212,6 +212,7 @@ fn addDeps( step.addPackage(imgui.pkg); step.addPackage(glfw.pkg); step.addPackage(libuv.pkg); + step.addPackage(pixman.pkg); step.addPackage(stb_image_resize.pkg); step.addPackage(utf8proc.pkg); From ca97c63a65276f070b100c59bf87a6cadd857fc9 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Wed, 23 Nov 2022 21:31:24 -0800 Subject: [PATCH 08/40] font: detect box glyphs, start littering stuff to denote special fonts --- src/font/Group.zig | 68 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 67 insertions(+), 1 deletion(-) diff --git a/src/font/Group.zig b/src/font/Group.zig index 1d1f168fc..c7ea8bd48 100644 --- a/src/font/Group.zig +++ b/src/font/Group.zig @@ -4,6 +4,10 @@ //! a codepoint doesn't map cleanly. For example, if a user requests a bold //! char and it doesn't exist we can fallback to a regular non-bold char so //! we show SOMETHING. +//! +//! Note this is made specifically for terminals so it has some features +//! that aren't generally helpful, such as detecting and drawing the terminal +//! box glyphs and requiring cell sizes for such glyphs. const Group = @This(); const std = @import("std"); @@ -82,7 +86,12 @@ pub fn deinit(self: *Group) void { /// The group takes ownership of the face. The face will be deallocated when /// the group is deallocated. pub fn addFace(self: *Group, alloc: Allocator, style: Style, face: DeferredFace) !void { - try self.faces.getPtr(style).append(alloc, face); + const list = self.faces.getPtr(style); + + // We have some special indexes so we must never pass those. + if (list.items.len >= FontIndex.special_start - 1) return error.GroupFull; + + try list.append(alloc, face); } /// Resize the fonts to the desired size. @@ -110,6 +119,14 @@ pub const FontIndex = packed struct { const idx_bits = 8 - @typeInfo(@typeInfo(Style).Enum.tag_type).Int.bits; pub const IndexInt = @Type(.{ .Int = .{ .signedness = .unsigned, .bits = idx_bits } }); + /// Special indexes. They start at "special_start" and are guaranteed + /// to not be less than that. + pub const special_start = std.math.maxInt(IndexInt); + + /// Our box drawing font is always specified by box_index. This font + /// is rendered just-in-time using 2D graphics APIs. + pub const box: FontIndex = .{ .idx = special_start }; + style: Style = .regular, idx: IndexInt = 0, @@ -118,6 +135,13 @@ pub const FontIndex = packed struct { return @bitCast(u8, self); } + /// Returns true if this is a "special" index which doesn't map to + /// a real font face. We can still render it but there is no face for + /// this font. + pub fn special(self: FontIndex) bool { + return self.idx >= special_start; + } + test { // We never want to take up more than a byte since font indexes are // everywhere so if we increase the size of this we'll dramatically @@ -142,6 +166,16 @@ pub fn indexForCodepoint( style: Style, p: ?Presentation, ) ?FontIndex { + // If this is a box drawing glyph, we use the special font index. This + // will force special logic where we'll render this ourselves. + if (switch (cp) { + // "Box Drawing" block + 0x2500...0x257F => true, + else => false, + }) { + return FontIndex.box; + } + // If we can find the exact value, then return that. if (self.indexForCodepointExact(cp, style, p)) |value| return value; @@ -190,6 +224,7 @@ fn indexForCodepointExact(self: Group, cp: u32, style: Style, p: ?Presentation) /// Return the Face represented by a given FontIndex. pub fn faceFromIndex(self: Group, index: FontIndex) !Face { + if (index.special()) return error.SpecialHasNoFace; const deferred = &self.faces.get(index.style).items[@intCast(usize, index.idx)]; try deferred.load(self.lib, self.size); return deferred.face.?; @@ -214,6 +249,8 @@ pub fn renderGlyph( glyph_index: u32, max_height: ?u16, ) !Glyph { + // TODO: render special faces here + const face = &self.faces.get(index.style).items[@intCast(usize, index.idx)]; try face.load(self.lib, self.size); return try face.face.?.renderGlyph(alloc, atlas, glyph_index, max_height); @@ -278,6 +315,35 @@ test { } } +test "box glyph" { + const testing = std.testing; + const alloc = testing.allocator; + + var atlas_greyscale = try Atlas.init(alloc, 512, .greyscale); + defer atlas_greyscale.deinit(alloc); + + var lib = try Library.init(); + defer lib.deinit(); + + var group = try init(alloc, lib, .{ .points = 12 }); + defer group.deinit(); + + // Should find a box glyph + const idx = group.indexForCodepoint(0x2500, .regular, null).?; + try testing.expectEqual(Style.regular, idx.style); + try testing.expectEqual(@as(FontIndex.IndexInt, FontIndex.box.idx), idx.idx); + + // Should render it + const glyph = try group.renderGlyph( + alloc, + &atlas_greyscale, + idx, + 0x2500, + null, + ); + _ = glyph; +} + test "resize" { const testing = std.testing; const alloc = testing.allocator; From 4b8b5c5fc1131967a0191926edf5adcbf8434003 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 24 Nov 2022 08:33:32 -0800 Subject: [PATCH 09/40] font: skeleton for box drawing and hook up to Group --- src/font/BoxFont.zig | 168 +++++++++++++++++++++++++++++++++++++++++++ src/font/Group.zig | 72 +++++++++++++------ src/font/main.zig | 1 + 3 files changed, 220 insertions(+), 21 deletions(-) create mode 100644 src/font/BoxFont.zig diff --git a/src/font/BoxFont.zig b/src/font/BoxFont.zig new file mode 100644 index 000000000..9084a7841 --- /dev/null +++ b/src/font/BoxFont.zig @@ -0,0 +1,168 @@ +//! This file contains functions for drawing the box drawing characters +//! (https://en.wikipedia.org/wiki/Box-drawing_character) and related +//! characters that are provided by the terminal. +const BoxFont = @This(); + +const std = @import("std"); +const assert = std.debug.assert; +const Allocator = std.mem.Allocator; + +const pixman = @import("pixman"); +const font = @import("main.zig"); +const Atlas = @import("../Atlas.zig"); + +/// The cell width and height because the boxes are fit perfectly +/// into a cell so that they all properly connect with zero spacing. +width: u32, +height: u32, + +/// Base thickness value for lines of the box. This is in points. +thickness: u32, + +/// We use alpha-channel-only images for the box font so white causes +/// a pixel to be shown. +const white: pixman.Color = .{ + .red = 0xFFFF, + .green = 0xFFFF, + .blue = 0xFFFF, + .alpha = 0xFFFF, +}; + +/// The thickness of a line. +const Thickness = enum { + light, + heavy, + + /// Calculate the real height of a line based on its thickness + /// and a base thickness value. The base thickness value is expected + /// to be in pixels. + fn height(self: Thickness, base: u32) u32 { + return switch (self) { + .light => base, + .heavy => base * 3, + }; + } +}; + +pub fn renderGlyph( + self: BoxFont, + alloc: Allocator, + atlas: *Atlas, + cp: u32, +) !font.Glyph { + assert(atlas.format == .greyscale); + + // TODO: render depending on cp + _ = cp; + + // Determine the config for our image buffer. The images we draw + // for boxes are always 8bpp + const format: pixman.FormatCode = .a8; + const stride = format.strideForWidth(self.width); + const len = @intCast(usize, stride * @intCast(c_int, self.height)); + + // Allocate our buffer + var data = try alloc.alloc(u32, len); + defer alloc.free(data); + std.mem.set(u32, data, 0); + + // Create the image we'll draw to + const img = try pixman.Image.createBitsNoClear( + format, + @intCast(c_int, self.width), + @intCast(c_int, self.height), + data.ptr, + stride, + ); + defer _ = img.unref(); + + self.draw_box_drawings_light_horizontal(img); + + // Reserve our region in the atlas and render the glyph to it. + const region = try atlas.reserve(alloc, self.width, self.height); + if (region.width > 0 and region.height > 0) { + // Convert our []u32 to []u8 since we use 8bpp formats + assert(format.bpp() == 8); + const len_u8 = len * 4; + const data_u8 = @alignCast(@alignOf(u8), @ptrCast([*]u8, data.ptr)[0..len_u8]); + + const depth = atlas.format.depth(); + + // We can avoid a buffer copy if our atlas width and bitmap + // width match and the bitmap pitch is just the width (meaning + // the data is tightly packed). + const needs_copy = !(self.width * depth == stride); + + // If we need to copy the data, we copy it into a temporary buffer. + const buffer = if (needs_copy) buffer: { + var temp = try alloc.alloc(u8, self.width * self.height * depth); + var dst_ptr = temp; + var src_ptr = data_u8.ptr; + var i: usize = 0; + while (i < self.height) : (i += 1) { + std.mem.copy(u8, dst_ptr, src_ptr[0 .. self.width * depth]); + dst_ptr = dst_ptr[self.width * depth ..]; + src_ptr += @intCast(usize, stride); + } + break :buffer temp; + } else data_u8[0..(self.width * self.height * depth)]; + defer if (buffer.ptr != data_u8.ptr) alloc.free(buffer); + + // Write the glyph information into the atlas + assert(region.width == self.width); + assert(region.height == self.height); + atlas.set(region, buffer); + } + + return font.Glyph{ + .width = self.width, + .height = self.height, + .offset_x = 0, + .offset_y = 0, + .atlas_x = region.x, + .atlas_y = region.y, + .advance_x = @intToFloat(f32, self.width), + }; +} + +fn draw_box_drawings_light_horizontal(self: BoxFont, img: *pixman.Image) void { + self.hline_middle(img, .light); +} + +fn hline_middle(self: BoxFont, img: *pixman.Image, thickness: Thickness) void { + const height = thickness.height(self.thickness); + self.hline(img, 0, self.width, (self.height - height) / 2, height); +} + +fn hline( + self: BoxFont, + img: *pixman.Image, + x1: u32, + x2: u32, + y: u32, + thickness_px: u32, +) void { + const boxes = &[_]pixman.Box32{ + .{ + .x1 = @intCast(i32, @min(@max(x1, 0), self.width)), + .x2 = @intCast(i32, @min(@max(x2, 0), self.width)), + .y1 = @intCast(i32, @min(@max(y, 0), self.height)), + .y2 = @intCast(i32, @min(@max(y + thickness_px, 0), self.height)), + }, + }; + + img.fillBoxes(.src, white, boxes) catch {}; +} + +test "all" { + const testing = std.testing; + const alloc = testing.allocator; + + var atlas_greyscale = try Atlas.init(alloc, 512, .greyscale); + defer atlas_greyscale.deinit(alloc); + + const face: BoxFont = .{ .width = 18, .height = 36, .thickness = 2 }; + const glyph = try face.renderGlyph(alloc, &atlas_greyscale, 0x2500); + try testing.expectEqual(@as(u32, face.width), glyph.width); + try testing.expectEqual(@as(u32, face.height), glyph.height); +} diff --git a/src/font/Group.zig b/src/font/Group.zig index c7ea8bd48..6d9766cf8 100644 --- a/src/font/Group.zig +++ b/src/font/Group.zig @@ -47,9 +47,14 @@ size: font.face.DesiredSize, faces: StyleArray, /// If discovery is available, we'll look up fonts where we can't find -/// the codepoint. +/// the codepoint. This can be set after initialization. discover: ?font.Discover = null, +/// Set this to a non-null value to enable box font glyph drawing. If this +/// isn't enabled we'll just fall through to trying to use regular fonts +/// to render box glyphs. +box_font: ?font.BoxFont = null, + pub fn init( alloc: Allocator, lib: Library, @@ -89,7 +94,7 @@ pub fn addFace(self: *Group, alloc: Allocator, style: Style, face: DeferredFace) const list = self.faces.getPtr(style); // We have some special indexes so we must never pass those. - if (list.items.len >= FontIndex.special_start - 1) return error.GroupFull; + if (list.items.len >= FontIndex.Special.start - 1) return error.GroupFull; try list.append(alloc, face); } @@ -119,17 +124,23 @@ pub const FontIndex = packed struct { const idx_bits = 8 - @typeInfo(@typeInfo(Style).Enum.tag_type).Int.bits; pub const IndexInt = @Type(.{ .Int = .{ .signedness = .unsigned, .bits = idx_bits } }); - /// Special indexes. They start at "special_start" and are guaranteed - /// to not be less than that. - pub const special_start = std.math.maxInt(IndexInt); + /// The special-case fonts that we support. + pub const Special = enum(IndexInt) { + // We start all special fonts at this index so they can be detected. + pub const start = std.math.maxInt(IndexInt); - /// Our box drawing font is always specified by box_index. This font - /// is rendered just-in-time using 2D graphics APIs. - pub const box: FontIndex = .{ .idx = special_start }; + /// Box drawing, this is rendered JIT using 2D graphics APIs. + box = start, + }; style: Style = .regular, idx: IndexInt = 0, + /// Initialize a special font index. + pub fn initSpecial(v: Special) FontIndex { + return .{ .style = .regular, .idx = @enumToInt(v) }; + } + /// Convert to int pub fn int(self: FontIndex) u8 { return @bitCast(u8, self); @@ -138,8 +149,9 @@ pub const FontIndex = packed struct { /// Returns true if this is a "special" index which doesn't map to /// a real font face. We can still render it but there is no face for /// this font. - pub fn special(self: FontIndex) bool { - return self.idx >= special_start; + pub fn special(self: FontIndex) ?Special { + if (self.idx < Special.start) return null; + return @intToEnum(Special, self.idx); } test { @@ -167,13 +179,16 @@ pub fn indexForCodepoint( p: ?Presentation, ) ?FontIndex { // If this is a box drawing glyph, we use the special font index. This - // will force special logic where we'll render this ourselves. - if (switch (cp) { - // "Box Drawing" block - 0x2500...0x257F => true, - else => false, - }) { - return FontIndex.box; + // will force special logic where we'll render this ourselves. If we don't + // have a box font set, then we just try to use regular fonts. + if (self.box_font != null) { + if (switch (cp) { + // "Box Drawing" block + 0x2500...0x257F => true, + else => false, + }) { + return FontIndex.initSpecial(.box); + } } // If we can find the exact value, then return that. @@ -224,7 +239,7 @@ fn indexForCodepointExact(self: Group, cp: u32, style: Style, p: ?Presentation) /// Return the Face represented by a given FontIndex. pub fn faceFromIndex(self: Group, index: FontIndex) !Face { - if (index.special()) return error.SpecialHasNoFace; + if (index.special() != null) return error.SpecialHasNoFace; const deferred = &self.faces.get(index.style).items[@intCast(usize, index.idx)]; try deferred.load(self.lib, self.size); return deferred.face.?; @@ -249,7 +264,14 @@ pub fn renderGlyph( glyph_index: u32, max_height: ?u16, ) !Glyph { - // TODO: render special faces here + // Special-case fonts are rendered directly. + if (index.special()) |sp| switch (sp) { + .box => return try self.box_font.?.renderGlyph( + alloc, + atlas, + glyph_index, + ), + }; const face = &self.faces.get(index.style).items[@intCast(usize, index.idx)]; try face.load(self.lib, self.size); @@ -313,6 +335,11 @@ test { try testing.expectEqual(Style.regular, idx.style); try testing.expectEqual(@as(FontIndex.IndexInt, 1), idx.idx); } + + // Box glyph should be null since we didn't set a box font + { + try testing.expect(group.indexForCodepoint(0x1FB00, .regular, null) == null); + } } test "box glyph" { @@ -328,10 +355,13 @@ test "box glyph" { var group = try init(alloc, lib, .{ .points = 12 }); defer group.deinit(); + // Set box font + group.box_font = font.BoxFont{ .width = 18, .height = 36, .thickness = 2 }; + // Should find a box glyph const idx = group.indexForCodepoint(0x2500, .regular, null).?; try testing.expectEqual(Style.regular, idx.style); - try testing.expectEqual(@as(FontIndex.IndexInt, FontIndex.box.idx), idx.idx); + try testing.expectEqual(@enumToInt(FontIndex.Special.box), idx.idx); // Should render it const glyph = try group.renderGlyph( @@ -341,7 +371,7 @@ test "box glyph" { 0x2500, null, ); - _ = glyph; + try testing.expectEqual(@as(u32, 36), glyph.height); } test "resize" { diff --git a/src/font/main.zig b/src/font/main.zig index 69e6a9431..ed21e2f09 100644 --- a/src/font/main.zig +++ b/src/font/main.zig @@ -1,6 +1,7 @@ const std = @import("std"); const build_options = @import("build_options"); +pub const BoxFont = @import("BoxFont.zig"); pub const discovery = @import("discovery.zig"); pub const face = @import("face.zig"); pub const DeferredFace = @import("DeferredFace.zig"); From 9bc8d85d6733821913397a8e0993e9ae7a1bb6a1 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 24 Nov 2022 08:46:33 -0800 Subject: [PATCH 10/40] test rendering box glyphs, looks OKAY --- src/Window.zig | 11 +++++++++++ src/font/Group.zig | 14 +++++++++++++- src/font/GroupCache.zig | 6 ++++-- src/renderer/OpenGL.zig | 6 ++++++ 4 files changed, 34 insertions(+), 3 deletions(-) diff --git a/src/Window.zig b/src/Window.zig index ebe3b67c7..f5243deab 100644 --- a/src/Window.zig +++ b/src/Window.zig @@ -289,6 +289,17 @@ pub fn create(alloc: Allocator, app: *App, config: *const Config) !*Window { // Pre-calculate our initial cell size ourselves. const cell_size = try renderer.CellSize.init(alloc, font_group); + // Setup our box font + font_group.group.box_font = font.BoxFont{ + .width = @floatToInt(u32, cell_size.width), + .height = @floatToInt(u32, cell_size.height), + .thickness = 2, + }; + + // TEST + const idx = (try font_group.indexForCodepoint(alloc, 0x2500, .regular, null)).?; + _ = try font_group.renderGlyph(alloc, idx, 0x2500, null); + // Convert our padding from points to pixels const padding_x = (@intToFloat(f32, config.@"window-padding-x") * x_dpi) / 72; const padding_y = (@intToFloat(f32, config.@"window-padding-y") * y_dpi) / 72; diff --git a/src/font/Group.zig b/src/font/Group.zig index 6d9766cf8..b2cd242ea 100644 --- a/src/font/Group.zig +++ b/src/font/Group.zig @@ -237,7 +237,19 @@ fn indexForCodepointExact(self: Group, cp: u32, style: Style, p: ?Presentation) return null; } -/// Return the Face represented by a given FontIndex. +/// Returns the presentation for a specific font index. This is useful for +/// determining what atlas is needed. +pub fn presentationFromIndex(self: Group, index: FontIndex) !font.Presentation { + if (index.special()) |sp| switch (sp) { + .box => return .text, + }; + + const face = try self.faceFromIndex(index); + return face.presentation; +} + +/// Return the Face represented by a given FontIndex. Note that special +/// fonts (i.e. box glyphs) do not have a face. pub fn faceFromIndex(self: Group, index: FontIndex) !Face { if (index.special() != null) return error.SpecialHasNoFace; const deferred = &self.faces.get(index.style).items[@intCast(usize, index.idx)]; diff --git a/src/font/GroupCache.zig b/src/font/GroupCache.zig index 4ed68f6dc..77b488bc8 100644 --- a/src/font/GroupCache.zig +++ b/src/font/GroupCache.zig @@ -132,8 +132,10 @@ pub fn renderGlyph( if (gop.found_existing) return gop.value_ptr.*; // Uncached, render it - const face = try self.group.faceFromIndex(index); - const atlas: *Atlas = if (face.presentation == .emoji) &self.atlas_color else &self.atlas_greyscale; + const atlas: *Atlas = switch (try self.group.presentationFromIndex(index)) { + .text => &self.atlas_greyscale, + .emoji => &self.atlas_color, + }; const glyph = self.group.renderGlyph( alloc, atlas, diff --git a/src/renderer/OpenGL.zig b/src/renderer/OpenGL.zig index db274a42e..9336c1de7 100644 --- a/src/renderer/OpenGL.zig +++ b/src/renderer/OpenGL.zig @@ -501,6 +501,12 @@ pub fn setFontSize(self: *OpenGL, size: font.face.DesiredSize) !void { if (std.meta.eql(self.cell_size, new_cell_size)) return; self.cell_size = new_cell_size; + // Set the cell size of the box font + if (self.font_group.group.box_font) |*box| { + box.width = @floatToInt(u32, self.cell_size.width); + box.height = @floatToInt(u32, self.cell_size.height); + } + // Notify the window that the cell size changed. _ = self.window_mailbox.push(.{ .cell_size = new_cell_size, From b9f97217ab8a1cc4e139c24a1353dfd25c092f3b Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 24 Nov 2022 08:59:14 -0800 Subject: [PATCH 11/40] font: shaper handles special fonts --- src/font/Shaper.zig | 60 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 53 insertions(+), 7 deletions(-) diff --git a/src/font/Shaper.zig b/src/font/Shaper.zig index dde0a5235..9f2877b2a 100644 --- a/src/font/Shaper.zig +++ b/src/font/Shaper.zig @@ -7,6 +7,7 @@ const Allocator = std.mem.Allocator; const harfbuzz = @import("harfbuzz"); const trace = @import("tracy").trace; const Atlas = @import("../Atlas.zig"); +const font = @import("main.zig"); const Face = @import("main.zig").Face; const DeferredFace = @import("main.zig").DeferredFace; const Group = @import("main.zig").Group; @@ -56,14 +57,18 @@ pub fn shape(self: *Shaper, run: TextRun) ![]Cell { const tracy = trace(@src()); defer tracy.end(); - // TODO: we do not want to hardcode these - const hb_feats = &[_]harfbuzz.Feature{ - harfbuzz.Feature.fromString("dlig").?, - harfbuzz.Feature.fromString("liga").?, - }; + // We only do shaping if the font is not a special-case. For special-case + // fonts, the codepoint == glyph_index so we don't need to run any shaping. + if (run.font_index.special() == null) { + // TODO: we do not want to hardcode these + const hb_feats = &[_]harfbuzz.Feature{ + harfbuzz.Feature.fromString("dlig").?, + harfbuzz.Feature.fromString("liga").?, + }; - const face = try run.group.group.faceFromIndex(run.font_index); - harfbuzz.shape(face.hb_font, self.hb_buf, hb_feats); + const face = try run.group.group.faceFromIndex(run.font_index); + harfbuzz.shape(face.hb_font, self.hb_buf, hb_feats); + } // If our buffer is empty, we short-circuit the rest of the work // return nothing. @@ -569,6 +574,47 @@ test "shape Chinese characters" { try testing.expectEqual(@as(usize, 1), count); } +test "shape box glyphs" { + const testing = std.testing; + const alloc = testing.allocator; + + var testdata = try testShaper(alloc); + defer testdata.deinit(); + + // Setup the box font + testdata.cache.group.box_font = font.BoxFont{ + .width = 18, + .height = 36, + .thickness = 2, + }; + + var buf: [32]u8 = undefined; + var buf_idx: usize = 0; + buf_idx += try std.unicode.utf8Encode(0x2500, buf[buf_idx..]); // horiz line + buf_idx += try std.unicode.utf8Encode(0x2501, buf[buf_idx..]); // + + // Make a screen with some data + var screen = try terminal.Screen.init(alloc, 3, 10, 0); + defer screen.deinit(); + try screen.testWriteString(buf[0..buf_idx]); + + // Get our run iterator + var shaper = testdata.shaper; + var it = shaper.runIterator(testdata.cache, screen.getRow(.{ .screen = 0 })); + var count: usize = 0; + while (try it.next(alloc)) |run| { + count += 1; + try testing.expectEqual(@as(u32, 2), shaper.hb_buf.getLength()); + const cells = try shaper.shape(run); + try testing.expectEqual(@as(usize, 2), cells.len); + try testing.expectEqual(@as(u32, 0x2500), cells[0].glyph_index); + try testing.expectEqual(@as(u16, 0), cells[0].x); + try testing.expectEqual(@as(u32, 0x2501), cells[1].glyph_index); + try testing.expectEqual(@as(u16, 1), cells[1].x); + } + try testing.expectEqual(@as(usize, 1), count); +} + const TestShaper = struct { alloc: Allocator, shaper: Shaper, From 6b2ca86ed83800ec68ea0af15f427d7ac159a9df Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 24 Nov 2022 09:12:34 -0800 Subject: [PATCH 12/40] fix offset for box glyphs --- src/font/BoxFont.zig | 22 ++++++++++++++++++---- src/font/Group.zig | 1 + src/renderer/OpenGL.zig | 8 +++++--- 3 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/font/BoxFont.zig b/src/font/BoxFont.zig index 9084a7841..4dfb4eb2e 100644 --- a/src/font/BoxFont.zig +++ b/src/font/BoxFont.zig @@ -11,6 +11,8 @@ const pixman = @import("pixman"); const font = @import("main.zig"); const Atlas = @import("../Atlas.zig"); +const log = std.log.scoped(.box_font); + /// The cell width and height because the boxes are fit perfectly /// into a cell so that they all properly connect with zero spacing. width: u32, @@ -49,11 +51,13 @@ pub fn renderGlyph( alloc: Allocator, atlas: *Atlas, cp: u32, + font_size: font.face.DesiredSize, ) !font.Glyph { assert(atlas.format == .greyscale); // TODO: render depending on cp _ = cp; + _ = font_size; // Determine the config for our image buffer. The images we draw // for boxes are always 8bpp @@ -114,11 +118,16 @@ pub fn renderGlyph( atlas.set(region, buffer); } + // Our coordinates start at the BOTTOM for our renderers so we have to + // specify an offset of the full height because we rendered a full size + // cell. + const offset_y = @intCast(i32, self.height); + return font.Glyph{ .width = self.width, .height = self.height, .offset_x = 0, - .offset_y = 0, + .offset_y = offset_y, .atlas_x = region.x, .atlas_y = region.y, .advance_x = @intToFloat(f32, self.width), @@ -130,8 +139,8 @@ fn draw_box_drawings_light_horizontal(self: BoxFont, img: *pixman.Image) void { } fn hline_middle(self: BoxFont, img: *pixman.Image, thickness: Thickness) void { - const height = thickness.height(self.thickness); - self.hline(img, 0, self.width, (self.height - height) / 2, height); + const thick_px = thickness.height(self.thickness); + self.hline(img, 0, self.width, (self.height - thick_px) / 2, thick_px); } fn hline( @@ -162,7 +171,12 @@ test "all" { defer atlas_greyscale.deinit(alloc); const face: BoxFont = .{ .width = 18, .height = 36, .thickness = 2 }; - const glyph = try face.renderGlyph(alloc, &atlas_greyscale, 0x2500); + const glyph = try face.renderGlyph( + alloc, + &atlas_greyscale, + 0x2500, + .{ .points = 12 }, + ); try testing.expectEqual(@as(u32, face.width), glyph.width); try testing.expectEqual(@as(u32, face.height), glyph.height); } diff --git a/src/font/Group.zig b/src/font/Group.zig index b2cd242ea..8003f1015 100644 --- a/src/font/Group.zig +++ b/src/font/Group.zig @@ -282,6 +282,7 @@ pub fn renderGlyph( alloc, atlas, glyph_index, + self.size, ), }; diff --git a/src/renderer/OpenGL.zig b/src/renderer/OpenGL.zig index 9336c1de7..d8ee2fa89 100644 --- a/src/renderer/OpenGL.zig +++ b/src/renderer/OpenGL.zig @@ -958,7 +958,6 @@ pub fn updateCell( // If the cell has a character, draw it if (cell.char > 0) { // Render - const face = try self.font_group.group.faceFromIndex(shaper_run.font_index); const glyph = try self.font_group.renderGlyph( self.alloc, shaper_run.font_index, @@ -967,8 +966,11 @@ pub fn updateCell( ); // If we're rendering a color font, we use the color atlas - var mode: GPUCellMode = .fg; - if (face.presentation == .emoji) mode = .fg_color; + const presentation = try self.font_group.group.presentationFromIndex(shaper_run.font_index); + var mode: GPUCellMode = switch (presentation) { + .text => .fg, + .emoji => .fg_color, + }; self.cells.appendAssumeCapacity(.{ .mode = mode, From cb8f55229b718b2f88380c152d06e542b75f05ed Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 24 Nov 2022 09:22:04 -0800 Subject: [PATCH 13/40] remove test code --- src/Window.zig | 4 ---- src/font/BoxFont.zig | 6 ++---- src/font/Group.zig | 1 - 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/Window.zig b/src/Window.zig index f5243deab..1373e3b58 100644 --- a/src/Window.zig +++ b/src/Window.zig @@ -296,10 +296,6 @@ pub fn create(alloc: Allocator, app: *App, config: *const Config) !*Window { .thickness = 2, }; - // TEST - const idx = (try font_group.indexForCodepoint(alloc, 0x2500, .regular, null)).?; - _ = try font_group.renderGlyph(alloc, idx, 0x2500, null); - // Convert our padding from points to pixels const padding_x = (@intToFloat(f32, config.@"window-padding-x") * x_dpi) / 72; const padding_y = (@intToFloat(f32, config.@"window-padding-y") * y_dpi) / 72; diff --git a/src/font/BoxFont.zig b/src/font/BoxFont.zig index 4dfb4eb2e..a4b7a6392 100644 --- a/src/font/BoxFont.zig +++ b/src/font/BoxFont.zig @@ -18,7 +18,8 @@ const log = std.log.scoped(.box_font); width: u32, height: u32, -/// Base thickness value for lines of the box. This is in points. +/// Base thickness value for lines of the box. This is in pixels. If you +/// want to do any DPI scaling, it is expected to be done earlier. thickness: u32, /// We use alpha-channel-only images for the box font so white causes @@ -51,13 +52,11 @@ pub fn renderGlyph( alloc: Allocator, atlas: *Atlas, cp: u32, - font_size: font.face.DesiredSize, ) !font.Glyph { assert(atlas.format == .greyscale); // TODO: render depending on cp _ = cp; - _ = font_size; // Determine the config for our image buffer. The images we draw // for boxes are always 8bpp @@ -175,7 +174,6 @@ test "all" { alloc, &atlas_greyscale, 0x2500, - .{ .points = 12 }, ); try testing.expectEqual(@as(u32, face.width), glyph.width); try testing.expectEqual(@as(u32, face.height), glyph.height); diff --git a/src/font/Group.zig b/src/font/Group.zig index 8003f1015..b2cd242ea 100644 --- a/src/font/Group.zig +++ b/src/font/Group.zig @@ -282,7 +282,6 @@ pub fn renderGlyph( alloc, atlas, glyph_index, - self.size, ), }; From fbd5eba11ab6fc8c7f037033ee9b5d553e4c656d Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 24 Nov 2022 09:31:16 -0800 Subject: [PATCH 14/40] font: more box glyphs --- src/font/BoxFont.zig | 64 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 60 insertions(+), 4 deletions(-) diff --git a/src/font/BoxFont.zig b/src/font/BoxFont.zig index a4b7a6392..4d6fec722 100644 --- a/src/font/BoxFont.zig +++ b/src/font/BoxFont.zig @@ -55,9 +55,6 @@ pub fn renderGlyph( ) !font.Glyph { assert(atlas.format == .greyscale); - // TODO: render depending on cp - _ = cp; - // Determine the config for our image buffer. The images we draw // for boxes are always 8bpp const format: pixman.FormatCode = .a8; @@ -79,7 +76,7 @@ pub fn renderGlyph( ); defer _ = img.unref(); - self.draw_box_drawings_light_horizontal(img); + try self.draw(img, cp); // Reserve our region in the atlas and render the glyph to it. const region = try atlas.reserve(alloc, self.width, self.height); @@ -133,15 +130,74 @@ pub fn renderGlyph( }; } +fn draw(self: BoxFont, img: *pixman.Image, cp: u32) !void { + switch (cp) { + 0x2500 => self.draw_box_drawings_light_horizontal(img), + 0x2501 => self.draw_box_drawings_heavy_horizontal(img), + 0x2502 => self.draw_box_drawings_light_vertical(img), + 0x2503 => self.draw_box_drawings_heavy_vertical(img), + // 0x2504 => self.draw_box_drawings_light_triple_dash_horizontal(img), + // 0x2505 => self.draw_box_drawings_heavy_triple_dash_horizontal(img), + // 0x2506 => self.draw_box_drawings_light_triple_dash_vertical(img), + // 0x2507 => self.draw_box_drawings_heavy_triple_dash_vertical(img), + // 0x2508 => self.draw_box_drawings_light_quadruple_dash_horizontal(img), + // 0x2509 => self.draw_box_drawings_heavy_quadruple_dash_horizontal(img), + // 0x250a => self.draw_box_drawings_light_quadruple_dash_vertical(img), + // 0x250b => self.draw_box_drawings_heavy_quadruple_dash_vertical(img), + // 0x250c => self.draw_box_drawings_light_down_and_right(img), + // 0x250d => self.draw_box_drawings_down_light_and_right_heavy(img), + // 0x250e => self.draw_box_drawings_down_heavy_and_right_light(img), + // 0x250f => self.draw_box_drawings_heavy_down_and_right(img), + else => return error.InvalidCodepoint, + } +} + fn draw_box_drawings_light_horizontal(self: BoxFont, img: *pixman.Image) void { self.hline_middle(img, .light); } +fn draw_box_drawings_heavy_horizontal(self: BoxFont, img: *pixman.Image) void { + self.hline_middle(img, .heavy); +} + +fn draw_box_drawings_light_vertical(self: BoxFont, img: *pixman.Image) void { + self.vline_middle(img, .light); +} + +fn draw_box_drawings_heavy_vertical(self: BoxFont, img: *pixman.Image) void { + self.vline_middle(img, .heavy); +} + +fn vline_middle(self: BoxFont, img: *pixman.Image, thickness: Thickness) void { + const thick_px = thickness.height(self.thickness); + self.vline(img, 0, self.height, (self.width - thick_px) / 2, thick_px); +} + fn hline_middle(self: BoxFont, img: *pixman.Image, thickness: Thickness) void { const thick_px = thickness.height(self.thickness); self.hline(img, 0, self.width, (self.height - thick_px) / 2, thick_px); } +fn vline( + self: BoxFont, + img: *pixman.Image, + y1: u32, + y2: u32, + x: u32, + thickness_px: u32, +) void { + const boxes = &[_]pixman.Box32{ + .{ + .x1 = @intCast(i32, @min(@max(x, 0), self.width)), + .x2 = @intCast(i32, @min(@max(x + thickness_px, 0), self.width)), + .y1 = @intCast(i32, @min(@max(y1, 0), self.height)), + .y2 = @intCast(i32, @min(@max(y2, 0), self.height)), + }, + }; + + img.fillBoxes(.src, white, boxes) catch {}; +} + fn hline( self: BoxFont, img: *pixman.Image, From 6e30238d369d3a4eb093944b09512a5d0364f46c Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 24 Nov 2022 09:49:02 -0800 Subject: [PATCH 15/40] more... more... --- src/font/BoxFont.zig | 176 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 172 insertions(+), 4 deletions(-) diff --git a/src/font/BoxFont.zig b/src/font/BoxFont.zig index 4d6fec722..dc77930ec 100644 --- a/src/font/BoxFont.zig +++ b/src/font/BoxFont.zig @@ -136,10 +136,10 @@ fn draw(self: BoxFont, img: *pixman.Image, cp: u32) !void { 0x2501 => self.draw_box_drawings_heavy_horizontal(img), 0x2502 => self.draw_box_drawings_light_vertical(img), 0x2503 => self.draw_box_drawings_heavy_vertical(img), - // 0x2504 => self.draw_box_drawings_light_triple_dash_horizontal(img), - // 0x2505 => self.draw_box_drawings_heavy_triple_dash_horizontal(img), - // 0x2506 => self.draw_box_drawings_light_triple_dash_vertical(img), - // 0x2507 => self.draw_box_drawings_heavy_triple_dash_vertical(img), + 0x2504 => self.draw_box_drawings_light_triple_dash_horizontal(img), + 0x2505 => self.draw_box_drawings_heavy_triple_dash_horizontal(img), + 0x2506 => self.draw_box_drawings_light_triple_dash_vertical(img), + 0x2507 => self.draw_box_drawings_heavy_triple_dash_vertical(img), // 0x2508 => self.draw_box_drawings_light_quadruple_dash_horizontal(img), // 0x2509 => self.draw_box_drawings_heavy_quadruple_dash_horizontal(img), // 0x250a => self.draw_box_drawings_light_quadruple_dash_vertical(img), @@ -168,6 +168,174 @@ fn draw_box_drawings_heavy_vertical(self: BoxFont, img: *pixman.Image) void { self.vline_middle(img, .heavy); } +fn draw_box_drawings_light_triple_dash_horizontal(self: BoxFont, img: *pixman.Image) void { + self.draw_box_drawings_dash_horizontal( + img, + 3, + Thickness.light.height(self.thickness), + @max(4, Thickness.light.height(self.thickness)), + ); +} + +fn draw_box_drawings_heavy_triple_dash_horizontal(self: BoxFont, img: *pixman.Image) void { + self.draw_box_drawings_dash_horizontal( + img, + 3, + Thickness.heavy.height(self.thickness), + @max(4, Thickness.light.height(self.thickness)), + ); +} + +fn draw_box_drawings_light_triple_dash_vertical(self: BoxFont, img: *pixman.Image) void { + self.draw_box_drawings_dash_vertical( + img, + 3, + Thickness.light.height(self.thickness), + @max(4, Thickness.light.height(self.thickness)), + ); +} + +fn draw_box_drawings_heavy_triple_dash_vertical(self: BoxFont, img: *pixman.Image) void { + self.draw_box_drawings_dash_vertical( + img, + 3, + Thickness.heavy.height(self.thickness), + @max(4, Thickness.light.height(self.thickness)), + ); +} + +fn draw_box_drawings_dash_horizontal( + self: BoxFont, + img: *pixman.Image, + count: u8, + thick_px: u32, + gap: u32, +) void { + assert(count >= 2 and count <= 4); + + // The number of gaps we have is one less than the number of dashes. + // "- - -" => 2 gaps + const gap_count = count - 1; + + // Determine the width of our dashes + const dash_width = dash_width: { + var gap_i = gap; + var dash_width = (self.width - (gap_count * gap_i)) / count; + while (dash_width <= 0 and gap_i > 1) { + gap_i -= 1; + dash_width = (self.width - (gap_count * gap_i)) / count; + } + + // If we can't fit any dashes then we just render a horizontal line. + if (dash_width <= 0) { + self.hline_middle(img, .light); + return; + } + + break :dash_width dash_width; + }; + + // Our total width should be less than our real width + assert(count * dash_width + gap_count * gap <= self.width); + const remaining = self.width - count * dash_width - gap_count * gap; + + var x: [4]u32 = .{0} ** 4; + var w: [4]u32 = .{dash_width} ** 4; + x[1] = x[0] + w[0] + gap; + if (count == 2) + w[1] = self.width - x[1] + else if (count == 3) + w[1] += remaining + else + w[1] += remaining / 2; + + if (count >= 3) { + x[2] = x[1] + w[1] + gap; + if (count == 3) + w[2] = self.width - x[2] + else + w[2] += remaining - remaining / 2; + } + + if (count >= 4) { + x[3] = x[2] + w[2] + gap; + w[3] = self.width - x[3]; + } + + self.hline(img, x[0], x[0] + w[0], (self.height - thick_px) / 2, thick_px); + self.hline(img, x[1], x[1] + w[1], (self.height - thick_px) / 2, thick_px); + if (count >= 3) + self.hline(img, x[2], x[2] + w[2], (self.height - thick_px) / 2, thick_px); + if (count >= 4) + self.hline(img, x[3], x[3] + w[3], (self.height - thick_px) / 2, thick_px); +} + +fn draw_box_drawings_dash_vertical( + self: BoxFont, + img: *pixman.Image, + count: u8, + thick_px: u32, + gap: u32, +) void { + assert(count >= 2 and count <= 4); + + // The number of gaps we have is one less than the number of dashes. + // "- - -" => 2 gaps + const gap_count = count - 1; + + // Determine the height of our dashes + const dash_height = dash_height: { + var gap_i = gap; + var dash_height = (self.height - (gap_count * gap_i)) / count; + while (dash_height <= 0 and gap_i > 1) { + gap_i -= 1; + dash_height = (self.height - (gap_count * gap_i)) / count; + } + + // If we can't fit any dashes then we just render a horizontal line. + if (dash_height <= 0) { + self.vline_middle(img, .light); + return; + } + + break :dash_height dash_height; + }; + + // Our total height should be less than our real height + assert(count * dash_height + gap_count * gap <= self.height); + const remaining = self.height - count * dash_height - gap_count * gap; + + var y: [4]u32 = .{0} ** 4; + var h: [4]u32 = .{dash_height} ** 4; + y[1] = y[0] + h[0] + gap; + if (count == 2) + h[1] = self.height - y[1] + else if (count == 3) + h[1] += remaining + else + h[1] += remaining / 2; + + if (count >= 3) { + y[2] = y[1] + h[1] + gap; + if (count == 3) + h[2] = self.height - y[2] + else + h[2] += remaining - remaining / 2; + } + + if (count >= 4) { + y[3] = y[2] + h[2] + gap; + h[3] = self.height - y[3]; + } + + self.vline(img, y[0], y[0] + h[0], (self.width - thick_px) / 2, thick_px); + self.vline(img, y[1], y[1] + h[1], (self.width - thick_px) / 2, thick_px); + if (count >= 3) + self.vline(img, y[2], y[2] + h[2], (self.width - thick_px) / 2, thick_px); + if (count >= 4) + self.vline(img, y[3], y[3] + h[3], (self.width - thick_px) / 2, thick_px); +} + fn vline_middle(self: BoxFont, img: *pixman.Image, thickness: Thickness) void { const thick_px = thickness.height(self.thickness); self.vline(img, 0, self.height, (self.width - thick_px) / 2, thick_px); From fc8f36a59e535d91dbbaec9c20fe88252ec58a97 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 24 Nov 2022 10:00:57 -0800 Subject: [PATCH 16/40] more glyphs for the glyph god --- src/font/BoxFont.zig | 150 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 141 insertions(+), 9 deletions(-) diff --git a/src/font/BoxFont.zig b/src/font/BoxFont.zig index dc77930ec..1cc4dfbc5 100644 --- a/src/font/BoxFont.zig +++ b/src/font/BoxFont.zig @@ -1,6 +1,14 @@ //! This file contains functions for drawing the box drawing characters //! (https://en.wikipedia.org/wiki/Box-drawing_character) and related //! characters that are provided by the terminal. +//! +//! The box drawing logic is based off similar logic in Kitty and Foot. +//! The primary drawing code was ported directly and slightly modified from Foot +//! (https://codeberg.org/dnkl/foot/). Foot is licensed under the MIT +//! license and is copyright 2019 Daniel Eklöf. +//! +//! The modifications made are primarily around spacing, DPI calculations, +//! and adapting the code to our atlas model. const BoxFont = @This(); const std = @import("std"); @@ -42,7 +50,7 @@ const Thickness = enum { fn height(self: Thickness, base: u32) u32 { return switch (self) { .light => base, - .heavy => base * 3, + .heavy => base * 2, }; } }; @@ -140,14 +148,14 @@ fn draw(self: BoxFont, img: *pixman.Image, cp: u32) !void { 0x2505 => self.draw_box_drawings_heavy_triple_dash_horizontal(img), 0x2506 => self.draw_box_drawings_light_triple_dash_vertical(img), 0x2507 => self.draw_box_drawings_heavy_triple_dash_vertical(img), - // 0x2508 => self.draw_box_drawings_light_quadruple_dash_horizontal(img), - // 0x2509 => self.draw_box_drawings_heavy_quadruple_dash_horizontal(img), - // 0x250a => self.draw_box_drawings_light_quadruple_dash_vertical(img), - // 0x250b => self.draw_box_drawings_heavy_quadruple_dash_vertical(img), - // 0x250c => self.draw_box_drawings_light_down_and_right(img), - // 0x250d => self.draw_box_drawings_down_light_and_right_heavy(img), - // 0x250e => self.draw_box_drawings_down_heavy_and_right_light(img), - // 0x250f => self.draw_box_drawings_heavy_down_and_right(img), + 0x2508 => self.draw_box_drawings_light_quadruple_dash_horizontal(img), + 0x2509 => self.draw_box_drawings_heavy_quadruple_dash_horizontal(img), + 0x250a => self.draw_box_drawings_light_quadruple_dash_vertical(img), + 0x250b => self.draw_box_drawings_heavy_quadruple_dash_vertical(img), + 0x250c => self.draw_box_drawings_light_down_and_right(img), + 0x250d => self.draw_box_drawings_down_light_and_right_heavy(img), + 0x250e => self.draw_box_drawings_down_heavy_and_right_light(img), + 0x250f => self.draw_box_drawings_heavy_down_and_right(img), else => return error.InvalidCodepoint, } } @@ -204,6 +212,62 @@ fn draw_box_drawings_heavy_triple_dash_vertical(self: BoxFont, img: *pixman.Imag ); } +fn draw_box_drawings_light_quadruple_dash_horizontal(self: BoxFont, img: *pixman.Image) void { + self.draw_box_drawings_dash_horizontal( + img, + 4, + Thickness.light.height(self.thickness), + @max(4, Thickness.light.height(self.thickness)), + ); +} + +fn draw_box_drawings_heavy_quadruple_dash_horizontal(self: BoxFont, img: *pixman.Image) void { + self.draw_box_drawings_dash_horizontal( + img, + 4, + Thickness.heavy.height(self.thickness), + @max(4, Thickness.light.height(self.thickness)), + ); +} + +fn draw_box_drawings_light_quadruple_dash_vertical(self: BoxFont, img: *pixman.Image) void { + self.draw_box_drawings_dash_vertical( + img, + 4, + Thickness.light.height(self.thickness), + @max(4, Thickness.light.height(self.thickness)), + ); +} + +fn draw_box_drawings_heavy_quadruple_dash_vertical(self: BoxFont, img: *pixman.Image) void { + self.draw_box_drawings_dash_vertical( + img, + 4, + Thickness.heavy.height(self.thickness), + @max(4, Thickness.light.height(self.thickness)), + ); +} + +fn draw_box_drawings_light_down_and_right(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_right(img, .light, .light); + self.vline_middle_down(img, .light, .light); +} + +fn draw_box_drawings_down_light_and_right_heavy(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_right(img, .light, .heavy); + self.vline_middle_down(img, .light, .light); +} + +fn draw_box_drawings_down_heavy_and_right_light(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_right(img, .light, .light); + self.vline_middle_down(img, .heavy, .light); +} + +fn draw_box_drawings_heavy_down_and_right(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_right(img, .heavy, .heavy); + self.vline_middle_down(img, .heavy, .heavy); +} + fn draw_box_drawings_dash_horizontal( self: BoxFont, img: *pixman.Image, @@ -341,11 +405,79 @@ fn vline_middle(self: BoxFont, img: *pixman.Image, thickness: Thickness) void { self.vline(img, 0, self.height, (self.width - thick_px) / 2, thick_px); } +fn vline_middle_up( + self: BoxFont, + img: *pixman.Image, + vthickness: Thickness, + hthickness: Thickness, +) void { + const hthick_px = hthickness.height(self.thickness); + const vthick_px = vthickness.height(self.thickness); + self.vline( + img, + 0, + (self.height + hthick_px) / 2, + (self.width - vthick_px) / 2, + vthick_px, + ); +} + +fn vline_middle_down( + self: BoxFont, + img: *pixman.Image, + vthickness: Thickness, + hthickness: Thickness, +) void { + const hthick_px = hthickness.height(self.thickness); + const vthick_px = vthickness.height(self.thickness); + self.vline( + img, + (self.height - hthick_px) / 2, + self.height, + (self.width - vthick_px) / 2, + vthick_px, + ); +} + fn hline_middle(self: BoxFont, img: *pixman.Image, thickness: Thickness) void { const thick_px = thickness.height(self.thickness); self.hline(img, 0, self.width, (self.height - thick_px) / 2, thick_px); } +fn hline_middle_left( + self: BoxFont, + img: *pixman.Image, + vthickness: Thickness, + hthickness: Thickness, +) void { + const hthick_px = hthickness.height(self.thickness); + const vthick_px = vthickness.height(self.thickness); + self.hline( + img, + 0, + (self.width + vthick_px) / 2, + (self.height - hthick_px) / 2, + hthick_px, + ); +} + +fn hline_middle_right( + self: BoxFont, + img: *pixman.Image, + vthickness: Thickness, + hthickness: Thickness, +) void { + const hthick_px = hthickness.height(self.thickness); + const vthick_px = vthickness.height(self.thickness); + self.hline( + img, + (self.width - vthick_px) / 2, + self.width, + (self.height - hthick_px) / 2, + hthick_px, + ); +} + fn vline( self: BoxFont, img: *pixman.Image, From 3b3c93af0272fa1989ca850e6b8e6c61735f92fd Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 24 Nov 2022 10:05:25 -0800 Subject: [PATCH 17/40] metal: support for box glyphs --- src/renderer/Metal.zig | 13 +++++++++++-- src/renderer/OpenGL.zig | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/renderer/Metal.zig b/src/renderer/Metal.zig index 0868218db..0112b0926 100644 --- a/src/renderer/Metal.zig +++ b/src/renderer/Metal.zig @@ -408,6 +408,12 @@ pub fn setFontSize(self: *Metal, size: font.face.DesiredSize) !void { if (std.meta.eql(self.cell_size, new_cell_size)) return; self.cell_size = new_cell_size; + // Set the cell size of the box font + if (self.font_group.group.box_font) |*box| { + box.width = @floatToInt(u32, self.cell_size.width); + box.height = @floatToInt(u32, self.cell_size.height); + } + // Notify the window that the cell size changed. _ = self.window_mailbox.push(.{ .cell_size = new_cell_size, @@ -880,8 +886,11 @@ pub fn updateCell( ); // If we're rendering a color font, we use the color atlas - const face = try self.font_group.group.faceFromIndex(shaper_run.font_index); - const mode: GPUCellMode = if (face.presentation == .emoji) .fg_color else .fg; + const presentation = try self.font_group.group.presentationFromIndex(shaper_run.font_index); + const mode: GPUCellMode = switch (presentation) { + .text => .fg, + .emoji => .fg_color, + }; self.cells.appendAssumeCapacity(.{ .mode = mode, diff --git a/src/renderer/OpenGL.zig b/src/renderer/OpenGL.zig index d8ee2fa89..bc40d5207 100644 --- a/src/renderer/OpenGL.zig +++ b/src/renderer/OpenGL.zig @@ -967,7 +967,7 @@ pub fn updateCell( // If we're rendering a color font, we use the color atlas const presentation = try self.font_group.group.presentationFromIndex(shaper_run.font_index); - var mode: GPUCellMode = switch (presentation) { + const mode: GPUCellMode = switch (presentation) { .text => .fg, .emoji => .fg_color, }; From 8033a4a6658e908d12501fb47c77fe9e9184ec88 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 24 Nov 2022 11:16:22 -0800 Subject: [PATCH 18/40] more --- src/font/BoxFont.zig | 100 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/src/font/BoxFont.zig b/src/font/BoxFont.zig index 1cc4dfbc5..38d3e3b84 100644 --- a/src/font/BoxFont.zig +++ b/src/font/BoxFont.zig @@ -156,6 +156,24 @@ fn draw(self: BoxFont, img: *pixman.Image, cp: u32) !void { 0x250d => self.draw_box_drawings_down_light_and_right_heavy(img), 0x250e => self.draw_box_drawings_down_heavy_and_right_light(img), 0x250f => self.draw_box_drawings_heavy_down_and_right(img), + + 0x2510 => self.draw_box_drawings_light_down_and_left(img), + 0x2511 => self.draw_box_drawings_down_light_and_left_heavy(img), + 0x2512 => self.draw_box_drawings_down_heavy_and_left_light(img), + 0x2513 => self.draw_box_drawings_heavy_down_and_left(img), + 0x2514 => self.draw_box_drawings_light_up_and_right(img), + 0x2515 => self.draw_box_drawings_up_light_and_right_heavy(img), + 0x2516 => self.draw_box_drawings_up_heavy_and_right_light(img), + 0x2517 => self.draw_box_drawings_heavy_up_and_right(img), + 0x2518 => self.draw_box_drawings_light_up_and_left(img), + 0x2519 => self.draw_box_drawings_up_light_and_left_heavy(img), + 0x251a => self.draw_box_drawings_up_heavy_and_left_light(img), + 0x251b => self.draw_box_drawings_heavy_up_and_left(img), + 0x251c => self.draw_box_drawings_light_vertical_and_right(img), + 0x251d => self.draw_box_drawings_vertical_light_and_right_heavy(img), + 0x251e => self.draw_box_drawings_up_heavy_and_right_down_light(img), + 0x251f => self.draw_box_drawings_down_heavy_and_right_up_light(img), + else => return error.InvalidCodepoint, } } @@ -268,6 +286,88 @@ fn draw_box_drawings_heavy_down_and_right(self: BoxFont, img: *pixman.Image) voi self.vline_middle_down(img, .heavy, .heavy); } +fn draw_box_drawings_light_down_and_left(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_left(img, .light, .light); + self.vline_middle_down(img, .light, .light); +} + +fn draw_box_drawings_down_light_and_left_heavy(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_left(img, .light, .heavy); + self.vline_middle_down(img, .light, .light); +} + +fn draw_box_drawings_down_heavy_and_left_light(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_left(img, .light, .light); + self.vline_middle_down(img, .heavy, .light); +} + +fn draw_box_drawings_heavy_down_and_left(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_left(img, .heavy, .heavy); + self.vline_middle_down(img, .heavy, .heavy); +} + +fn draw_box_drawings_light_up_and_right(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_right(img, .light, .light); + self.vline_middle_up(img, .light, .light); +} + +fn draw_box_drawings_up_light_and_right_heavy(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_right(img, .light, .heavy); + self.vline_middle_up(img, .light, .light); +} + +fn draw_box_drawings_up_heavy_and_right_light(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_right(img, .light, .light); + self.vline_middle_up(img, .heavy, .light); +} + +fn draw_box_drawings_heavy_up_and_right(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_right(img, .heavy, .heavy); + self.vline_middle_up(img, .heavy, .heavy); +} + +fn draw_box_drawings_light_up_and_left(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_left(img, .light, .light); + self.vline_middle_up(img, .light, .light); +} + +fn draw_box_drawings_up_light_and_left_heavy(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_left(img, .light, .heavy); + self.vline_middle_up(img, .light, .light); +} + +fn draw_box_drawings_up_heavy_and_left_light(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_left(img, .light, .light); + self.vline_middle_up(img, .heavy, .light); +} + +fn draw_box_drawings_heavy_up_and_left(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_left(img, .heavy, .heavy); + self.vline_middle_up(img, .heavy, .heavy); +} + +fn draw_box_drawings_light_vertical_and_right(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_right(img, .light, .light); + self.vline_middle(img, .light); +} + +fn draw_box_drawings_vertical_light_and_right_heavy(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_right(img, .light, .heavy); + self.vline_middle(img, .light); +} + +fn draw_box_drawings_up_heavy_and_right_down_light(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_right(img, .light, .light); + self.vline_middle_up(img, .heavy, .light); + self.vline_middle_down(img, .light, .light); +} + +fn draw_box_drawings_down_heavy_and_right_up_light(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_right(img, .light, .light); + self.vline_middle_up(img, .light, .light); + self.vline_middle_down(img, .heavy, .light); +} + fn draw_box_drawings_dash_horizontal( self: BoxFont, img: *pixman.Image, From d8561ba23a240549adf3b090f048057afd069f44 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 24 Nov 2022 11:27:32 -0800 Subject: [PATCH 19/40] 16 more --- src/font/BoxFont.zig | 105 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 105 insertions(+) diff --git a/src/font/BoxFont.zig b/src/font/BoxFont.zig index 38d3e3b84..29daef188 100644 --- a/src/font/BoxFont.zig +++ b/src/font/BoxFont.zig @@ -174,6 +174,23 @@ fn draw(self: BoxFont, img: *pixman.Image, cp: u32) !void { 0x251e => self.draw_box_drawings_up_heavy_and_right_down_light(img), 0x251f => self.draw_box_drawings_down_heavy_and_right_up_light(img), + 0x2520 => self.draw_box_drawings_vertical_heavy_and_right_light(img), + 0x2521 => self.draw_box_drawings_down_light_and_right_up_heavy(img), + 0x2522 => self.draw_box_drawings_up_light_and_right_down_heavy(img), + 0x2523 => self.draw_box_drawings_heavy_vertical_and_right(img), + 0x2524 => self.draw_box_drawings_light_vertical_and_left(img), + 0x2525 => self.draw_box_drawings_vertical_light_and_left_heavy(img), + 0x2526 => self.draw_box_drawings_up_heavy_and_left_down_light(img), + 0x2527 => self.draw_box_drawings_down_heavy_and_left_up_light(img), + 0x2528 => self.draw_box_drawings_vertical_heavy_and_left_light(img), + 0x2529 => self.draw_box_drawings_down_light_and_left_up_heavy(img), + 0x252a => self.draw_box_drawings_up_light_and_left_down_heavy(img), + 0x252b => self.draw_box_drawings_heavy_vertical_and_left(img), + 0x252c => self.draw_box_drawings_light_down_and_horizontal(img), + 0x252d => self.draw_box_drawings_left_heavy_and_right_down_light(img), + 0x252e => self.draw_box_drawings_right_heavy_and_left_down_light(img), + 0x252f => self.draw_box_drawings_down_light_and_horizontal_heavy(img), + else => return error.InvalidCodepoint, } } @@ -368,6 +385,94 @@ fn draw_box_drawings_down_heavy_and_right_up_light(self: BoxFont, img: *pixman.I self.vline_middle_down(img, .heavy, .light); } +fn draw_box_drawings_vertical_heavy_and_right_light(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_right(img, .light, .light); + self.vline_middle(img, .heavy); +} + +fn draw_box_drawings_down_light_and_right_up_heavy(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_right(img, .heavy, .heavy); + self.vline_middle_up(img, .heavy, .heavy); + self.vline_middle_down(img, .light, .light); +} + +fn draw_box_drawings_up_light_and_right_down_heavy(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_right(img, .heavy, .heavy); + self.vline_middle_up(img, .light, .light); + self.vline_middle_down(img, .heavy, .heavy); +} + +fn draw_box_drawings_heavy_vertical_and_right(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_right(img, .heavy, .heavy); + self.vline_middle(img, .heavy); +} + +fn draw_box_drawings_light_vertical_and_left(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_left(img, .light, .light); + self.vline_middle(img, .light); +} + +fn draw_box_drawings_vertical_light_and_left_heavy(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_left(img, .light, .heavy); + self.vline_middle(img, .light); +} + +fn draw_box_drawings_up_heavy_and_left_down_light(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_left(img, .light, .light); + self.vline_middle_up(img, .heavy, .light); + self.vline_middle_down(img, .light, .light); +} + +fn draw_box_drawings_down_heavy_and_left_up_light(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_left(img, .light, .light); + self.vline_middle_up(img, .light, .light); + self.vline_middle_down(img, .heavy, .light); +} + +fn draw_box_drawings_vertical_heavy_and_left_light(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_left(img, .light, .light); + self.vline_middle(img, .heavy); +} + +fn draw_box_drawings_down_light_and_left_up_heavy(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_left(img, .heavy, .heavy); + self.vline_middle_up(img, .heavy, .heavy); + self.vline_middle_down(img, .light, .light); +} + +fn draw_box_drawings_up_light_and_left_down_heavy(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_left(img, .heavy, .heavy); + self.vline_middle_up(img, .light, .light); + self.vline_middle_down(img, .heavy, .heavy); +} + +fn draw_box_drawings_heavy_vertical_and_left(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_left(img, .heavy, .heavy); + self.vline_middle(img, .heavy); +} + +fn draw_box_drawings_light_down_and_horizontal(self: BoxFont, img: *pixman.Image) void { + self.hline_middle(img, .light); + self.vline_middle_down(img, .light, .light); +} + +fn draw_box_drawings_left_heavy_and_right_down_light(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_left(img, .light, .heavy); + self.hline_middle_right(img, .light, .light); + self.vline_middle_down(img, .light, .light); +} + +fn draw_box_drawings_right_heavy_and_left_down_light(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_left(img, .light, .light); + self.hline_middle_right(img, .light, .heavy); + self.vline_middle_down(img, .light, .light); +} + +fn draw_box_drawings_down_light_and_horizontal_heavy(self: BoxFont, img: *pixman.Image) void { + self.hline_middle(img, .heavy); + self.vline_middle_down(img, .light, .light); +} + fn draw_box_drawings_dash_horizontal( self: BoxFont, img: *pixman.Image, From dcd16c4d9569d7be9e7cd5342fceed643bf4f3a2 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 24 Nov 2022 11:49:17 -0800 Subject: [PATCH 20/40] 16 more --- src/font/BoxFont.zig | 317 ++++++++++++++++++++++++++++--------------- 1 file changed, 211 insertions(+), 106 deletions(-) diff --git a/src/font/BoxFont.zig b/src/font/BoxFont.zig index 29daef188..4a7168c84 100644 --- a/src/font/BoxFont.zig +++ b/src/font/BoxFont.zig @@ -140,79 +140,96 @@ pub fn renderGlyph( fn draw(self: BoxFont, img: *pixman.Image, cp: u32) !void { switch (cp) { - 0x2500 => self.draw_box_drawings_light_horizontal(img), - 0x2501 => self.draw_box_drawings_heavy_horizontal(img), - 0x2502 => self.draw_box_drawings_light_vertical(img), - 0x2503 => self.draw_box_drawings_heavy_vertical(img), - 0x2504 => self.draw_box_drawings_light_triple_dash_horizontal(img), - 0x2505 => self.draw_box_drawings_heavy_triple_dash_horizontal(img), - 0x2506 => self.draw_box_drawings_light_triple_dash_vertical(img), - 0x2507 => self.draw_box_drawings_heavy_triple_dash_vertical(img), - 0x2508 => self.draw_box_drawings_light_quadruple_dash_horizontal(img), - 0x2509 => self.draw_box_drawings_heavy_quadruple_dash_horizontal(img), - 0x250a => self.draw_box_drawings_light_quadruple_dash_vertical(img), - 0x250b => self.draw_box_drawings_heavy_quadruple_dash_vertical(img), - 0x250c => self.draw_box_drawings_light_down_and_right(img), - 0x250d => self.draw_box_drawings_down_light_and_right_heavy(img), - 0x250e => self.draw_box_drawings_down_heavy_and_right_light(img), - 0x250f => self.draw_box_drawings_heavy_down_and_right(img), + 0x2500 => self.draw_light_horizontal(img), + 0x2501 => self.draw_heavy_horizontal(img), + 0x2502 => self.draw_light_vertical(img), + 0x2503 => self.draw_heavy_vertical(img), + 0x2504 => self.draw_light_triple_dash_horizontal(img), + 0x2505 => self.draw_heavy_triple_dash_horizontal(img), + 0x2506 => self.draw_light_triple_dash_vertical(img), + 0x2507 => self.draw_heavy_triple_dash_vertical(img), + 0x2508 => self.draw_light_quadruple_dash_horizontal(img), + 0x2509 => self.draw_heavy_quadruple_dash_horizontal(img), + 0x250a => self.draw_light_quadruple_dash_vertical(img), + 0x250b => self.draw_heavy_quadruple_dash_vertical(img), + 0x250c => self.draw_light_down_and_right(img), + 0x250d => self.draw_down_light_and_right_heavy(img), + 0x250e => self.draw_down_heavy_and_right_light(img), + 0x250f => self.draw_heavy_down_and_right(img), - 0x2510 => self.draw_box_drawings_light_down_and_left(img), - 0x2511 => self.draw_box_drawings_down_light_and_left_heavy(img), - 0x2512 => self.draw_box_drawings_down_heavy_and_left_light(img), - 0x2513 => self.draw_box_drawings_heavy_down_and_left(img), - 0x2514 => self.draw_box_drawings_light_up_and_right(img), - 0x2515 => self.draw_box_drawings_up_light_and_right_heavy(img), - 0x2516 => self.draw_box_drawings_up_heavy_and_right_light(img), - 0x2517 => self.draw_box_drawings_heavy_up_and_right(img), - 0x2518 => self.draw_box_drawings_light_up_and_left(img), - 0x2519 => self.draw_box_drawings_up_light_and_left_heavy(img), - 0x251a => self.draw_box_drawings_up_heavy_and_left_light(img), - 0x251b => self.draw_box_drawings_heavy_up_and_left(img), - 0x251c => self.draw_box_drawings_light_vertical_and_right(img), - 0x251d => self.draw_box_drawings_vertical_light_and_right_heavy(img), - 0x251e => self.draw_box_drawings_up_heavy_and_right_down_light(img), - 0x251f => self.draw_box_drawings_down_heavy_and_right_up_light(img), + 0x2510 => self.draw_light_down_and_left(img), + 0x2511 => self.draw_down_light_and_left_heavy(img), + 0x2512 => self.draw_down_heavy_and_left_light(img), + 0x2513 => self.draw_heavy_down_and_left(img), + 0x2514 => self.draw_light_up_and_right(img), + 0x2515 => self.draw_up_light_and_right_heavy(img), + 0x2516 => self.draw_up_heavy_and_right_light(img), + 0x2517 => self.draw_heavy_up_and_right(img), + 0x2518 => self.draw_light_up_and_left(img), + 0x2519 => self.draw_up_light_and_left_heavy(img), + 0x251a => self.draw_up_heavy_and_left_light(img), + 0x251b => self.draw_heavy_up_and_left(img), + 0x251c => self.draw_light_vertical_and_right(img), + 0x251d => self.draw_vertical_light_and_right_heavy(img), + 0x251e => self.draw_up_heavy_and_right_down_light(img), + 0x251f => self.draw_down_heavy_and_right_up_light(img), - 0x2520 => self.draw_box_drawings_vertical_heavy_and_right_light(img), - 0x2521 => self.draw_box_drawings_down_light_and_right_up_heavy(img), - 0x2522 => self.draw_box_drawings_up_light_and_right_down_heavy(img), - 0x2523 => self.draw_box_drawings_heavy_vertical_and_right(img), - 0x2524 => self.draw_box_drawings_light_vertical_and_left(img), - 0x2525 => self.draw_box_drawings_vertical_light_and_left_heavy(img), - 0x2526 => self.draw_box_drawings_up_heavy_and_left_down_light(img), - 0x2527 => self.draw_box_drawings_down_heavy_and_left_up_light(img), - 0x2528 => self.draw_box_drawings_vertical_heavy_and_left_light(img), - 0x2529 => self.draw_box_drawings_down_light_and_left_up_heavy(img), - 0x252a => self.draw_box_drawings_up_light_and_left_down_heavy(img), - 0x252b => self.draw_box_drawings_heavy_vertical_and_left(img), - 0x252c => self.draw_box_drawings_light_down_and_horizontal(img), - 0x252d => self.draw_box_drawings_left_heavy_and_right_down_light(img), - 0x252e => self.draw_box_drawings_right_heavy_and_left_down_light(img), - 0x252f => self.draw_box_drawings_down_light_and_horizontal_heavy(img), + 0x2520 => self.draw_vertical_heavy_and_right_light(img), + 0x2521 => self.draw_down_light_and_right_up_heavy(img), + 0x2522 => self.draw_up_light_and_right_down_heavy(img), + 0x2523 => self.draw_heavy_vertical_and_right(img), + 0x2524 => self.draw_light_vertical_and_left(img), + 0x2525 => self.draw_vertical_light_and_left_heavy(img), + 0x2526 => self.draw_up_heavy_and_left_down_light(img), + 0x2527 => self.draw_down_heavy_and_left_up_light(img), + 0x2528 => self.draw_vertical_heavy_and_left_light(img), + 0x2529 => self.draw_down_light_and_left_up_heavy(img), + 0x252a => self.draw_up_light_and_left_down_heavy(img), + 0x252b => self.draw_heavy_vertical_and_left(img), + 0x252c => self.draw_light_down_and_horizontal(img), + 0x252d => self.draw_left_heavy_and_right_down_light(img), + 0x252e => self.draw_right_heavy_and_left_down_light(img), + 0x252f => self.draw_down_light_and_horizontal_heavy(img), + + 0x2530 => self.draw_down_heavy_and_horizontal_light(img), + 0x2531 => self.draw_right_light_and_left_down_heavy(img), + 0x2532 => self.draw_left_light_and_right_down_heavy(img), + 0x2533 => self.draw_heavy_down_and_horizontal(img), + 0x2534 => self.draw_light_up_and_horizontal(img), + 0x2535 => self.draw_left_heavy_and_right_up_light(img), + 0x2536 => self.draw_right_heavy_and_left_up_light(img), + 0x2537 => self.draw_up_light_and_horizontal_heavy(img), + 0x2538 => self.draw_up_heavy_and_horizontal_light(img), + 0x2539 => self.draw_right_light_and_left_up_heavy(img), + 0x253a => self.draw_left_light_and_right_up_heavy(img), + 0x253b => self.draw_heavy_up_and_horizontal(img), + 0x253c => self.draw_light_vertical_and_horizontal(img), + 0x253d => self.draw_left_heavy_and_right_vertical_light(img), + 0x253e => self.draw_right_heavy_and_left_vertical_light(img), + 0x253f => self.draw_vertical_light_and_horizontal_heavy(img), else => return error.InvalidCodepoint, } } -fn draw_box_drawings_light_horizontal(self: BoxFont, img: *pixman.Image) void { +fn draw_light_horizontal(self: BoxFont, img: *pixman.Image) void { self.hline_middle(img, .light); } -fn draw_box_drawings_heavy_horizontal(self: BoxFont, img: *pixman.Image) void { +fn draw_heavy_horizontal(self: BoxFont, img: *pixman.Image) void { self.hline_middle(img, .heavy); } -fn draw_box_drawings_light_vertical(self: BoxFont, img: *pixman.Image) void { +fn draw_light_vertical(self: BoxFont, img: *pixman.Image) void { self.vline_middle(img, .light); } -fn draw_box_drawings_heavy_vertical(self: BoxFont, img: *pixman.Image) void { +fn draw_heavy_vertical(self: BoxFont, img: *pixman.Image) void { self.vline_middle(img, .heavy); } -fn draw_box_drawings_light_triple_dash_horizontal(self: BoxFont, img: *pixman.Image) void { - self.draw_box_drawings_dash_horizontal( +fn draw_light_triple_dash_horizontal(self: BoxFont, img: *pixman.Image) void { + self.draw_dash_horizontal( img, 3, Thickness.light.height(self.thickness), @@ -220,8 +237,8 @@ fn draw_box_drawings_light_triple_dash_horizontal(self: BoxFont, img: *pixman.Im ); } -fn draw_box_drawings_heavy_triple_dash_horizontal(self: BoxFont, img: *pixman.Image) void { - self.draw_box_drawings_dash_horizontal( +fn draw_heavy_triple_dash_horizontal(self: BoxFont, img: *pixman.Image) void { + self.draw_dash_horizontal( img, 3, Thickness.heavy.height(self.thickness), @@ -229,8 +246,8 @@ fn draw_box_drawings_heavy_triple_dash_horizontal(self: BoxFont, img: *pixman.Im ); } -fn draw_box_drawings_light_triple_dash_vertical(self: BoxFont, img: *pixman.Image) void { - self.draw_box_drawings_dash_vertical( +fn draw_light_triple_dash_vertical(self: BoxFont, img: *pixman.Image) void { + self.draw_dash_vertical( img, 3, Thickness.light.height(self.thickness), @@ -238,8 +255,8 @@ fn draw_box_drawings_light_triple_dash_vertical(self: BoxFont, img: *pixman.Imag ); } -fn draw_box_drawings_heavy_triple_dash_vertical(self: BoxFont, img: *pixman.Image) void { - self.draw_box_drawings_dash_vertical( +fn draw_heavy_triple_dash_vertical(self: BoxFont, img: *pixman.Image) void { + self.draw_dash_vertical( img, 3, Thickness.heavy.height(self.thickness), @@ -247,8 +264,8 @@ fn draw_box_drawings_heavy_triple_dash_vertical(self: BoxFont, img: *pixman.Imag ); } -fn draw_box_drawings_light_quadruple_dash_horizontal(self: BoxFont, img: *pixman.Image) void { - self.draw_box_drawings_dash_horizontal( +fn draw_light_quadruple_dash_horizontal(self: BoxFont, img: *pixman.Image) void { + self.draw_dash_horizontal( img, 4, Thickness.light.height(self.thickness), @@ -256,8 +273,8 @@ fn draw_box_drawings_light_quadruple_dash_horizontal(self: BoxFont, img: *pixman ); } -fn draw_box_drawings_heavy_quadruple_dash_horizontal(self: BoxFont, img: *pixman.Image) void { - self.draw_box_drawings_dash_horizontal( +fn draw_heavy_quadruple_dash_horizontal(self: BoxFont, img: *pixman.Image) void { + self.draw_dash_horizontal( img, 4, Thickness.heavy.height(self.thickness), @@ -265,8 +282,8 @@ fn draw_box_drawings_heavy_quadruple_dash_horizontal(self: BoxFont, img: *pixman ); } -fn draw_box_drawings_light_quadruple_dash_vertical(self: BoxFont, img: *pixman.Image) void { - self.draw_box_drawings_dash_vertical( +fn draw_light_quadruple_dash_vertical(self: BoxFont, img: *pixman.Image) void { + self.draw_dash_vertical( img, 4, Thickness.light.height(self.thickness), @@ -274,8 +291,8 @@ fn draw_box_drawings_light_quadruple_dash_vertical(self: BoxFont, img: *pixman.I ); } -fn draw_box_drawings_heavy_quadruple_dash_vertical(self: BoxFont, img: *pixman.Image) void { - self.draw_box_drawings_dash_vertical( +fn draw_heavy_quadruple_dash_vertical(self: BoxFont, img: *pixman.Image) void { + self.draw_dash_vertical( img, 4, Thickness.heavy.height(self.thickness), @@ -283,197 +300,285 @@ fn draw_box_drawings_heavy_quadruple_dash_vertical(self: BoxFont, img: *pixman.I ); } -fn draw_box_drawings_light_down_and_right(self: BoxFont, img: *pixman.Image) void { +fn draw_light_down_and_right(self: BoxFont, img: *pixman.Image) void { self.hline_middle_right(img, .light, .light); self.vline_middle_down(img, .light, .light); } -fn draw_box_drawings_down_light_and_right_heavy(self: BoxFont, img: *pixman.Image) void { +fn draw_down_light_and_right_heavy(self: BoxFont, img: *pixman.Image) void { self.hline_middle_right(img, .light, .heavy); self.vline_middle_down(img, .light, .light); } -fn draw_box_drawings_down_heavy_and_right_light(self: BoxFont, img: *pixman.Image) void { +fn draw_down_heavy_and_right_light(self: BoxFont, img: *pixman.Image) void { self.hline_middle_right(img, .light, .light); self.vline_middle_down(img, .heavy, .light); } -fn draw_box_drawings_heavy_down_and_right(self: BoxFont, img: *pixman.Image) void { +fn draw_heavy_down_and_right(self: BoxFont, img: *pixman.Image) void { self.hline_middle_right(img, .heavy, .heavy); self.vline_middle_down(img, .heavy, .heavy); } -fn draw_box_drawings_light_down_and_left(self: BoxFont, img: *pixman.Image) void { +fn draw_light_down_and_left(self: BoxFont, img: *pixman.Image) void { self.hline_middle_left(img, .light, .light); self.vline_middle_down(img, .light, .light); } -fn draw_box_drawings_down_light_and_left_heavy(self: BoxFont, img: *pixman.Image) void { +fn draw_down_light_and_left_heavy(self: BoxFont, img: *pixman.Image) void { self.hline_middle_left(img, .light, .heavy); self.vline_middle_down(img, .light, .light); } -fn draw_box_drawings_down_heavy_and_left_light(self: BoxFont, img: *pixman.Image) void { +fn draw_down_heavy_and_left_light(self: BoxFont, img: *pixman.Image) void { self.hline_middle_left(img, .light, .light); self.vline_middle_down(img, .heavy, .light); } -fn draw_box_drawings_heavy_down_and_left(self: BoxFont, img: *pixman.Image) void { +fn draw_heavy_down_and_left(self: BoxFont, img: *pixman.Image) void { self.hline_middle_left(img, .heavy, .heavy); self.vline_middle_down(img, .heavy, .heavy); } -fn draw_box_drawings_light_up_and_right(self: BoxFont, img: *pixman.Image) void { +fn draw_light_up_and_right(self: BoxFont, img: *pixman.Image) void { self.hline_middle_right(img, .light, .light); self.vline_middle_up(img, .light, .light); } -fn draw_box_drawings_up_light_and_right_heavy(self: BoxFont, img: *pixman.Image) void { +fn draw_up_light_and_right_heavy(self: BoxFont, img: *pixman.Image) void { self.hline_middle_right(img, .light, .heavy); self.vline_middle_up(img, .light, .light); } -fn draw_box_drawings_up_heavy_and_right_light(self: BoxFont, img: *pixman.Image) void { +fn draw_up_heavy_and_right_light(self: BoxFont, img: *pixman.Image) void { self.hline_middle_right(img, .light, .light); self.vline_middle_up(img, .heavy, .light); } -fn draw_box_drawings_heavy_up_and_right(self: BoxFont, img: *pixman.Image) void { +fn draw_heavy_up_and_right(self: BoxFont, img: *pixman.Image) void { self.hline_middle_right(img, .heavy, .heavy); self.vline_middle_up(img, .heavy, .heavy); } -fn draw_box_drawings_light_up_and_left(self: BoxFont, img: *pixman.Image) void { +fn draw_light_up_and_left(self: BoxFont, img: *pixman.Image) void { self.hline_middle_left(img, .light, .light); self.vline_middle_up(img, .light, .light); } -fn draw_box_drawings_up_light_and_left_heavy(self: BoxFont, img: *pixman.Image) void { +fn draw_up_light_and_left_heavy(self: BoxFont, img: *pixman.Image) void { self.hline_middle_left(img, .light, .heavy); self.vline_middle_up(img, .light, .light); } -fn draw_box_drawings_up_heavy_and_left_light(self: BoxFont, img: *pixman.Image) void { +fn draw_up_heavy_and_left_light(self: BoxFont, img: *pixman.Image) void { self.hline_middle_left(img, .light, .light); self.vline_middle_up(img, .heavy, .light); } -fn draw_box_drawings_heavy_up_and_left(self: BoxFont, img: *pixman.Image) void { +fn draw_heavy_up_and_left(self: BoxFont, img: *pixman.Image) void { self.hline_middle_left(img, .heavy, .heavy); self.vline_middle_up(img, .heavy, .heavy); } -fn draw_box_drawings_light_vertical_and_right(self: BoxFont, img: *pixman.Image) void { +fn draw_light_vertical_and_right(self: BoxFont, img: *pixman.Image) void { self.hline_middle_right(img, .light, .light); self.vline_middle(img, .light); } -fn draw_box_drawings_vertical_light_and_right_heavy(self: BoxFont, img: *pixman.Image) void { +fn draw_vertical_light_and_right_heavy(self: BoxFont, img: *pixman.Image) void { self.hline_middle_right(img, .light, .heavy); self.vline_middle(img, .light); } -fn draw_box_drawings_up_heavy_and_right_down_light(self: BoxFont, img: *pixman.Image) void { +fn draw_up_heavy_and_right_down_light(self: BoxFont, img: *pixman.Image) void { self.hline_middle_right(img, .light, .light); self.vline_middle_up(img, .heavy, .light); self.vline_middle_down(img, .light, .light); } -fn draw_box_drawings_down_heavy_and_right_up_light(self: BoxFont, img: *pixman.Image) void { +fn draw_down_heavy_and_right_up_light(self: BoxFont, img: *pixman.Image) void { self.hline_middle_right(img, .light, .light); self.vline_middle_up(img, .light, .light); self.vline_middle_down(img, .heavy, .light); } -fn draw_box_drawings_vertical_heavy_and_right_light(self: BoxFont, img: *pixman.Image) void { +fn draw_vertical_heavy_and_right_light(self: BoxFont, img: *pixman.Image) void { self.hline_middle_right(img, .light, .light); self.vline_middle(img, .heavy); } -fn draw_box_drawings_down_light_and_right_up_heavy(self: BoxFont, img: *pixman.Image) void { +fn draw_down_light_and_right_up_heavy(self: BoxFont, img: *pixman.Image) void { self.hline_middle_right(img, .heavy, .heavy); self.vline_middle_up(img, .heavy, .heavy); self.vline_middle_down(img, .light, .light); } -fn draw_box_drawings_up_light_and_right_down_heavy(self: BoxFont, img: *pixman.Image) void { +fn draw_up_light_and_right_down_heavy(self: BoxFont, img: *pixman.Image) void { self.hline_middle_right(img, .heavy, .heavy); self.vline_middle_up(img, .light, .light); self.vline_middle_down(img, .heavy, .heavy); } -fn draw_box_drawings_heavy_vertical_and_right(self: BoxFont, img: *pixman.Image) void { +fn draw_heavy_vertical_and_right(self: BoxFont, img: *pixman.Image) void { self.hline_middle_right(img, .heavy, .heavy); self.vline_middle(img, .heavy); } -fn draw_box_drawings_light_vertical_and_left(self: BoxFont, img: *pixman.Image) void { +fn draw_light_vertical_and_left(self: BoxFont, img: *pixman.Image) void { self.hline_middle_left(img, .light, .light); self.vline_middle(img, .light); } -fn draw_box_drawings_vertical_light_and_left_heavy(self: BoxFont, img: *pixman.Image) void { +fn draw_vertical_light_and_left_heavy(self: BoxFont, img: *pixman.Image) void { self.hline_middle_left(img, .light, .heavy); self.vline_middle(img, .light); } -fn draw_box_drawings_up_heavy_and_left_down_light(self: BoxFont, img: *pixman.Image) void { +fn draw_up_heavy_and_left_down_light(self: BoxFont, img: *pixman.Image) void { self.hline_middle_left(img, .light, .light); self.vline_middle_up(img, .heavy, .light); self.vline_middle_down(img, .light, .light); } -fn draw_box_drawings_down_heavy_and_left_up_light(self: BoxFont, img: *pixman.Image) void { +fn draw_down_heavy_and_left_up_light(self: BoxFont, img: *pixman.Image) void { self.hline_middle_left(img, .light, .light); self.vline_middle_up(img, .light, .light); self.vline_middle_down(img, .heavy, .light); } -fn draw_box_drawings_vertical_heavy_and_left_light(self: BoxFont, img: *pixman.Image) void { +fn draw_vertical_heavy_and_left_light(self: BoxFont, img: *pixman.Image) void { self.hline_middle_left(img, .light, .light); self.vline_middle(img, .heavy); } -fn draw_box_drawings_down_light_and_left_up_heavy(self: BoxFont, img: *pixman.Image) void { +fn draw_down_light_and_left_up_heavy(self: BoxFont, img: *pixman.Image) void { self.hline_middle_left(img, .heavy, .heavy); self.vline_middle_up(img, .heavy, .heavy); self.vline_middle_down(img, .light, .light); } -fn draw_box_drawings_up_light_and_left_down_heavy(self: BoxFont, img: *pixman.Image) void { +fn draw_up_light_and_left_down_heavy(self: BoxFont, img: *pixman.Image) void { self.hline_middle_left(img, .heavy, .heavy); self.vline_middle_up(img, .light, .light); self.vline_middle_down(img, .heavy, .heavy); } -fn draw_box_drawings_heavy_vertical_and_left(self: BoxFont, img: *pixman.Image) void { +fn draw_heavy_vertical_and_left(self: BoxFont, img: *pixman.Image) void { self.hline_middle_left(img, .heavy, .heavy); self.vline_middle(img, .heavy); } -fn draw_box_drawings_light_down_and_horizontal(self: BoxFont, img: *pixman.Image) void { +fn draw_light_down_and_horizontal(self: BoxFont, img: *pixman.Image) void { self.hline_middle(img, .light); self.vline_middle_down(img, .light, .light); } -fn draw_box_drawings_left_heavy_and_right_down_light(self: BoxFont, img: *pixman.Image) void { +fn draw_left_heavy_and_right_down_light(self: BoxFont, img: *pixman.Image) void { self.hline_middle_left(img, .light, .heavy); self.hline_middle_right(img, .light, .light); self.vline_middle_down(img, .light, .light); } -fn draw_box_drawings_right_heavy_and_left_down_light(self: BoxFont, img: *pixman.Image) void { +fn draw_right_heavy_and_left_down_light(self: BoxFont, img: *pixman.Image) void { self.hline_middle_left(img, .light, .light); self.hline_middle_right(img, .light, .heavy); self.vline_middle_down(img, .light, .light); } -fn draw_box_drawings_down_light_and_horizontal_heavy(self: BoxFont, img: *pixman.Image) void { +fn draw_down_light_and_horizontal_heavy(self: BoxFont, img: *pixman.Image) void { self.hline_middle(img, .heavy); self.vline_middle_down(img, .light, .light); } -fn draw_box_drawings_dash_horizontal( +fn draw_down_heavy_and_horizontal_light(self: BoxFont, img: *pixman.Image) void { + self.hline_middle(img, .light); + self.vline_middle_down(img, .heavy, .light); +} + +fn draw_right_light_and_left_down_heavy(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_left(img, .heavy, .heavy); + self.hline_middle_right(img, .light, .light); + self.vline_middle_down(img, .heavy, .heavy); +} + +fn draw_left_light_and_right_down_heavy(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_left(img, .light, .light); + self.hline_middle_right(img, .heavy, .heavy); + self.vline_middle_down(img, .heavy, .heavy); +} + +fn draw_heavy_down_and_horizontal(self: BoxFont, img: *pixman.Image) void { + self.hline_middle(img, .heavy); + self.vline_middle_down(img, .heavy, .heavy); +} + +fn draw_light_up_and_horizontal(self: BoxFont, img: *pixman.Image) void { + self.hline_middle(img, .light); + self.vline_middle_up(img, .light, .light); +} + +fn draw_left_heavy_and_right_up_light(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_left(img, .light, .heavy); + self.hline_middle_right(img, .light, .light); + self.vline_middle_up(img, .light, .light); +} + +fn draw_right_heavy_and_left_up_light(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_left(img, .light, .light); + self.hline_middle_right(img, .light, .heavy); + self.vline_middle_up(img, .light, .light); +} + +fn draw_up_light_and_horizontal_heavy(self: BoxFont, img: *pixman.Image) void { + self.hline_middle(img, .light); + self.vline_middle_up(img, .light, .light); +} + +fn draw_up_heavy_and_horizontal_light(self: BoxFont, img: *pixman.Image) void { + self.hline_middle(img, .light); + self.vline_middle_up(img, .heavy, .light); +} + +fn draw_right_light_and_left_up_heavy(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_left(img, .heavy, .heavy); + self.hline_middle_right(img, .light, .light); + self.vline_middle_up(img, .heavy, .heavy); +} + +fn draw_left_light_and_right_up_heavy(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_left(img, .light, .light); + self.hline_middle_right(img, .heavy, .heavy); + self.vline_middle_up(img, .heavy, .heavy); +} + +fn draw_heavy_up_and_horizontal(self: BoxFont, img: *pixman.Image) void { + self.hline_middle(img, .heavy); + self.vline_middle_up(img, .heavy, .heavy); +} + +fn draw_light_vertical_and_horizontal(self: BoxFont, img: *pixman.Image) void { + self.hline_middle(img, .light); + self.vline_middle(img, .light); +} + +fn draw_left_heavy_and_right_vertical_light(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_left(img, .light, .heavy); + self.hline_middle_right(img, .light, .light); + self.vline_middle(img, .light); +} + +fn draw_right_heavy_and_left_vertical_light(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_left(img, .light, .light); + self.hline_middle_right(img, .light, .heavy); + self.vline_middle(img, .light); +} + +fn draw_vertical_light_and_horizontal_heavy(self: BoxFont, img: *pixman.Image) void { + self.hline_middle(img, .heavy); + self.vline_middle(img, .light); +} + +fn draw_dash_horizontal( self: BoxFont, img: *pixman.Image, count: u8, @@ -539,7 +644,7 @@ fn draw_box_drawings_dash_horizontal( self.hline(img, x[3], x[3] + w[3], (self.height - thick_px) / 2, thick_px); } -fn draw_box_drawings_dash_vertical( +fn draw_dash_vertical( self: BoxFont, img: *pixman.Image, count: u8, From 58292e5930abe46ea0188c35750a59cf80839d8d Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Thu, 24 Nov 2022 12:01:15 -0800 Subject: [PATCH 21/40] 16 more --- src/font/BoxFont.zig | 127 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) diff --git a/src/font/BoxFont.zig b/src/font/BoxFont.zig index 4a7168c84..ee4c132fa 100644 --- a/src/font/BoxFont.zig +++ b/src/font/BoxFont.zig @@ -208,6 +208,23 @@ fn draw(self: BoxFont, img: *pixman.Image, cp: u32) !void { 0x253e => self.draw_right_heavy_and_left_vertical_light(img), 0x253f => self.draw_vertical_light_and_horizontal_heavy(img), + 0x2540 => self.draw_up_heavy_and_down_horizontal_light(img), + 0x2541 => self.draw_down_heavy_and_up_horizontal_light(img), + 0x2542 => self.draw_vertical_heavy_and_horizontal_light(img), + 0x2543 => self.draw_left_up_heavy_and_right_down_light(img), + 0x2544 => self.draw_right_up_heavy_and_left_down_light(img), + 0x2545 => self.draw_left_down_heavy_and_right_up_light(img), + 0x2546 => self.draw_right_down_heavy_and_left_up_light(img), + 0x2547 => self.draw_down_light_and_up_horizontal_heavy(img), + 0x2548 => self.draw_up_light_and_down_horizontal_heavy(img), + 0x2549 => self.draw_right_light_and_left_vertical_heavy(img), + 0x254a => self.draw_left_light_and_right_vertical_heavy(img), + 0x254b => self.draw_heavy_vertical_and_horizontal(img), + 0x254c => self.draw_light_double_dash_horizontal(img), + 0x254d => self.draw_heavy_double_dash_horizontal(img), + 0x254e => self.draw_light_double_dash_vertical(img), + 0x254f => self.draw_heavy_double_dash_vertical(img), + else => return error.InvalidCodepoint, } } @@ -578,6 +595,116 @@ fn draw_vertical_light_and_horizontal_heavy(self: BoxFont, img: *pixman.Image) v self.vline_middle(img, .light); } +fn draw_up_heavy_and_down_horizontal_light(self: BoxFont, img: *pixman.Image) void { + self.hline_middle(img, .light); + self.vline_middle_up(img, .heavy, .heavy); + self.vline_middle_down(img, .light, .light); +} + +fn draw_down_heavy_and_up_horizontal_light(self: BoxFont, img: *pixman.Image) void { + self.hline_middle(img, .light); + self.vline_middle_up(img, .light, .light); + self.vline_middle_down(img, .heavy, .light); +} + +fn draw_vertical_heavy_and_horizontal_light(self: BoxFont, img: *pixman.Image) void { + self.hline_middle(img, .light); + self.vline_middle(img, .heavy); +} + +fn draw_left_up_heavy_and_right_down_light(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_left(img, .heavy, .heavy); + self.hline_middle_right(img, .light, .light); + self.vline_middle_up(img, .heavy, .heavy); + self.vline_middle_down(img, .light, .light); +} + +fn draw_right_up_heavy_and_left_down_light(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_left(img, .light, .light); + self.hline_middle_right(img, .heavy, .heavy); + self.vline_middle_up(img, .heavy, .heavy); + self.vline_middle_down(img, .light, .light); +} + +fn draw_left_down_heavy_and_right_up_light(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_left(img, .heavy, .heavy); + self.hline_middle_right(img, .light, .light); + self.vline_middle_up(img, .light, .light); + self.vline_middle_down(img, .heavy, .heavy); +} + +fn draw_right_down_heavy_and_left_up_light(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_left(img, .light, .light); + self.hline_middle_right(img, .heavy, .heavy); + self.vline_middle_up(img, .light, .light); + self.vline_middle_down(img, .heavy, .heavy); +} + +fn draw_down_light_and_up_horizontal_heavy(self: BoxFont, img: *pixman.Image) void { + self.hline_middle(img, .heavy); + self.vline_middle_up(img, .heavy, .heavy); + self.vline_middle_down(img, .light, .light); +} + +fn draw_up_light_and_down_horizontal_heavy(self: BoxFont, img: *pixman.Image) void { + self.hline_middle(img, .heavy); + self.vline_middle_up(img, .light, .light); + self.vline_middle_down(img, .heavy, .heavy); +} + +fn draw_right_light_and_left_vertical_heavy(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_left(img, .heavy, .heavy); + self.hline_middle_right(img, .light, .light); + self.vline_middle(img, .heavy); +} + +fn draw_left_light_and_right_vertical_heavy(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_left(img, .light, .light); + self.hline_middle_right(img, .heavy, .heavy); + self.vline_middle(img, .heavy); +} + +fn draw_heavy_vertical_and_horizontal(self: BoxFont, img: *pixman.Image) void { + self.hline_middle(img, .heavy); + self.vline_middle(img, .heavy); +} + +fn draw_light_double_dash_horizontal(self: BoxFont, img: *pixman.Image) void { + self.draw_dash_horizontal( + img, + 2, + Thickness.light.height(self.thickness), + Thickness.light.height(self.thickness), + ); +} + +fn draw_heavy_double_dash_horizontal(self: BoxFont, img: *pixman.Image) void { + self.draw_dash_horizontal( + img, + 2, + Thickness.heavy.height(self.thickness), + Thickness.heavy.height(self.thickness), + ); +} + +fn draw_light_double_dash_vertical(self: BoxFont, img: *pixman.Image) void { + self.draw_dash_vertical( + img, + 2, + Thickness.light.height(self.thickness), + Thickness.heavy.height(self.thickness), + ); +} + +fn draw_heavy_double_dash_vertical(self: BoxFont, img: *pixman.Image) void { + self.draw_dash_vertical( + img, + 2, + Thickness.heavy.height(self.thickness), + Thickness.heavy.height(self.thickness), + ); +} + fn draw_dash_horizontal( self: BoxFont, img: *pixman.Image, From ef16ba263f7dd65c4bcbaf7c8138eba435bf09eb Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 25 Nov 2022 10:40:33 -0800 Subject: [PATCH 22/40] 16 more --- src/font/BoxFont.zig | 160 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) diff --git a/src/font/BoxFont.zig b/src/font/BoxFont.zig index ee4c132fa..4882ab7bf 100644 --- a/src/font/BoxFont.zig +++ b/src/font/BoxFont.zig @@ -225,6 +225,23 @@ fn draw(self: BoxFont, img: *pixman.Image, cp: u32) !void { 0x254e => self.draw_light_double_dash_vertical(img), 0x254f => self.draw_heavy_double_dash_vertical(img), + 0x2550 => self.draw_double_horizontal(img), + 0x2551 => self.draw_double_vertical(img), + 0x2552 => self.draw_down_single_and_right_double(img), + 0x2553 => self.draw_down_double_and_right_single(img), + 0x2554 => self.draw_double_down_and_right(img), + 0x2555 => self.draw_down_single_and_left_double(img), + 0x2556 => self.draw_down_double_and_left_single(img), + 0x2557 => self.draw_double_down_and_left(img), + 0x2558 => self.draw_up_single_and_right_double(img), + 0x2559 => self.draw_up_double_and_right_single(img), + 0x255a => self.draw_double_up_and_right(img), + 0x255b => self.draw_up_single_and_left_double(img), + 0x255c => self.draw_up_double_and_left_single(img), + 0x255d => self.draw_double_up_and_left(img), + 0x255e => self.draw_vertical_single_and_right_double(img), + 0x255f => self.draw_vertical_double_and_right_single(img), + else => return error.InvalidCodepoint, } } @@ -705,6 +722,149 @@ fn draw_heavy_double_dash_vertical(self: BoxFont, img: *pixman.Image) void { ); } +fn draw_double_horizontal(self: BoxFont, img: *pixman.Image) void { + const thick_px = Thickness.light.height(self.thickness); + const mid = (self.height - thick_px * 3) / 2; + self.hline(img, 0, self.width, mid, thick_px); + self.hline(img, 0, self.width, mid + 2 * thick_px, thick_px); +} + +fn draw_double_vertical(self: BoxFont, img: *pixman.Image) void { + const thick_px = Thickness.light.height(self.thickness); + const mid = (self.width - thick_px * 3) / 2; + self.vline(img, 0, self.height, mid, thick_px); + self.vline(img, 0, self.height, mid + 2 * thick_px, thick_px); +} + +fn draw_down_single_and_right_double(self: BoxFont, img: *pixman.Image) void { + const thick_px = Thickness.light.height(self.thickness); + const hmid = (self.height - thick_px * 3) / 2; + const vmid = (self.width - thick_px) / 2; + self.vline_middle_down(img, .light, .light); + self.hline(img, vmid, self.width, hmid, thick_px); + self.hline(img, vmid, self.width, hmid + 2 * thick_px, thick_px); +} + +fn draw_down_double_and_right_single(self: BoxFont, img: *pixman.Image) void { + const thick_px = Thickness.light.height(self.thickness); + const hmid = (self.height - thick_px) / 2; + const vmid = (self.width - thick_px * 3) / 2; + self.hline_middle_right(img, .light, .light); + self.vline(img, hmid, self.height, vmid, thick_px); + self.vline(img, hmid, self.height, vmid + 2 * thick_px, thick_px); +} + +fn draw_double_down_and_right(self: BoxFont, img: *pixman.Image) void { + const thick_px = Thickness.light.height(self.thickness); + const hmid = (self.height - thick_px * 3) / 2; + const vmid = (self.width - thick_px * 3) / 2; + self.vline(img, hmid, self.height, vmid, thick_px); + self.vline(img, hmid + 2 * thick_px, self.height, vmid + 2 * thick_px, thick_px); + self.hline(img, vmid, self.width, hmid, thick_px); + self.hline(img, vmid + 2 * thick_px, self.width, hmid + 2 * thick_px, thick_px); +} + +fn draw_down_single_and_left_double(self: BoxFont, img: *pixman.Image) void { + const thick_px = Thickness.light.height(self.thickness); + const hmid = (self.height - thick_px * 3) / 2; + const vmid = (self.width + thick_px) / 2; + self.vline_middle_down(img, .light, .light); + self.hline(img, 0, vmid, hmid, thick_px); + self.hline(img, 0, vmid, hmid + 2 * thick_px, thick_px); +} + +fn draw_down_double_and_left_single(self: BoxFont, img: *pixman.Image) void { + const thick_px = Thickness.light.height(self.thickness); + const hmid = (self.height - thick_px) / 2; + const vmid = (self.width - thick_px * 3) / 2; + self.hline_middle_left(img, .light, .light); + self.vline(img, hmid, self.height, vmid, thick_px); + self.vline(img, hmid, self.height, vmid + 2 * thick_px, thick_px); +} + +fn draw_double_down_and_left(self: BoxFont, img: *pixman.Image) void { + const thick_px = Thickness.light.height(self.thickness); + const hmid = (self.height - thick_px * 3) / 2; + const vmid = (self.width - thick_px * 3) / 2; + self.vline(img, hmid + 2 * thick_px, self.height, vmid, thick_px); + self.vline(img, hmid, self.height, vmid + 2 * thick_px, thick_px); + self.hline(img, 0, vmid + 2 * thick_px, hmid, thick_px); + self.hline(img, 0, vmid, hmid + 2 * thick_px, thick_px); +} + +fn draw_up_single_and_right_double(self: BoxFont, img: *pixman.Image) void { + const thick_px = Thickness.light.height(self.thickness); + const hmid = (self.height - thick_px * 3) / 2; + const vmid = (self.width - thick_px) / 2; + self.vline_middle_up(img, .light, .light); + self.hline(img, vmid, self.width, hmid, thick_px); + self.hline(img, vmid, self.width, hmid + 2 * thick_px, thick_px); +} + +fn draw_up_double_and_right_single(self: BoxFont, img: *pixman.Image) void { + const thick_px = Thickness.light.height(self.thickness); + const hmid = (self.height + thick_px) / 2; + const vmid = (self.width - thick_px * 3) / 2; + self.hline_middle_right(img, .light, .light); + self.vline(img, 0, hmid, vmid, thick_px); + self.vline(img, 0, hmid, vmid + 2 * thick_px, thick_px); +} + +fn draw_double_up_and_right(self: BoxFont, img: *pixman.Image) void { + const thick_px = Thickness.light.height(self.thickness); + const hmid = (self.height - thick_px * 3) / 2; + const vmid = (self.width - thick_px * 3) / 2; + self.vline(img, 0, hmid + 2 * thick_px, vmid, thick_px); + self.vline(img, 0, hmid, vmid + 2 * thick_px, thick_px); + self.hline(img, vmid + 2 * thick_px, self.width, hmid, thick_px); + self.hline(img, vmid, self.width, hmid + 2 * thick_px, thick_px); +} + +fn draw_up_single_and_left_double(self: BoxFont, img: *pixman.Image) void { + const thick_px = Thickness.light.height(self.thickness); + const hmid = (self.height - thick_px * 3) / 2; + const vmid = (self.width + thick_px) / 2; + self.vline_middle_up(img, .light, .light); + self.hline(img, 0, vmid, hmid, thick_px); + self.hline(img, 0, vmid, hmid + 2 * thick_px, thick_px); +} + +fn draw_up_double_and_left_single(self: BoxFont, img: *pixman.Image) void { + const thick_px = Thickness.light.height(self.thickness); + const hmid = (self.height + thick_px) / 2; + const vmid = (self.width - thick_px * 3) / 2; + self.hline_middle_left(img, .light, .light); + self.vline(img, 0, hmid, vmid, thick_px); + self.vline(img, 0, hmid, vmid + 2 * thick_px, thick_px); +} + +fn draw_double_up_and_left(self: BoxFont, img: *pixman.Image) void { + const thick_px = Thickness.light.height(self.thickness); + const hmid = (self.height - thick_px * 3) / 2; + const vmid = (self.width - thick_px * 3) / 2; + self.vline(img, 0, hmid + 0 * thick_px + thick_px, vmid, thick_px); + self.vline(img, 0, hmid + 2 * thick_px + thick_px, vmid + 2 * thick_px, thick_px); + self.hline(img, 0, vmid, hmid, thick_px); + self.hline(img, 0, vmid, hmid + 2 * thick_px, thick_px); +} + +fn draw_vertical_single_and_right_double(self: BoxFont, img: *pixman.Image) void { + const thick_px = Thickness.light.height(self.thickness); + const hmid = (self.height - thick_px * 3) / 2; + const vmid = (self.width - thick_px) / 2; + self.vline_middle(img, .light); + self.hline(img, vmid, self.width, hmid, thick_px); + self.hline(img, vmid, self.width, hmid + 2 * thick_px, thick_px); +} + +fn draw_vertical_double_and_right_single(self: BoxFont, img: *pixman.Image) void { + const thick_px = Thickness.light.height(self.thickness); + const vmid = (self.width - thick_px * 3) / 2; + self.hline(img, vmid + 2 * thick_px, self.width, (self.height - thick_px) / 2, thick_px); + self.vline(img, 0, self.height, vmid, thick_px); + self.vline(img, 0, self.height, vmid + 2 * thick_px, thick_px); +} + fn draw_dash_horizontal( self: BoxFont, img: *pixman.Image, From 72cd107e6aa5e7c4638c65374cbf0ae7dee03d50 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 25 Nov 2022 10:57:38 -0800 Subject: [PATCH 23/40] many more boxes --- src/font/BoxFont.zig | 140 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) diff --git a/src/font/BoxFont.zig b/src/font/BoxFont.zig index 4882ab7bf..606393a64 100644 --- a/src/font/BoxFont.zig +++ b/src/font/BoxFont.zig @@ -242,6 +242,20 @@ fn draw(self: BoxFont, img: *pixman.Image, cp: u32) !void { 0x255e => self.draw_vertical_single_and_right_double(img), 0x255f => self.draw_vertical_double_and_right_single(img), + 0x2560 => self.draw_double_vertical_and_right(img), + 0x2561 => self.draw_vertical_single_and_left_double(img), + 0x2562 => self.draw_vertical_double_and_left_single(img), + 0x2563 => self.draw_double_vertical_and_left(img), + 0x2564 => self.draw_down_single_and_horizontal_double(img), + 0x2565 => self.draw_down_double_and_horizontal_single(img), + 0x2566 => self.draw_double_down_and_horizontal(img), + 0x2567 => self.draw_up_single_and_horizontal_double(img), + 0x2568 => self.draw_up_double_and_horizontal_single(img), + 0x2569 => self.draw_double_up_and_horizontal(img), + 0x256a => self.draw_vertical_single_and_horizontal_double(img), + 0x256b => self.draw_vertical_double_and_horizontal_single(img), + 0x256c => self.draw_double_vertical_and_horizontal(img), + else => return error.InvalidCodepoint, } } @@ -865,6 +879,132 @@ fn draw_vertical_double_and_right_single(self: BoxFont, img: *pixman.Image) void self.vline(img, 0, self.height, vmid + 2 * thick_px, thick_px); } +fn draw_double_vertical_and_right(self: BoxFont, img: *pixman.Image) void { + const thick_px = Thickness.light.height(self.thickness); + const hmid = (self.height - thick_px * 3) / 2; + const vmid = (self.width - thick_px * 3) / 2; + self.vline(img, 0, self.height, vmid, thick_px); + self.vline(img, 0, hmid, vmid + 2 * thick_px, thick_px); + self.vline(img, hmid + 2 * thick_px, self.height, vmid + 2 * thick_px, thick_px); + self.hline(img, vmid + 2 * thick_px, self.width, hmid, thick_px); + self.hline(img, vmid + 2 * thick_px, self.width, hmid + 2 * thick_px, thick_px); +} + +fn draw_vertical_single_and_left_double(self: BoxFont, img: *pixman.Image) void { + const thick_px = Thickness.light.height(self.thickness); + const hmid = (self.height - thick_px * 3) / 2; + const vmid = (self.width + thick_px) / 2; + self.vline_middle(img, .light); + self.hline(img, 0, vmid, hmid, thick_px); + self.hline(img, 0, vmid, hmid + 2 * thick_px, thick_px); +} + +fn draw_vertical_double_and_left_single(self: BoxFont, img: *pixman.Image) void { + const thick_px = Thickness.light.height(self.thickness); + const vmid = (self.width - thick_px * 3) / 2; + self.hline(img, 0, vmid, (self.height - thick_px) / 2, thick_px); + self.vline(img, 0, self.height, vmid, thick_px); + self.vline(img, 0, self.height, vmid + 2 * thick_px, thick_px); +} + +fn draw_double_vertical_and_left(self: BoxFont, img: *pixman.Image) void { + const thick_px = Thickness.light.height(self.thickness); + const hmid = (self.height - thick_px * 3) / 2; + const vmid = (self.width - thick_px * 3) / 2; + self.vline(img, 0, self.height, vmid + 2 * thick_px, thick_px); + self.vline(img, 0, hmid, vmid, thick_px); + self.vline(img, hmid + 2 * thick_px, self.height, vmid, thick_px); + self.hline(img, 0, vmid + thick_px, hmid, thick_px); + self.hline(img, 0, vmid, hmid + 2 * thick_px, thick_px); +} + +fn draw_down_single_and_horizontal_double(self: BoxFont, img: *pixman.Image) void { + const thick_px = Thickness.light.height(self.thickness); + const hmid = (self.height - thick_px * 3) / 2; + self.vline(img, hmid + 2 * thick_px, self.height, (self.width - thick_px) / 2, thick_px); + self.hline(img, 0, self.width, hmid, thick_px); + self.hline(img, 0, self.width, hmid + 2 * thick_px, thick_px); +} + +fn draw_down_double_and_horizontal_single(self: BoxFont, img: *pixman.Image) void { + const thick_px = Thickness.light.height(self.thickness); + const hmid = (self.height - thick_px) / 2; + const vmid = (self.width - thick_px * 3) / 2; + self.hline_middle(img, .light); + self.vline(img, hmid, self.height, vmid, thick_px); + self.vline(img, hmid, self.height, vmid + 2 * thick_px, thick_px); +} + +fn draw_double_down_and_horizontal(self: BoxFont, img: *pixman.Image) void { + const thick_px = Thickness.light.height(self.thickness); + const hmid = (self.height - thick_px * 3) / 2; + const vmid = (self.width - thick_px * 3) / 2; + self.hline(img, 0, self.width, hmid, thick_px); + self.hline(img, 0, vmid, hmid + 2 * thick_px, thick_px); + self.hline(img, vmid + 2 * thick_px, self.width, hmid + 2 * thick_px, thick_px); + self.vline(img, hmid + 2 * thick_px, self.height, vmid, thick_px); + self.vline(img, hmid + 2 * thick_px, self.height, vmid + 2 * thick_px, thick_px); +} + +fn draw_up_single_and_horizontal_double(self: BoxFont, img: *pixman.Image) void { + const thick_px = Thickness.light.height(self.thickness); + const hmid = (self.height - thick_px * 3) / 2; + const vmid = (self.width - thick_px) / 2; + self.vline(img, 0, hmid, vmid, thick_px); + self.hline(img, 0, self.width, hmid, thick_px); + self.hline(img, 0, self.width, hmid + 2 * thick_px, thick_px); +} + +fn draw_up_double_and_horizontal_single(self: BoxFont, img: *pixman.Image) void { + const thick_px = Thickness.light.height(self.thickness); + const hmid = (self.height - thick_px) / 2; + const vmid = (self.width - thick_px * 3) / 2; + self.hline_middle(img, .light); + self.vline(img, 0, hmid, vmid, thick_px); + self.vline(img, 0, hmid, vmid + 2 * thick_px, thick_px); +} + +fn draw_double_up_and_horizontal(self: BoxFont, img: *pixman.Image) void { + const thick_px = Thickness.light.height(self.thickness); + const hmid = (self.height - thick_px * 3) / 2; + const vmid = (self.width - thick_px * 3) / 2; + self.vline(img, 0, hmid, vmid, thick_px); + self.vline(img, 0, hmid, vmid + 2 * thick_px, thick_px); + self.hline(img, 0, vmid + thick_px, hmid, thick_px); + self.hline(img, vmid + 2 * thick_px, self.width, hmid, thick_px); + self.hline(img, 0, self.width, hmid + 2 * thick_px, thick_px); +} + +fn draw_vertical_single_and_horizontal_double(self: BoxFont, img: *pixman.Image) void { + const thick_px = Thickness.light.height(self.thickness); + const hmid = (self.height - thick_px * 3) / 2; + self.vline_middle(img, .light); + self.hline(img, 0, self.width, hmid, thick_px); + self.hline(img, 0, self.width, hmid + 2 * thick_px, thick_px); +} + +fn draw_vertical_double_and_horizontal_single(self: BoxFont, img: *pixman.Image) void { + const thick_px = Thickness.light.height(self.thickness); + const vmid = (self.width - thick_px * 3) / 2; + self.hline_middle(img, .light); + self.vline(img, 0, self.height, vmid, thick_px); + self.vline(img, 0, self.height, vmid + 2 * thick_px, thick_px); +} + +fn draw_double_vertical_and_horizontal(self: BoxFont, img: *pixman.Image) void { + const thick_px = Thickness.light.height(self.thickness); + const hmid = (self.height - thick_px * 3) / 2; + const vmid = (self.width - thick_px * 3) / 2; + self.hline(img, 0, vmid, hmid, thick_px); + self.hline(img, vmid + 2 * thick_px, self.width, hmid, thick_px); + self.hline(img, 0, vmid, hmid + 2 * thick_px, thick_px); + self.hline(img, vmid + 2 * thick_px, self.width, hmid + 2 * thick_px, thick_px); + self.vline(img, 0, hmid + thick_px, vmid, thick_px); + self.vline(img, 0, hmid, vmid + 2 * thick_px, thick_px); + self.vline(img, hmid + 2 * thick_px, self.height, vmid, thick_px); + self.vline(img, hmid + 2 * thick_px, self.height, vmid + 2 * thick_px, thick_px); +} + fn draw_dash_horizontal( self: BoxFont, img: *pixman.Image, From 336d2c4e136418c9e8ba1d99d3a565918ebed9f9 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 25 Nov 2022 11:34:31 -0800 Subject: [PATCH 24/40] pkg/pixman: more image APIs --- pkg/pixman/image.zig | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/pkg/pixman/image.zig b/pkg/pixman/image.zig index dc888e7df..e6bb950c8 100644 --- a/pkg/pixman/image.zig +++ b/pkg/pixman/image.zig @@ -23,6 +23,30 @@ pub const Image = opaque { return c.pixman_image_unref(@ptrCast(*c.pixman_image_t, self)) == 1; } + /// A variant of getDataUnsafe that sets the length of the slice to + /// height * stride. Its possible the buffer is larger but this is the + /// known safe values. If you KNOW the buffer is larger you can use the + /// unsafe variant. + pub fn getData(self: *Image) []u32 { + const height = self.getHeight(); + const stride = self.getStride(); + const ptr = self.getDataUnsafe(); + const len = @intCast(usize, height * stride); + return ptr[0..len]; + } + + pub fn getDataUnsafe(self: *Image) [*]u32 { + return c.pixman_image_get_data(@ptrCast(*c.pixman_image_t, self)); + } + + pub fn getHeight(self: *Image) c_int { + return c.pixman_image_get_height(@ptrCast(*c.pixman_image_t, self)); + } + + pub fn getStride(self: *Image) c_int { + return c.pixman_image_get_stride(@ptrCast(*c.pixman_image_t, self)); + } + pub fn fillBoxes( self: *Image, op: pixman.Op, @@ -53,6 +77,9 @@ test "create and destroy" { defer alloc.free(data); std.mem.set(u32, data, 0); const img = try Image.createBitsNoClear(.g1, width, height, data.ptr, stride); + try testing.expectEqual(@as(c_int, height), img.getHeight()); + try testing.expectEqual(@as(c_int, stride), img.getStride()); + try testing.expect(img.getData().len == height * stride); try testing.expect(img.unref()); } From 8ad5dd1853959ebd3a404c758615b47a894eb1d3 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 25 Nov 2022 12:22:21 -0800 Subject: [PATCH 25/40] arc glyphs --- src/font/BoxFont.zig | 280 ++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 264 insertions(+), 16 deletions(-) diff --git a/src/font/BoxFont.zig b/src/font/BoxFont.zig index 606393a64..b92670667 100644 --- a/src/font/BoxFont.zig +++ b/src/font/BoxFont.zig @@ -69,8 +69,9 @@ pub fn renderGlyph( const stride = format.strideForWidth(self.width); const len = @intCast(usize, stride * @intCast(c_int, self.height)); - // Allocate our buffer - var data = try alloc.alloc(u32, len); + // Allocate our buffer. pixman uses []u32 so we divide our length + // by 4 since u32 / u8 = 4. + var data = try alloc.alloc(u32, len / 4); defer alloc.free(data); std.mem.set(u32, data, 0); @@ -84,15 +85,14 @@ pub fn renderGlyph( ); defer _ = img.unref(); - try self.draw(img, cp); + try self.draw(alloc, img, cp); // Reserve our region in the atlas and render the glyph to it. const region = try atlas.reserve(alloc, self.width, self.height); if (region.width > 0 and region.height > 0) { // Convert our []u32 to []u8 since we use 8bpp formats assert(format.bpp() == 8); - const len_u8 = len * 4; - const data_u8 = @alignCast(@alignOf(u8), @ptrCast([*]u8, data.ptr)[0..len_u8]); + const data_u8 = @alignCast(@alignOf(u8), @ptrCast([*]u8, data.ptr)[0..len]); const depth = atlas.format.depth(); @@ -138,7 +138,7 @@ pub fn renderGlyph( }; } -fn draw(self: BoxFont, img: *pixman.Image, cp: u32) !void { +fn draw(self: BoxFont, alloc: Allocator, img: *pixman.Image, cp: u32) !void { switch (cp) { 0x2500 => self.draw_light_horizontal(img), 0x2501 => self.draw_heavy_horizontal(img), @@ -255,6 +255,7 @@ fn draw(self: BoxFont, img: *pixman.Image, cp: u32) !void { 0x256a => self.draw_vertical_single_and_horizontal_double(img), 0x256b => self.draw_vertical_double_and_horizontal_single(img), 0x256c => self.draw_double_vertical_and_horizontal(img), + 0x256d...0x2570 => try self.draw_light_arc(alloc, img, cp), else => return error.InvalidCodepoint, } @@ -1005,6 +1006,249 @@ fn draw_double_vertical_and_horizontal(self: BoxFont, img: *pixman.Image) void { self.vline(img, hmid + 2 * thick_px, self.height, vmid + 2 * thick_px, thick_px); } +fn draw_light_arc( + self: BoxFont, + alloc: Allocator, + img: *pixman.Image, + cp: u32, +) !void { + const supersample = 4; + const height = self.height * supersample; + const width = self.width * supersample; + const stride = pixman.FormatCode.a8.strideForWidth(width); + + // Allocate our buffer + var data = try alloc.alloc(u8, height * @intCast(u32, stride)); + defer alloc.free(data); + std.mem.set(u8, data, 0); + + const height_pixels = self.height; + const width_pixels = self.width; + const thick_pixels = Thickness.light.height(self.thickness); + const thick = thick_pixels * supersample; + + const circle_inner_edge = (@min(width_pixels, height_pixels) - thick_pixels) / 2; + + // We want to draw the quartercircle by filling small circles (with r = + // thickness/2.) whose centers are on its edge. This means to get the + // radius of the quartercircle, we add the exact half thickness to the + // radius of the inner circle. + var c_r: f64 = @intToFloat(f64, circle_inner_edge) + @intToFloat(f64, thick_pixels) / 2; + + // We need to draw short lines from the end of the quartercircle to the + // box-edges, store one endpoint (the other is the edge of the + // quartercircle) in these vars. + var vert_to: u32 = 0; + var hor_to: u32 = 0; + + // Coordinates of the circle-center. + var c_x: u32 = 0; + var c_y: u32 = 0; + + // For a given y there are up to two solutions for the circle-equation. + // Set to -1 for the left, and 1 for the right hemisphere. + var circle_hemisphere: i32 = 0; + + // The quarter circle only has to be evaluated for a small range of + // y-values. + var y_min: u32 = 0; + var y_max: u32 = 0; + + switch (cp) { + '╭' => { + // Don't use supersampled coordinates yet, we want to align actual + // pixels. + // + // pixel-coordinates of the lower edge of the right line and the + // right edge of the bottom line. + const right_bottom_edge = (height_pixels + thick_pixels) / 2; + const bottom_right_edge = (width_pixels + thick_pixels) / 2; + + // find coordinates of circle-center. + c_y = right_bottom_edge + circle_inner_edge; + c_x = bottom_right_edge + circle_inner_edge; + + // we want to render the left, not the right hemisphere of the circle. + circle_hemisphere = -1; + + // don't evaluate beyond c_y, the vertical line is drawn there. + y_min = 0; + y_max = c_y; + + // the vertical line should extend to the bottom of the box, the + // horizontal to the right. + vert_to = height_pixels; + hor_to = width_pixels; + }, + '╮' => { + const left_bottom_edge = (height_pixels + thick_pixels) / 2; + const bottom_left_edge = (width_pixels - thick_pixels) / 2; + + c_y = left_bottom_edge + circle_inner_edge; + c_x = bottom_left_edge - circle_inner_edge; + + circle_hemisphere = 1; + + y_min = 0; + y_max = c_y; + + vert_to = height_pixels; + hor_to = 0; + }, + '╰' => { + const right_top_edge = (height_pixels - thick_pixels) / 2; + const top_right_edge = (width_pixels + thick_pixels) / 2; + + c_y = right_top_edge - circle_inner_edge; + c_x = top_right_edge + circle_inner_edge; + + circle_hemisphere = -1; + + y_min = c_y; + y_max = height_pixels; + + vert_to = 0; + hor_to = width_pixels; + }, + '╯' => { + const left_top_edge = (height_pixels - thick_pixels) / 2; + const top_left_edge = (width_pixels - thick_pixels) / 2; + + c_y = left_top_edge - circle_inner_edge; + c_x = top_left_edge - circle_inner_edge; + + circle_hemisphere = 1; + + y_min = c_y; + y_max = height_pixels; + + vert_to = 0; + hor_to = 0; + }, + + else => {}, + } + + // store for horizontal+vertical line. + const c_x_pixels = c_x; + const c_y_pixels = c_y; + + // Bring coordinates from pixel-grid to supersampled grid. + c_r *= supersample; + c_x *= supersample; + c_y *= supersample; + + y_min *= supersample; + y_max *= supersample; + + const c_r2 = c_r * c_r; + + // To prevent gaps in the circle, each pixel is sampled multiple times. + // As the quartercircle ends (vertically) in the middle of a pixel, an + // uneven number helps hit that exactly. + { + var i: f64 = @intToFloat(f64, y_min) * 16; + while (i <= @intToFloat(f64, y_max) * 16) : (i += 1) { + const y = i / 16; + const x = x: { + // circle_hemisphere * sqrt(c_r2 - (y - c_y) * (y - c_y)) + c_x; + const hemi = @intToFloat(f64, circle_hemisphere); + const y_part = y - @intToFloat(f64, c_y); + const y_squared = y_part * y_part; + const sqrt = @sqrt(c_r2 - y_squared); + const f_c_x = @intToFloat(f64, c_x); + + // We need to detect overflows and just skip this i + const a = hemi * sqrt; + const b = a + f_c_x; + + // If the float math didn't work, ignore. + if (std.math.isNan(b)) continue; + + break :x b; + }; + + const row = @floatToInt(i32, @round(y)); + const col = @floatToInt(i32, @round(x)); + if (col < 0) continue; + + // rectangle big enough to fit entire circle with radius thick/2. + const row1 = row - @intCast(i32, thick / 2 + 1); + const row2 = row + @intCast(i32, thick / 2 + 1); + const col1 = col - @intCast(i32, thick / 2 + 1); + const col2 = col + @intCast(i32, thick / 2 + 1); + + const row_start = @min(row1, row2); + const row_end = @max(row1, row2); + const col_start = @min(col1, col2); + const col_end = @max(col1, col2); + + assert(row_end > row_start); + assert(col_end > col_start); + + // draw circle with radius thick/2 around x,y. + // this is accomplished by rejecting pixels where the distance from + // their center to x,y is greater than thick/2. + var r: i32 = @max(row_start, 0); + const r_end = @max(@min(row_end, @intCast(i32, height)), 0); + while (r < r_end) : (r += 1) { + const r_midpoint = @intToFloat(f64, r) + 0.5; + + var c: i32 = @max(col_start, 0); + const c_end = @max(@min(col_end, @intCast(i32, width)), 0); + while (c < c_end) : (c += 1) { + const c_midpoint = @intToFloat(f64, c) + 0.5; + + // vector from point on quartercircle to midpoint of the current pixel. + const center_midpoint_x = c_midpoint - x; + const center_midpoint_y = r_midpoint - y; + + // distance from current point to circle-center. + const dist = @sqrt(center_midpoint_x * center_midpoint_x + center_midpoint_y * center_midpoint_y); + // skip if midpoint of pixel is outside the circle. + if (dist > @intToFloat(f64, thick) / 2) continue; + + const idx = @intCast(usize, r * stride + c); + data[idx] = 0xff; + } + } + } + } + + // Downsample + { + // We want to convert our []u32 to []u8 since we use an 8bpp format + var data_u32 = img.getData(); + const len_u8 = data_u32.len * 4; + var real_data = @alignCast(@alignOf(u8), @ptrCast([*]u8, data_u32.ptr)[0..len_u8]); + const real_stride = img.getStride(); + + var r: u32 = 0; + while (r < self.height) : (r += 1) { + var c: u32 = 0; + while (c < self.width) : (c += 1) { + var total: u32 = 0; + var i: usize = 0; + while (i < supersample) : (i += 1) { + var j: usize = 0; + while (j < supersample) : (j += 1) { + const idx = (r * supersample + i) * @intCast(usize, stride) + c * supersample + j; + total += data[idx]; + } + } + + const average = @intCast(u8, @min(total / (supersample * supersample), 0xff)); + const idx = r * @intCast(usize, real_stride) + c; + real_data[idx] = average; + } + } + } + + // draw vertical/horizontal lines from quartercircle-edge to box-edge. + self.vline(img, @min(c_y_pixels, vert_to), @max(c_y_pixels, vert_to), (width_pixels - thick_pixels) / 2, thick_pixels); + self.hline(img, @min(c_x_pixels, hor_to), @max(c_x_pixels, hor_to), (height_pixels - thick_pixels) / 2, thick_pixels); +} + fn draw_dash_horizontal( self: BoxFont, img: *pixman.Image, @@ -1259,15 +1503,19 @@ test "all" { const testing = std.testing; const alloc = testing.allocator; - var atlas_greyscale = try Atlas.init(alloc, 512, .greyscale); - defer atlas_greyscale.deinit(alloc); + var cp: u32 = 0x2500; + const end = 0x2570; + while (cp <= end) : (cp += 1) { + var atlas_greyscale = try Atlas.init(alloc, 512, .greyscale); + defer atlas_greyscale.deinit(alloc); - const face: BoxFont = .{ .width = 18, .height = 36, .thickness = 2 }; - const glyph = try face.renderGlyph( - alloc, - &atlas_greyscale, - 0x2500, - ); - try testing.expectEqual(@as(u32, face.width), glyph.width); - try testing.expectEqual(@as(u32, face.height), glyph.height); + const face: BoxFont = .{ .width = 18, .height = 36, .thickness = 2 }; + const glyph = try face.renderGlyph( + alloc, + &atlas_greyscale, + cp, + ); + try testing.expectEqual(@as(u32, face.width), glyph.width); + try testing.expectEqual(@as(u32, face.height), glyph.height); + } } From 179f5e62833307fb5e38d8c14e8343bac3186d0e Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 25 Nov 2022 13:16:07 -0800 Subject: [PATCH 26/40] opengl: continue rendering if single cell has error --- src/renderer/OpenGL.zig | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/renderer/OpenGL.zig b/src/renderer/OpenGL.zig index bc40d5207..c43ac006f 100644 --- a/src/renderer/OpenGL.zig +++ b/src/renderer/OpenGL.zig @@ -769,7 +769,7 @@ pub fn rebuildCells( var iter = self.font_shaper.runIterator(self.font_group, row); while (try iter.next(self.alloc)) |run| { for (try self.font_shaper.shape(run)) |shaper_cell| { - assert(try self.updateCell( + if (self.updateCell( term_selection, screen, row.getCell(shaper_cell.x), @@ -777,7 +777,15 @@ pub fn rebuildCells( run, shaper_cell.x, y, - )); + )) |update| { + assert(update); + } else |err| { + log.warn("error building cell, will be invalid x={} y={}, err={}", .{ + shaper_cell.x, + y, + err, + }); + } } } From f1a052640c5bcf4d5bc0cad55100732d0f3dde86 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 25 Nov 2022 13:23:00 -0800 Subject: [PATCH 27/40] pkg/pixman: rasterize trapezoids --- pkg/pixman/image.zig | 14 ++++++++++++++ pkg/pixman/types.zig | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/pkg/pixman/image.zig b/pkg/pixman/image.zig index e6bb950c8..e1e055be0 100644 --- a/pkg/pixman/image.zig +++ b/pkg/pixman/image.zig @@ -61,6 +61,20 @@ pub const Image = opaque { @ptrCast([*c]const c.pixman_box32_t, boxes.ptr), ) == 0) return pixman.Error.PixmanFailure; } + + pub fn rasterizeTrapezoid( + self: *Image, + trap: pixman.Trapezoid, + x_off: c_int, + y_off: c_int, + ) void { + c.pixman_rasterize_trapezoid( + @ptrCast(*c.pixman_image_t, self), + @ptrCast(*const c.pixman_trapezoid_t, &trap), + x_off, + y_off, + ); + } }; test "create and destroy" { diff --git a/pkg/pixman/types.zig b/pkg/pixman/types.zig index d3cad88ae..9b520896a 100644 --- a/pkg/pixman/types.zig +++ b/pkg/pixman/types.zig @@ -68,6 +68,38 @@ pub const Color = extern struct { alpha: u16, }; +pub const Fixed = enum(i32) { + _, + + pub fn init(v: anytype) Fixed { + return switch (@TypeOf(v)) { + comptime_int, u32 => @intToEnum(Fixed, v << 16), + f64 => @intToEnum(Fixed, @floatToInt(i32, v * 65536)), + else => { + @compileLog(@TypeOf(v)); + @compileError("unsupported type"); + }, + }; + } +}; + +pub const PointFixed = extern struct { + x: Fixed, + y: Fixed, +}; + +pub const LineFixed = extern struct { + p1: PointFixed, + p2: PointFixed, +}; + +pub const Trapezoid = extern struct { + top: Fixed, + bottom: Fixed, + left: LineFixed, + right: LineFixed, +}; + pub const Box32 = extern struct { x1: i32, y1: i32, From f727b30ca6c1bb29b98111902b8fb76b9075be63 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 25 Nov 2022 13:36:26 -0800 Subject: [PATCH 28/40] more box fonts --- src/font/BoxFont.zig | 133 +++++++++++++++++++++++++++++++++++++++++++ src/font/Group.zig | 4 ++ 2 files changed, 137 insertions(+) diff --git a/src/font/BoxFont.zig b/src/font/BoxFont.zig index b92670667..9adb18a47 100644 --- a/src/font/BoxFont.zig +++ b/src/font/BoxFont.zig @@ -257,6 +257,22 @@ fn draw(self: BoxFont, alloc: Allocator, img: *pixman.Image, cp: u32) !void { 0x256c => self.draw_double_vertical_and_horizontal(img), 0x256d...0x2570 => try self.draw_light_arc(alloc, img, cp), + 0x2571 => self.draw_light_diagonal_upper_right_to_lower_left(img), + 0x2572 => self.draw_light_diagonal_upper_left_to_lower_right(img), + 0x2573 => self.draw_light_diagonal_cross(img), + 0x2574 => self.draw_light_left(img), + 0x2575 => self.draw_light_up(img), + 0x2576 => self.draw_light_right(img), + 0x2577 => self.draw_light_down(img), + 0x2578 => self.draw_heavy_left(img), + 0x2579 => self.draw_heavy_up(img), + 0x257a => self.draw_heavy_right(img), + 0x257b => self.draw_heavy_down(img), + 0x257c => self.draw_light_left_and_heavy_right(img), + 0x257d => self.draw_light_up_and_heavy_down(img), + 0x257e => self.draw_heavy_left_and_light_right(img), + 0x257f => self.draw_heavy_up_and_light_down(img), + else => return error.InvalidCodepoint, } } @@ -1006,6 +1022,123 @@ fn draw_double_vertical_and_horizontal(self: BoxFont, img: *pixman.Image) void { self.vline(img, hmid + 2 * thick_px, self.height, vmid + 2 * thick_px, thick_px); } +fn draw_light_diagonal_upper_right_to_lower_left(self: BoxFont, img: *pixman.Image) void { + const thick_px = Thickness.light.height(self.thickness); + img.rasterizeTrapezoid(.{ + .top = pixman.Fixed.init(0), + .bottom = pixman.Fixed.init(self.height), + .left = .{ + .p1 = .{ + .x = pixman.Fixed.init(@intToFloat(f64, self.width) - @intToFloat(f64, thick_px) / 2), + .y = pixman.Fixed.init(0), + }, + + .p2 = .{ + .x = pixman.Fixed.init(0 - @intToFloat(f64, thick_px) / 2), + .y = pixman.Fixed.init(self.height), + }, + }, + .right = .{ + .p1 = .{ + .x = pixman.Fixed.init(@intToFloat(f64, self.width) + @intToFloat(f64, thick_px) / 2), + .y = pixman.Fixed.init(0), + }, + + .p2 = .{ + .x = pixman.Fixed.init(0 + @intToFloat(f64, thick_px) / 2), + .y = pixman.Fixed.init(self.height), + }, + }, + }, 0, 0); +} + +fn draw_light_diagonal_upper_left_to_lower_right(self: BoxFont, img: *pixman.Image) void { + const thick_px = Thickness.light.height(self.thickness); + img.rasterizeTrapezoid(.{ + .top = pixman.Fixed.init(0), + .bottom = pixman.Fixed.init(self.height), + .left = .{ + .p1 = .{ + .x = pixman.Fixed.init(0 - @intToFloat(f64, thick_px) / 2), + .y = pixman.Fixed.init(0), + }, + + .p2 = .{ + .x = pixman.Fixed.init(@intToFloat(f64, self.width) - @intToFloat(f64, thick_px) / 2), + .y = pixman.Fixed.init(self.height), + }, + }, + .right = .{ + .p1 = .{ + .x = pixman.Fixed.init(0 + @intToFloat(f64, thick_px) / 2), + .y = pixman.Fixed.init(0), + }, + + .p2 = .{ + .x = pixman.Fixed.init(@intToFloat(f64, self.width) + @intToFloat(f64, thick_px) / 2), + .y = pixman.Fixed.init(self.height), + }, + }, + }, 0, 0); +} + +fn draw_light_diagonal_cross(self: BoxFont, img: *pixman.Image) void { + self.draw_light_diagonal_upper_right_to_lower_left(img); + self.draw_light_diagonal_upper_left_to_lower_right(img); +} + +fn draw_light_left(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_left(img, .light, .light); +} + +fn draw_light_up(self: BoxFont, img: *pixman.Image) void { + self.vline_middle_up(img, .light, .light); +} + +fn draw_light_right(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_right(img, .light, .light); +} + +fn draw_light_down(self: BoxFont, img: *pixman.Image) void { + self.vline_middle_down(img, .light, .light); +} + +fn draw_heavy_left(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_left(img, .heavy, .heavy); +} + +fn draw_heavy_up(self: BoxFont, img: *pixman.Image) void { + self.vline_middle_up(img, .heavy, .heavy); +} + +fn draw_heavy_right(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_right(img, .heavy, .heavy); +} + +fn draw_heavy_down(self: BoxFont, img: *pixman.Image) void { + self.vline_middle_down(img, .heavy, .heavy); +} + +fn draw_light_left_and_heavy_right(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_left(img, .light, .light); + self.hline_middle_right(img, .heavy, .heavy); +} + +fn draw_light_up_and_heavy_down(self: BoxFont, img: *pixman.Image) void { + self.vline_middle_up(img, .light, .light); + self.vline_middle_down(img, .heavy, .heavy); +} + +fn draw_heavy_left_and_light_right(self: BoxFont, img: *pixman.Image) void { + self.hline_middle_left(img, .heavy, .heavy); + self.hline_middle_right(img, .light, .light); +} + +fn draw_heavy_up_and_light_down(self: BoxFont, img: *pixman.Image) void { + self.vline_middle_up(img, .heavy, .heavy); + self.vline_middle_down(img, .light, .light); +} + fn draw_light_arc( self: BoxFont, alloc: Allocator, diff --git a/src/font/Group.zig b/src/font/Group.zig index b2cd242ea..dba50c051 100644 --- a/src/font/Group.zig +++ b/src/font/Group.zig @@ -185,6 +185,10 @@ pub fn indexForCodepoint( if (switch (cp) { // "Box Drawing" block 0x2500...0x257F => true, + + // "Block Elements" block + 0x2580...0x259f => true, + else => false, }) { return FontIndex.initSpecial(.box); From 353172199a8066dca7a0939d75d3a0cd0e6bd31f Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 25 Nov 2022 13:53:24 -0800 Subject: [PATCH 29/40] lot more boxes --- src/font/BoxFont.zig | 223 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 223 insertions(+) diff --git a/src/font/BoxFont.zig b/src/font/BoxFont.zig index 9adb18a47..52f9a5f06 100644 --- a/src/font/BoxFont.zig +++ b/src/font/BoxFont.zig @@ -273,6 +273,23 @@ fn draw(self: BoxFont, alloc: Allocator, img: *pixman.Image, cp: u32) !void { 0x257e => self.draw_heavy_left_and_light_right(img), 0x257f => self.draw_heavy_up_and_light_down(img), + 0x2580 => self.draw_upper_half_block(img), + 0x2581 => self.draw_lower_one_eighth_block(img), + 0x2582 => self.draw_lower_one_quarter_block(img), + 0x2583 => self.draw_lower_three_eighths_block(img), + 0x2584 => self.draw_lower_half_block(img), + 0x2585 => self.draw_lower_five_eighths_block(img), + 0x2586 => self.draw_lower_three_quarters_block(img), + 0x2587 => self.draw_lower_seven_eighths_block(img), + 0x2588 => self.draw_full_block(img), + 0x2589 => self.draw_left_seven_eighths_block(img), + 0x258a => self.draw_left_three_quarters_block(img), + 0x258b => self.draw_left_five_eighths_block(img), + 0x258c => self.draw_left_half_block(img), + 0x258d => self.draw_left_three_eighths_block(img), + 0x258e => self.draw_left_one_quarter_block(img), + 0x258f => self.draw_left_one_eighth_block(img), + else => return error.InvalidCodepoint, } } @@ -1139,6 +1156,192 @@ fn draw_heavy_up_and_light_down(self: BoxFont, img: *pixman.Image) void { self.vline_middle_down(img, .light, .light); } +fn draw_upper_half_block(self: BoxFont, img: *pixman.Image) void { + self.rect(img, 0, 0, self.width, self.height / 2); +} + +fn draw_lower_one_eighth_block(self: BoxFont, img: *pixman.Image) void { + self.rect(img, 0, self.height - (self.height / 8), self.width, self.height); +} + +fn draw_lower_one_quarter_block(self: BoxFont, img: *pixman.Image) void { + self.rect(img, 0, self.height - (self.height / 4), self.width, self.height); +} + +fn draw_lower_three_eighths_block(self: BoxFont, img: *pixman.Image) void { + self.rect( + img, + 0, + self.height - @floatToInt(u32, @round(3 * @intToFloat(f64, self.height) / 8)), + self.width, + self.height, + ); +} + +fn draw_lower_half_block(self: BoxFont, img: *pixman.Image) void { + self.rect( + img, + 0, + self.height - @floatToInt(u32, @round(@intToFloat(f64, self.height) / 2)), + self.width, + self.height, + ); +} + +fn draw_lower_five_eighths_block(self: BoxFont, img: *pixman.Image) void { + self.rect( + img, + 0, + self.height - @floatToInt(u32, @round(5 * @intToFloat(f64, self.height) / 8)), + self.width, + self.height, + ); +} + +fn draw_lower_three_quarters_block(self: BoxFont, img: *pixman.Image) void { + self.rect( + img, + 0, + self.height - @floatToInt(u32, @round(3 * @intToFloat(f64, self.height) / 4)), + self.width, + self.height, + ); +} + +fn draw_lower_seven_eighths_block(self: BoxFont, img: *pixman.Image) void { + self.rect( + img, + 0, + self.height - @floatToInt(u32, @round(7 * @intToFloat(f64, self.height) / 8)), + self.width, + self.height, + ); +} + +fn draw_upper_one_quarter_block(self: BoxFont, img: *pixman.Image) void { + self.rect( + img, + 0, + 0, + self.width, + @floatToInt(u32, @round(@intToFloat(f64, self.height) / 4)), + ); +} + +fn draw_upper_three_eighths_block(self: BoxFont, img: *pixman.Image) void { + self.rect( + img, + 0, + 0, + self.width, + @floatToInt(u32, @round(3 * @intToFloat(f64, self.height) / 8)), + ); +} + +fn draw_upper_five_eighths_block(self: BoxFont, img: *pixman.Image) void { + self.rect( + img, + 0, + 0, + self.width, + @floatToInt(u32, @round(5 * @intToFloat(f64, self.height) / 8)), + ); +} + +fn draw_upper_three_quarters_block(self: BoxFont, img: *pixman.Image) void { + self.rect( + img, + 0, + 0, + self.width, + @floatToInt(u32, @round(3 * @intToFloat(f64, self.height) / 4)), + ); +} + +fn draw_upper_seven_eighths_block(self: BoxFont, img: *pixman.Image) void { + self.rect( + img, + 0, + 0, + self.width, + @floatToInt(u32, @round(7 * @intToFloat(f64, self.height) / 8)), + ); +} + +fn draw_full_block(self: BoxFont, img: *pixman.Image) void { + self.rect(img, 0, 0, self.width, self.height); +} + +fn draw_left_seven_eighths_block(self: BoxFont, img: *pixman.Image) void { + self.rect( + img, + 0, + 0, + @floatToInt(u32, @round(7 * @intToFloat(f64, self.width) / 8)), + self.height, + ); +} + +fn draw_left_three_quarters_block(self: BoxFont, img: *pixman.Image) void { + self.rect( + img, + 0, + 0, + @floatToInt(u32, @round(3 * @intToFloat(f64, self.width) / 4)), + self.height, + ); +} + +fn draw_left_five_eighths_block(self: BoxFont, img: *pixman.Image) void { + self.rect( + img, + 0, + 0, + @floatToInt(u32, @round(5 * @intToFloat(f64, self.width) / 8)), + self.height, + ); +} + +fn draw_left_half_block(self: BoxFont, img: *pixman.Image) void { + self.rect( + img, + 0, + 0, + @floatToInt(u32, @round(@intToFloat(f64, self.width) / 2)), + self.height, + ); +} + +fn draw_left_three_eighths_block(self: BoxFont, img: *pixman.Image) void { + self.rect( + img, + 0, + 0, + @floatToInt(u32, @round(3 * @intToFloat(f64, self.width) / 8)), + self.height, + ); +} + +fn draw_left_one_quarter_block(self: BoxFont, img: *pixman.Image) void { + self.rect( + img, + 0, + 0, + @floatToInt(u32, @round(@intToFloat(f64, self.width) / 4)), + self.height, + ); +} + +fn draw_vertical_one_eighth_block_n(self: BoxFont, img: *pixman.Image, n: u32) void { + const x = @floatToInt(u32, @round(@intToFloat(f64, n) * @intToFloat(f64, self.width) / 8)); + const w = @floatToInt(u32, @round(@intToFloat(f64, self.width) / 8)); + self.rect(img, x, 0, x + w, self.height); +} + +fn draw_left_one_eighth_block(self: BoxFont, img: *pixman.Image) void { + self.draw_vertical_one_eighth_block_n(img, 0); +} + fn draw_light_arc( self: BoxFont, alloc: Allocator, @@ -1632,6 +1835,26 @@ fn hline( img.fillBoxes(.src, white, boxes) catch {}; } +fn rect( + self: BoxFont, + img: *pixman.Image, + x1: u32, + y1: u32, + x2: u32, + y2: u32, +) void { + const boxes = &[_]pixman.Box32{ + .{ + .x1 = @intCast(i32, @min(@max(x1, 0), self.width)), + .y1 = @intCast(i32, @min(@max(y1, 0), self.height)), + .x2 = @intCast(i32, @min(@max(x2, 0), self.width)), + .y2 = @intCast(i32, @min(@max(y2, 0), self.height)), + }, + }; + + img.fillBoxes(.src, white, boxes) catch {}; +} + test "all" { const testing = std.testing; const alloc = testing.allocator; From 88ff221d6db79026709de86c0c62e7232d8018eb Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 25 Nov 2022 14:10:25 -0800 Subject: [PATCH 30/40] pkg/pixman: fill rects --- pkg/pixman/image.zig | 15 +++++++++++++++ pkg/pixman/types.zig | 7 +++++++ 2 files changed, 22 insertions(+) diff --git a/pkg/pixman/image.zig b/pkg/pixman/image.zig index e1e055be0..22c55c7ac 100644 --- a/pkg/pixman/image.zig +++ b/pkg/pixman/image.zig @@ -62,6 +62,21 @@ pub const Image = opaque { ) == 0) return pixman.Error.PixmanFailure; } + pub fn fillRectangles( + self: *Image, + op: pixman.Op, + color: pixman.Color, + rects: []const pixman.Rectangle16, + ) pixman.Error!void { + if (c.pixman_image_fill_rectangles( + @enumToInt(op), + @ptrCast(*c.pixman_image_t, self), + @ptrCast(*const c.pixman_color_t, &color), + @intCast(c_int, rects.len), + @ptrCast([*c]const c.pixman_rectangle16_t, rects.ptr), + ) == 0) return pixman.Error.PixmanFailure; + } + pub fn rasterizeTrapezoid( self: *Image, trap: pixman.Trapezoid, diff --git a/pkg/pixman/types.zig b/pkg/pixman/types.zig index 9b520896a..acebba68e 100644 --- a/pkg/pixman/types.zig +++ b/pkg/pixman/types.zig @@ -100,6 +100,13 @@ pub const Trapezoid = extern struct { right: LineFixed, }; +pub const Rectangle16 = extern struct { + x: i16, + y: i16, + width: u16, + height: u16, +}; + pub const Box32 = extern struct { x1: i32, y1: i32, From 43c89cb4497e35657d8a1b6afe37a88aee104abe Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 25 Nov 2022 14:10:31 -0800 Subject: [PATCH 31/40] more glyphs --- src/font/BoxFont.zig | 135 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) diff --git a/src/font/BoxFont.zig b/src/font/BoxFont.zig index 52f9a5f06..21f34e856 100644 --- a/src/font/BoxFont.zig +++ b/src/font/BoxFont.zig @@ -290,6 +290,14 @@ fn draw(self: BoxFont, alloc: Allocator, img: *pixman.Image, cp: u32) !void { 0x258e => self.draw_left_one_quarter_block(img), 0x258f => self.draw_left_one_eighth_block(img), + 0x2590 => self.draw_right_half_block(img), + 0x2591 => self.draw_light_shade(img), + 0x2592 => self.draw_medium_shade(img), + 0x2593 => self.draw_dark_shade(img), + 0x2594 => self.draw_upper_one_eighth_block(img), + 0x2595 => self.draw_right_one_eighth_block(img), + 0x2596...0x259f => self.draw_quadrant(img, cp), + else => return error.InvalidCodepoint, } } @@ -1342,6 +1350,133 @@ fn draw_left_one_eighth_block(self: BoxFont, img: *pixman.Image) void { self.draw_vertical_one_eighth_block_n(img, 0); } +fn draw_right_half_block(self: BoxFont, img: *pixman.Image) void { + self.rect( + img, + @floatToInt(u32, @round(@intToFloat(f64, self.width) / 2)), + 0, + self.width, + self.height, + ); +} + +fn draw_pixman_shade(self: BoxFont, img: *pixman.Image, v: u16) void { + const rects = &[_]pixman.Rectangle16{ + .{ + .x = 0, + .y = 0, + .width = @intCast(u16, self.width), + .height = @intCast(u16, self.height), + }, + }; + + img.fillRectangles( + .src, + .{ .red = 0, .green = 0, .blue = 0, .alpha = v }, + rects, + ) catch {}; +} + +fn draw_light_shade(self: BoxFont, img: *pixman.Image) void { + self.draw_pixman_shade(img, 0x4000); +} + +fn draw_medium_shade(self: BoxFont, img: *pixman.Image) void { + self.draw_pixman_shade(img, 0x8000); +} + +fn draw_dark_shade(self: BoxFont, img: *pixman.Image) void { + self.draw_pixman_shade(img, 0xc000); +} + +fn draw_horizontal_one_eighth_block_n(self: BoxFont, img: *pixman.Image, n: u32) void { + const y = @floatToInt(u32, @round(@intToFloat(f64, n) * @intToFloat(f64, self.height) / 8)); + const h = @floatToInt(u32, @round(@intToFloat(f64, self.height) / 8)); + self.rect(img, 0, y, self.width, y + h); +} + +fn draw_upper_one_eighth_block(self: BoxFont, img: *pixman.Image) void { + self.draw_horizontal_one_eighth_block_n(img, 0); +} + +fn draw_right_one_eighth_block(self: BoxFont, img: *pixman.Image) void { + self.rect( + img, + self.width - @floatToInt(u32, @round(@intToFloat(f64, self.width) / 8)), + 0, + self.width, + self.height, + ); +} + +fn quad_upper_left(self: BoxFont, img: *pixman.Image) void { + self.rect( + img, + 0, + 0, + @floatToInt(u32, @ceil(@intToFloat(f64, self.width) / 2)), + @floatToInt(u32, @ceil(@intToFloat(f64, self.height) / 2)), + ); +} + +fn quad_upper_right(self: BoxFont, img: *pixman.Image) void { + self.rect( + img, + @floatToInt(u32, @floor(@intToFloat(f64, self.width) / 2)), + 0, + self.width, + @floatToInt(u32, @ceil(@intToFloat(f64, self.height) / 2)), + ); +} + +fn quad_lower_left(self: BoxFont, img: *pixman.Image) void { + self.rect( + img, + 0, + @floatToInt(u32, @floor(@intToFloat(f64, self.height) / 2)), + @floatToInt(u32, @ceil(@intToFloat(f64, self.width) / 2)), + self.height, + ); +} + +fn quad_lower_right(self: BoxFont, img: *pixman.Image) void { + self.rect( + img, + @floatToInt(u32, @floor(@intToFloat(f64, self.width) / 2)), + @floatToInt(u32, @floor(@intToFloat(f64, self.height) / 2)), + self.width, + self.height, + ); +} + +fn draw_quadrant(self: BoxFont, img: *pixman.Image, cp: u32) void { + const UPPER_LEFT: u8 = 1 << 0; + const UPPER_RIGHT: u8 = 1 << 1; + const LOWER_LEFT: u8 = 1 << 2; + const LOWER_RIGHT: u8 = 1 << 3; + const matrix: [10]u8 = .{ + LOWER_LEFT, + LOWER_RIGHT, + UPPER_LEFT, + UPPER_LEFT | LOWER_LEFT | LOWER_RIGHT, + UPPER_LEFT | LOWER_RIGHT, + UPPER_LEFT | UPPER_RIGHT | LOWER_LEFT, + UPPER_LEFT | UPPER_RIGHT | LOWER_RIGHT, + UPPER_RIGHT, + UPPER_RIGHT | LOWER_LEFT, + UPPER_RIGHT | LOWER_LEFT | LOWER_RIGHT, + }; + + assert(cp >= 0x2596 and cp <= 0x259f); + const idx = cp - 0x2596; + const encoded = matrix[idx]; + + if (encoded & UPPER_LEFT == UPPER_LEFT) self.quad_upper_left(img); + if (encoded & UPPER_RIGHT == UPPER_RIGHT) self.quad_upper_right(img); + if (encoded & LOWER_LEFT == LOWER_LEFT) self.quad_lower_left(img); + if (encoded & LOWER_RIGHT == LOWER_RIGHT) self.quad_lower_right(img); +} + fn draw_light_arc( self: BoxFont, alloc: Allocator, From 781571d7fbf534ea54a717b41b855449e0b45001 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 25 Nov 2022 14:26:19 -0800 Subject: [PATCH 32/40] braille --- src/font/BoxFont.zig | 97 ++++++++++++++++++++++++++++++++++++++++++++ src/font/Group.zig | 3 ++ 2 files changed, 100 insertions(+) diff --git a/src/font/BoxFont.zig b/src/font/BoxFont.zig index 21f34e856..08ece28f6 100644 --- a/src/font/BoxFont.zig +++ b/src/font/BoxFont.zig @@ -298,6 +298,8 @@ fn draw(self: BoxFont, alloc: Allocator, img: *pixman.Image, cp: u32) !void { 0x2595 => self.draw_right_one_eighth_block(img), 0x2596...0x259f => self.draw_quadrant(img, cp), + 0x2800...0x28FF => self.draw_braille(img, cp), + else => return error.InvalidCodepoint, } } @@ -1477,6 +1479,101 @@ fn draw_quadrant(self: BoxFont, img: *pixman.Image, cp: u32) void { if (encoded & LOWER_RIGHT == LOWER_RIGHT) self.quad_lower_right(img); } +fn draw_braille(self: BoxFont, img: *pixman.Image, cp: u32) void { + var w: u32 = @min(self.width / 4, self.height / 8); + var x_spacing: u32 = self.width / 4; + var y_spacing: u32 = self.height / 8; + var x_margin: u32 = x_spacing / 2; + var y_margin: u32 = y_spacing / 2; + + var x_px_left: u32 = self.width - 2 * x_margin - x_spacing - 2 * w; + var y_px_left: u32 = self.height - 2 * y_margin - 3 * y_spacing - 4 * w; + + // First, try hard to ensure the DOT width is non-zero + if (x_px_left >= 2 and y_px_left >= 4 and w == 0) { + w += 1; + x_px_left -= 2; + y_px_left -= 4; + } + + // Second, prefer a non-zero margin + if (x_px_left >= 2 and x_margin == 0) { + x_margin = 1; + x_px_left -= 2; + } + if (y_px_left >= 2 and y_margin == 0) { + y_margin = 1; + y_px_left -= 2; + } + + // Third, increase spacing + if (x_px_left >= 1) { + x_spacing += 1; + x_px_left -= 1; + } + if (y_px_left >= 3) { + y_spacing += 1; + y_px_left -= 3; + } + + // Fourth, margins (“spacing”, but on the sides) + if (x_px_left >= 2) { + x_margin += 1; + x_px_left -= 2; + } + if (y_px_left >= 2) { + y_margin += 1; + y_px_left -= 2; + } + + // Last - increase dot width + if (x_px_left >= 2 and y_px_left >= 4) { + w += 1; + x_px_left -= 2; + y_px_left -= 4; + } + + assert(x_px_left <= 1 or y_px_left <= 1); + assert(2 * x_margin + 2 * w + x_spacing <= self.width); + assert(2 * y_margin + 4 * w + 3 * y_spacing <= self.height); + + const x = [2]u32{ x_margin, x_margin + w + x_spacing }; + const y = y: { + var y: [4]u32 = undefined; + y[0] = y_margin; + y[1] = y[0] + w + y_spacing; + y[2] = y[1] + w + y_spacing; + y[3] = y[2] + w + y_spacing; + break :y y; + }; + + assert(cp >= 0x2800); + assert(cp <= 0x28ff); + const sym = cp - 0x2800; + + // Left side + if (sym & 1 > 0) + self.rect(img, x[0], y[0], x[0] + w, y[0] + w); + if (sym & 2 > 0) + self.rect(img, x[0], y[1], x[0] + w, y[1] + w); + if (sym & 4 > 0) + self.rect(img, x[0], y[2], x[0] + w, y[2] + w); + + // Right side + if (sym & 8 > 0) + self.rect(img, x[1], y[0], x[1] + w, y[0] + w); + if (sym & 16 > 0) + self.rect(img, x[1], y[1], x[1] + w, y[1] + w); + if (sym & 32 > 0) + self.rect(img, x[1], y[2], x[1] + w, y[2] + w); + + // 8-dot patterns + if (sym & 64 > 0) + self.rect(img, x[0], y[3], x[0] + w, y[3] + w); + if (sym & 128 > 0) + self.rect(img, x[1], y[3], x[1] + w, y[3] + w); +} + fn draw_light_arc( self: BoxFont, alloc: Allocator, diff --git a/src/font/Group.zig b/src/font/Group.zig index dba50c051..4b8fcf5d0 100644 --- a/src/font/Group.zig +++ b/src/font/Group.zig @@ -189,6 +189,9 @@ pub fn indexForCodepoint( // "Block Elements" block 0x2580...0x259f => true, + // "Braille" block + 0x2800...0x28FF => true, + else => false, }) { return FontIndex.initSpecial(.box); From 7676c1c52b0071acfaed219dd2f20c9714907358 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 25 Nov 2022 14:41:17 -0800 Subject: [PATCH 33/40] sextants --- conformance/blocks.zig | 67 ++++++++++++++++++++++++++ src/font/BoxFont.zig | 104 +++++++++++++++++++++++++++++++++++++++++ src/font/Group.zig | 3 ++ 3 files changed, 174 insertions(+) create mode 100644 conformance/blocks.zig diff --git a/conformance/blocks.zig b/conformance/blocks.zig new file mode 100644 index 000000000..288d1ffb7 --- /dev/null +++ b/conformance/blocks.zig @@ -0,0 +1,67 @@ +//! Delete Line (DL) - Esc [ M +const std = @import("std"); + +pub fn main() !void { + const stdout = std.io.getStdOut().writer(); + + // Box Drawing + { + try stdout.print("\x1b[4mBox Drawing\x1b[0m\n", .{}); + var i: usize = 0x2500; + var step: usize = 32; + while (i <= 0x257F) : (i += step) { + var j: usize = 0; + while (j < step) : (j += 1) { + try stdout.print("{u} ", .{@intCast(u21, i + j)}); + } + + try stdout.print("\n\n", .{}); + } + } + + // Block Elements + { + try stdout.print("\x1b[4mBlock Elements\x1b[0m\n", .{}); + var i: usize = 0x2580; + var step: usize = 32; + while (i <= 0x259f) : (i += step) { + var j: usize = 0; + while (j < step) : (j += 1) { + try stdout.print("{u} ", .{@intCast(u21, i + j)}); + } + + try stdout.print("\n\n", .{}); + } + } + + // Braille Elements + { + try stdout.print("\x1b[4mBraille\x1b[0m\n", .{}); + var i: usize = 0x2800; + var step: usize = 32; + while (i <= 0x28FF) : (i += step) { + var j: usize = 0; + while (j < step) : (j += 1) { + try stdout.print("{u} ", .{@intCast(u21, i + j)}); + } + + try stdout.print("\n\n", .{}); + } + } + + { + try stdout.print("\x1b[4mSextants\x1b[0m\n", .{}); + var i: usize = 0x1FB00; + var step: usize = 32; + const end = 0x1FB3B; + while (i <= end) : (i += step) { + var j: usize = 0; + while (j < step) : (j += 1) { + const v = i + j; + if (v <= end) try stdout.print("{u} ", .{@intCast(u21, v)}); + } + + try stdout.print("\n\n", .{}); + } + } +} diff --git a/src/font/BoxFont.zig b/src/font/BoxFont.zig index 08ece28f6..3d2668ddc 100644 --- a/src/font/BoxFont.zig +++ b/src/font/BoxFont.zig @@ -300,6 +300,8 @@ fn draw(self: BoxFont, alloc: Allocator, img: *pixman.Image, cp: u32) !void { 0x2800...0x28FF => self.draw_braille(img, cp), + 0x1FB00...0x1FB3B => self.draw_sextant(img, cp), + else => return error.InvalidCodepoint, } } @@ -1574,6 +1576,108 @@ fn draw_braille(self: BoxFont, img: *pixman.Image, cp: u32) void { self.rect(img, x[1], y[3], x[1] + w, y[3] + w); } +fn draw_sextant(self: BoxFont, img: *pixman.Image, cp: u32) void { + const UPPER_LEFT: u8 = 1 << 0; + const MIDDLE_LEFT: u8 = 1 << 1; + const LOWER_LEFT: u8 = 1 << 2; + const UPPER_RIGHT: u8 = 1 << 3; + const MIDDLE_RIGHT: u8 = 1 << 4; + const LOWER_RIGHT: u8 = 1 << 5; + + const matrix: [60]u8 = .{ + // U+1fb00 - U+1fb0f + UPPER_LEFT, + UPPER_RIGHT, + UPPER_LEFT | UPPER_RIGHT, + MIDDLE_LEFT, + UPPER_LEFT | MIDDLE_LEFT, + UPPER_RIGHT | MIDDLE_LEFT, + UPPER_LEFT | UPPER_RIGHT | MIDDLE_LEFT, + MIDDLE_RIGHT, + UPPER_LEFT | MIDDLE_RIGHT, + UPPER_RIGHT | MIDDLE_RIGHT, + UPPER_LEFT | UPPER_RIGHT | MIDDLE_RIGHT, + MIDDLE_LEFT | MIDDLE_RIGHT, + UPPER_LEFT | MIDDLE_LEFT | MIDDLE_RIGHT, + UPPER_RIGHT | MIDDLE_LEFT | MIDDLE_RIGHT, + UPPER_LEFT | UPPER_RIGHT | MIDDLE_LEFT | MIDDLE_RIGHT, + LOWER_LEFT, + + // U+1fb10 - U+1fb1f + UPPER_LEFT | LOWER_LEFT, + UPPER_RIGHT | LOWER_LEFT, + UPPER_LEFT | UPPER_RIGHT | LOWER_LEFT, + MIDDLE_LEFT | LOWER_LEFT, + UPPER_RIGHT | MIDDLE_LEFT | LOWER_LEFT, + UPPER_LEFT | UPPER_RIGHT | MIDDLE_LEFT | LOWER_LEFT, + MIDDLE_RIGHT | LOWER_LEFT, + UPPER_LEFT | MIDDLE_RIGHT | LOWER_LEFT, + UPPER_RIGHT | MIDDLE_RIGHT | LOWER_LEFT, + UPPER_LEFT | UPPER_RIGHT | MIDDLE_RIGHT | LOWER_LEFT, + MIDDLE_LEFT | MIDDLE_RIGHT | LOWER_LEFT, + UPPER_LEFT | MIDDLE_LEFT | MIDDLE_RIGHT | LOWER_LEFT, + UPPER_RIGHT | MIDDLE_LEFT | MIDDLE_RIGHT | LOWER_LEFT, + UPPER_LEFT | UPPER_RIGHT | MIDDLE_LEFT | MIDDLE_RIGHT | LOWER_LEFT, + LOWER_RIGHT, + UPPER_LEFT | LOWER_RIGHT, + + // U+1fb20 - U+1fb2f + UPPER_RIGHT | LOWER_RIGHT, + UPPER_LEFT | UPPER_RIGHT | LOWER_RIGHT, + MIDDLE_LEFT | LOWER_RIGHT, + UPPER_LEFT | MIDDLE_LEFT | LOWER_RIGHT, + UPPER_RIGHT | MIDDLE_LEFT | LOWER_RIGHT, + UPPER_LEFT | UPPER_RIGHT | MIDDLE_LEFT | LOWER_RIGHT, + MIDDLE_RIGHT | LOWER_RIGHT, + UPPER_LEFT | MIDDLE_RIGHT | LOWER_RIGHT, + UPPER_LEFT | UPPER_RIGHT | MIDDLE_RIGHT | LOWER_RIGHT, + MIDDLE_LEFT | MIDDLE_RIGHT | LOWER_RIGHT, + UPPER_LEFT | MIDDLE_LEFT | MIDDLE_RIGHT | LOWER_RIGHT, + UPPER_RIGHT | MIDDLE_LEFT | MIDDLE_RIGHT | LOWER_RIGHT, + UPPER_LEFT | UPPER_RIGHT | MIDDLE_LEFT | MIDDLE_RIGHT | LOWER_RIGHT, + LOWER_LEFT | LOWER_RIGHT, + UPPER_LEFT | LOWER_LEFT | LOWER_RIGHT, + UPPER_RIGHT | LOWER_LEFT | LOWER_RIGHT, + + // U+1fb30 - U+1fb3b + UPPER_LEFT | UPPER_RIGHT | LOWER_LEFT | LOWER_RIGHT, + MIDDLE_LEFT | LOWER_LEFT | LOWER_RIGHT, + UPPER_LEFT | MIDDLE_LEFT | LOWER_LEFT | LOWER_RIGHT, + UPPER_RIGHT | MIDDLE_LEFT | LOWER_LEFT | LOWER_RIGHT, + UPPER_LEFT | UPPER_RIGHT | MIDDLE_LEFT | LOWER_LEFT | LOWER_RIGHT, + MIDDLE_RIGHT | LOWER_LEFT | LOWER_RIGHT, + UPPER_LEFT | MIDDLE_RIGHT | LOWER_LEFT | LOWER_RIGHT, + UPPER_RIGHT | MIDDLE_RIGHT | LOWER_LEFT | LOWER_RIGHT, + UPPER_LEFT | UPPER_RIGHT | MIDDLE_RIGHT | LOWER_LEFT | LOWER_RIGHT, + MIDDLE_LEFT | MIDDLE_RIGHT | LOWER_LEFT | LOWER_RIGHT, + UPPER_LEFT | MIDDLE_LEFT | MIDDLE_RIGHT | LOWER_LEFT | LOWER_RIGHT, + UPPER_RIGHT | MIDDLE_LEFT | MIDDLE_RIGHT | LOWER_LEFT | LOWER_RIGHT, + }; + + assert(cp >= 0x1fb00 and cp <= 0x1fb3b); + const idx = cp - 0x1fb00; + const encoded = matrix[idx]; + + const x_halfs: [2]u32 = .{ + @floatToInt(u32, @round(@intToFloat(f64, self.width) / 2)), + @floatToInt(u32, @intToFloat(f64, self.width) / 2), + }; + + const y_thirds: [2]u32 = switch (@mod(self.height, 3)) { + 0 => .{ self.height / 3, 2 * self.height / 3 }, + 1 => .{ self.height / 3, 2 * self.height / 3 + 1 }, + 2 => .{ self.height / 3 + 1, 2 * self.height / 3 }, + else => unreachable, + }; + + if (encoded & UPPER_LEFT > 0) self.rect(img, 0, 0, x_halfs[0], y_thirds[0]); + if (encoded & MIDDLE_LEFT > 0) self.rect(img, 0, y_thirds[0], x_halfs[0], y_thirds[1]); + if (encoded & LOWER_LEFT > 0) self.rect(img, 0, y_thirds[1], x_halfs[0], self.height); + if (encoded & UPPER_RIGHT > 0) self.rect(img, x_halfs[1], 0, self.width, y_thirds[0]); + if (encoded & MIDDLE_RIGHT > 0) self.rect(img, x_halfs[1], y_thirds[0], self.width, y_thirds[1]); + if (encoded & LOWER_RIGHT > 0) self.rect(img, x_halfs[1], y_thirds[1], self.width, self.height); +} + fn draw_light_arc( self: BoxFont, alloc: Allocator, diff --git a/src/font/Group.zig b/src/font/Group.zig index 4b8fcf5d0..60d372c9e 100644 --- a/src/font/Group.zig +++ b/src/font/Group.zig @@ -192,6 +192,9 @@ pub fn indexForCodepoint( // "Braille" block 0x2800...0x28FF => true, + // "Symbols for Legacy Computing" block + 0x1FB00...0x1FB3B => true, + else => false, }) { return FontIndex.initSpecial(.box); From 2c9b3e2f9b699479979dff92670335bb0fe09245 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 25 Nov 2022 15:02:12 -0800 Subject: [PATCH 34/40] wedge triangles --- conformance/blocks.zig | 39 ++++ pkg/pixman/image.zig | 33 ++++ pkg/pixman/types.zig | 6 + src/font/BoxFont.zig | 395 +++++++++++++++++++++++++++++++++++++++-- src/font/Group.zig | 6 + 5 files changed, 468 insertions(+), 11 deletions(-) diff --git a/conformance/blocks.zig b/conformance/blocks.zig index 288d1ffb7..a92f73893 100644 --- a/conformance/blocks.zig +++ b/conformance/blocks.zig @@ -64,4 +64,43 @@ pub fn main() !void { try stdout.print("\n\n", .{}); } } + + { + try stdout.print("\x1b[4mWedge Triangles\x1b[0m\n", .{}); + { + var i: usize = 0x1FB3C; + const end = 0x1FB40; + while (i <= end) : (i += 1) { + try stdout.print("{u} ", .{@intCast(u21, i)}); + } + } + { + var i: usize = 0x1FB47; + const end = 0x1FB4B; + while (i <= end) : (i += 1) { + try stdout.print("{u} ", .{@intCast(u21, i)}); + } + } + { + var i: usize = 0x1FB57; + const end = 0x1FB5B; + while (i <= end) : (i += 1) { + try stdout.print("{u} ", .{@intCast(u21, i)}); + } + } + { + var i: usize = 0x1FB62; + const end = 0x1FB66; + while (i <= end) : (i += 1) { + try stdout.print("{u} ", .{@intCast(u21, i)}); + } + } + { + var i: usize = 0x1FB6C; + const end = 0x1FB6F; + while (i <= end) : (i += 1) { + try stdout.print("{u} ", .{@intCast(u21, i)}); + } + } + } } diff --git a/pkg/pixman/image.zig b/pkg/pixman/image.zig index 22c55c7ac..b285744cd 100644 --- a/pkg/pixman/image.zig +++ b/pkg/pixman/image.zig @@ -19,6 +19,14 @@ pub const Image = opaque { )) orelse return pixman.Error.PixmanFailure; } + pub fn createSolidFill( + color: pixman.Color, + ) pixman.Error!*Image { + return @ptrCast(?*Image, c.pixman_image_create_solid_fill( + @ptrCast(*const c.pixman_color_t, &color), + )) orelse return pixman.Error.PixmanFailure; + } + pub fn unref(self: *Image) bool { return c.pixman_image_unref(@ptrCast(*c.pixman_image_t, self)) == 1; } @@ -90,6 +98,31 @@ pub const Image = opaque { y_off, ); } + + pub fn compositeTriangles( + self: *Image, + op: pixman.Op, + src: *Image, + mask_format: pixman.FormatCode, + x_src: c_int, + y_src: c_int, + x_dst: c_int, + y_dst: c_int, + tris: []const pixman.Triangle, + ) void { + c.pixman_composite_triangles( + @enumToInt(op), + @ptrCast(*c.pixman_image_t, src), + @ptrCast(*c.pixman_image_t, self), + @enumToInt(mask_format), + x_src, + y_src, + x_dst, + y_dst, + @intCast(c_int, tris.len), + @ptrCast([*c]const c.pixman_triangle_t, tris.ptr), + ); + } }; test "create and destroy" { diff --git a/pkg/pixman/types.zig b/pkg/pixman/types.zig index acebba68e..f1ab9235d 100644 --- a/pkg/pixman/types.zig +++ b/pkg/pixman/types.zig @@ -93,6 +93,12 @@ pub const LineFixed = extern struct { p2: PointFixed, }; +pub const Triangle = extern struct { + p1: PointFixed, + p2: PointFixed, + p3: PointFixed, +}; + pub const Trapezoid = extern struct { top: Fixed, bottom: Fixed, diff --git a/src/font/BoxFont.zig b/src/font/BoxFont.zig index 3d2668ddc..94977fbbe 100644 --- a/src/font/BoxFont.zig +++ b/src/font/BoxFont.zig @@ -302,6 +302,13 @@ fn draw(self: BoxFont, alloc: Allocator, img: *pixman.Image, cp: u32) !void { 0x1FB00...0x1FB3B => self.draw_sextant(img, cp), + 0x1fb3c...0x1fb40, + 0x1fb47...0x1fb4b, + 0x1fb57...0x1fb5b, + 0x1fb62...0x1fb66, + 0x1fb6c...0x1fb6f, + => try self.draw_wedge_triangle(img, cp), + else => return error.InvalidCodepoint, } } @@ -1658,17 +1665,8 @@ fn draw_sextant(self: BoxFont, img: *pixman.Image, cp: u32) void { const idx = cp - 0x1fb00; const encoded = matrix[idx]; - const x_halfs: [2]u32 = .{ - @floatToInt(u32, @round(@intToFloat(f64, self.width) / 2)), - @floatToInt(u32, @intToFloat(f64, self.width) / 2), - }; - - const y_thirds: [2]u32 = switch (@mod(self.height, 3)) { - 0 => .{ self.height / 3, 2 * self.height / 3 }, - 1 => .{ self.height / 3, 2 * self.height / 3 + 1 }, - 2 => .{ self.height / 3 + 1, 2 * self.height / 3 }, - else => unreachable, - }; + const x_halfs = self.xHalfs(); + const y_thirds = self.yThirds(); if (encoded & UPPER_LEFT > 0) self.rect(img, 0, 0, x_halfs[0], y_thirds[0]); if (encoded & MIDDLE_LEFT > 0) self.rect(img, 0, y_thirds[0], x_halfs[0], y_thirds[1]); @@ -1678,6 +1676,381 @@ fn draw_sextant(self: BoxFont, img: *pixman.Image, cp: u32) void { if (encoded & LOWER_RIGHT > 0) self.rect(img, x_halfs[1], y_thirds[1], self.width, self.height); } +fn xHalfs(self: BoxFont) [2]u32 { + return .{ + @floatToInt(u32, @round(@intToFloat(f64, self.width) / 2)), + @floatToInt(u32, @intToFloat(f64, self.width) / 2), + }; +} + +fn yThirds(self: BoxFont) [2]u32 { + return switch (@mod(self.height, 3)) { + 0 => .{ self.height / 3, 2 * self.height / 3 }, + 1 => .{ self.height / 3, 2 * self.height / 3 + 1 }, + 2 => .{ self.height / 3 + 1, 2 * self.height / 3 }, + else => unreachable, + }; +} + +fn draw_wedge_triangle(self: BoxFont, img: *pixman.Image, 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, + } + + const tris = &[_]pixman.Triangle{ + .{ + .p1 = .{ .x = pixman.Fixed.init(p1_x), .y = pixman.Fixed.init(p1_y) }, + .p2 = .{ .x = pixman.Fixed.init(p2_x), .y = pixman.Fixed.init(p2_y) }, + .p3 = .{ .x = pixman.Fixed.init(p3_x), .y = pixman.Fixed.init(p3_y) }, + }, + }; + + const src = try pixman.Image.createSolidFill(white); + defer _ = src.unref(); + img.compositeTriangles(.over, src, .a8, 0, 0, 0, 0, tris); +} + fn draw_light_arc( self: BoxFont, alloc: Allocator, diff --git a/src/font/Group.zig b/src/font/Group.zig index 60d372c9e..0746765e9 100644 --- a/src/font/Group.zig +++ b/src/font/Group.zig @@ -194,6 +194,12 @@ pub fn indexForCodepoint( // "Symbols for Legacy Computing" block 0x1FB00...0x1FB3B => true, + 0x1FB3C...0x1FB40, + 0x1FB47...0x1FB4B, + 0x1FB57...0x1FB5B, + 0x1FB62...0x1FB66, + 0x1FB6C...0x1FB6F, + => true, else => false, }) { From 120dfb40431766d58e9f022e9b3226802da272d3 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 25 Nov 2022 15:12:34 -0800 Subject: [PATCH 35/40] more wedges --- conformance/blocks.zig | 43 ++++++++++-------------------------------- pkg/pixman/image.zig | 30 +++++++++++++++++++++++++++++ src/font/BoxFont.zig | 37 +++++++++++++++++++++++++++++++----- src/font/Group.zig | 8 ++++++++ 4 files changed, 80 insertions(+), 38 deletions(-) diff --git a/conformance/blocks.zig b/conformance/blocks.zig index a92f73893..cd4ae5b0b 100644 --- a/conformance/blocks.zig +++ b/conformance/blocks.zig @@ -67,40 +67,17 @@ pub fn main() !void { { try stdout.print("\x1b[4mWedge Triangles\x1b[0m\n", .{}); - { - var i: usize = 0x1FB3C; - const end = 0x1FB40; - while (i <= end) : (i += 1) { - try stdout.print("{u} ", .{@intCast(u21, i)}); - } - } - { - var i: usize = 0x1FB47; - const end = 0x1FB4B; - while (i <= end) : (i += 1) { - try stdout.print("{u} ", .{@intCast(u21, i)}); - } - } - { - var i: usize = 0x1FB57; - const end = 0x1FB5B; - while (i <= end) : (i += 1) { - try stdout.print("{u} ", .{@intCast(u21, i)}); - } - } - { - var i: usize = 0x1FB62; - const end = 0x1FB66; - while (i <= end) : (i += 1) { - try stdout.print("{u} ", .{@intCast(u21, i)}); - } - } - { - var i: usize = 0x1FB6C; - const end = 0x1FB6F; - while (i <= end) : (i += 1) { - try stdout.print("{u} ", .{@intCast(u21, i)}); + var i: usize = 0x1FB3C; + var step: usize = 32; + const end = 0x1FB6B; + while (i <= end) : (i += step) { + var j: usize = 0; + while (j < step) : (j += 1) { + const v = i + j; + if (v <= end) try stdout.print("{u} ", .{@intCast(u21, v)}); } + + try stdout.print("\n\n", .{}); } } } diff --git a/pkg/pixman/image.zig b/pkg/pixman/image.zig index b285744cd..caccc65a9 100644 --- a/pkg/pixman/image.zig +++ b/pkg/pixman/image.zig @@ -99,6 +99,36 @@ pub const Image = opaque { ); } + pub fn composite( + self: *Image, + op: pixman.Op, + src: *Image, + mask: ?*Image, + src_x: i16, + src_y: i16, + mask_x: i16, + mask_y: i16, + dest_x: i16, + dest_y: i16, + width: u16, + height: u16, + ) void { + c.pixman_image_composite( + @enumToInt(op), + @ptrCast(*c.pixman_image_t, src), + @ptrCast(?*c.pixman_image_t, mask), + @ptrCast(*c.pixman_image_t, self), + src_x, + src_y, + mask_x, + mask_y, + dest_x, + dest_y, + width, + height, + ); + } + pub fn compositeTriangles( self: *Image, op: pixman.Op, diff --git a/src/font/BoxFont.zig b/src/font/BoxFont.zig index 94977fbbe..d055c196b 100644 --- a/src/font/BoxFont.zig +++ b/src/font/BoxFont.zig @@ -302,13 +302,20 @@ fn draw(self: BoxFont, alloc: Allocator, img: *pixman.Image, cp: u32) !void { 0x1FB00...0x1FB3B => self.draw_sextant(img, cp), - 0x1fb3c...0x1fb40, - 0x1fb47...0x1fb4b, - 0x1fb57...0x1fb5b, - 0x1fb62...0x1fb66, - 0x1fb6c...0x1fb6f, + 0x1FB3C...0x1FB40, + 0x1FB47...0x1FB4B, + 0x1FB57...0x1FB5B, + 0x1FB62...0x1FB66, + 0x1FB6C...0x1FB6F, => try self.draw_wedge_triangle(img, cp), + 0x1FB41...0x1FB45, + 0x1FB4C...0x1FB50, + 0x1FB52...0x1FB56, + 0x1FB5D...0x1FB61, + 0x1FB68...0x1FB6B, + => try self.draw_wedge_triangle_inverted(img, cp), + else => return error.InvalidCodepoint, } } @@ -2051,6 +2058,26 @@ fn draw_wedge_triangle(self: BoxFont, img: *pixman.Image, cp: u32) !void { img.compositeTriangles(.over, src, .a8, 0, 0, 0, 0, tris); } +fn draw_wedge_triangle_inverted(self: BoxFont, img: *pixman.Image, cp: u32) !void { + try self.draw_wedge_triangle(img, cp); + + const src = try pixman.Image.createSolidFill(white); + defer _ = src.unref(); + img.composite( + .out, + src, + null, + 0, + 0, + 0, + 0, + 0, + 0, + @intCast(u16, self.width), + @intCast(u16, self.height), + ); +} + fn draw_light_arc( self: BoxFont, alloc: Allocator, diff --git a/src/font/Group.zig b/src/font/Group.zig index 0746765e9..60d9ab845 100644 --- a/src/font/Group.zig +++ b/src/font/Group.zig @@ -194,6 +194,7 @@ pub fn indexForCodepoint( // "Symbols for Legacy Computing" block 0x1FB00...0x1FB3B => true, + 0x1FB3C...0x1FB40, 0x1FB47...0x1FB4B, 0x1FB57...0x1FB5B, @@ -201,6 +202,13 @@ pub fn indexForCodepoint( 0x1FB6C...0x1FB6F, => true, + 0x1FB41...0x1FB45, + 0x1FB4C...0x1FB50, + 0x1FB52...0x1FB56, + 0x1FB5D...0x1FB61, + 0x1FB68...0x1FB6B, + => true, + else => false, }) { return FontIndex.initSpecial(.box); From d2727b1f5cd3167fd1781ea3e7bbde78304df1be Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 25 Nov 2022 15:18:36 -0800 Subject: [PATCH 36/40] more wedges --- src/font/BoxFont.zig | 42 ++++++++++++++++++++++++++++++++++++++++++ src/font/Group.zig | 8 ++++++++ 2 files changed, 50 insertions(+) diff --git a/src/font/BoxFont.zig b/src/font/BoxFont.zig index d055c196b..91001ffb2 100644 --- a/src/font/BoxFont.zig +++ b/src/font/BoxFont.zig @@ -316,6 +316,22 @@ fn draw(self: BoxFont, alloc: Allocator, img: *pixman.Image, cp: u32) !void { 0x1FB68...0x1FB6B, => try self.draw_wedge_triangle_inverted(img, cp), + 0x1FB46, + 0x1FB51, + 0x1FB5C, + 0x1FB67, + => try self.draw_wedge_triangle_and_box(img, cp), + + 0x1FB9A => { + try self.draw_wedge_triangle(img, 0x1fb6d); + try self.draw_wedge_triangle(img, 0x1fb6f); + }, + + 0x1FB9B => { + try self.draw_wedge_triangle(img, 0x1fb6c); + try self.draw_wedge_triangle(img, 0x1fb6e); + }, + else => return error.InvalidCodepoint, } } @@ -2078,6 +2094,32 @@ fn draw_wedge_triangle_inverted(self: BoxFont, img: *pixman.Image, cp: u32) !voi ); } +fn draw_wedge_triangle_and_box(self: BoxFont, img: *pixman.Image, cp: u32) !void { + try self.draw_wedge_triangle(img, cp); + + const y_thirds = self.yThirds(); + const box: pixman.Box32 = switch (cp) { + 0x1fb46, 0x1fb51 => .{ + .x1 = 0, + .y1 = @intCast(i32, y_thirds[1]), + .x2 = @intCast(i32, self.width), + .y2 = @intCast(i32, self.height), + }, + + 0x1fb5c, 0x1fb67 => .{ + .x1 = 0, + .y1 = 0, + .x2 = @intCast(i32, self.width), + .y2 = @intCast(i32, y_thirds[0]), + }, + + else => unreachable, + }; + + const boxes = &[_]pixman.Box32{box}; + img.fillBoxes(.src, white, boxes) catch {}; +} + fn draw_light_arc( self: BoxFont, alloc: Allocator, diff --git a/src/font/Group.zig b/src/font/Group.zig index 60d9ab845..13f2190b3 100644 --- a/src/font/Group.zig +++ b/src/font/Group.zig @@ -209,6 +209,14 @@ pub fn indexForCodepoint( 0x1FB68...0x1FB6B, => true, + 0x1FB46, + 0x1FB51, + 0x1FB5C, + 0x1FB67, + 0x1FB9A, + 0x1FB9B, + => true, + else => false, }) { return FontIndex.initSpecial(.box); From 4cac375fcf32d0b3c5d4fda2fa6fe0db9ce8a321 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 25 Nov 2022 15:22:59 -0800 Subject: [PATCH 37/40] more --- conformance/blocks.zig | 16 ++++++++++++++++ src/font/BoxFont.zig | 20 ++++++++++++++++++++ src/font/Group.zig | 2 ++ 3 files changed, 38 insertions(+) diff --git a/conformance/blocks.zig b/conformance/blocks.zig index cd4ae5b0b..9cc3881d9 100644 --- a/conformance/blocks.zig +++ b/conformance/blocks.zig @@ -80,4 +80,20 @@ pub fn main() !void { try stdout.print("\n\n", .{}); } } + + { + try stdout.print("\x1b[4mOther\x1b[0m\n", .{}); + var i: usize = 0x1FB70; + var step: usize = 32; + const end = 0x1FB8B; + while (i <= end) : (i += step) { + var j: usize = 0; + while (j < step) : (j += 1) { + const v = i + j; + if (v <= end) try stdout.print("{u} ", .{@intCast(u21, v)}); + } + + try stdout.print("\n\n", .{}); + } + } } diff --git a/src/font/BoxFont.zig b/src/font/BoxFont.zig index 91001ffb2..4cea89869 100644 --- a/src/font/BoxFont.zig +++ b/src/font/BoxFont.zig @@ -332,6 +332,26 @@ fn draw(self: BoxFont, alloc: Allocator, img: *pixman.Image, cp: u32) !void { try self.draw_wedge_triangle(img, 0x1fb6e); }, + 0x1FB70 => self.draw_vertical_one_eighth_block_n(img, 1), + 0x1FB71 => self.draw_vertical_one_eighth_block_n(img, 2), + 0x1FB72 => self.draw_vertical_one_eighth_block_n(img, 3), + 0x1FB73 => self.draw_vertical_one_eighth_block_n(img, 4), + 0x1FB74 => self.draw_vertical_one_eighth_block_n(img, 5), + 0x1FB75 => self.draw_vertical_one_eighth_block_n(img, 6), + + 0x1FB76 => self.draw_horizontal_one_eighth_block_n(img, 1), + 0x1FB77 => self.draw_horizontal_one_eighth_block_n(img, 2), + 0x1FB78 => self.draw_horizontal_one_eighth_block_n(img, 3), + 0x1FB79 => self.draw_horizontal_one_eighth_block_n(img, 4), + 0x1FB7A => self.draw_horizontal_one_eighth_block_n(img, 5), + 0x1FB7B => self.draw_horizontal_one_eighth_block_n(img, 6), + + 0x1fb82 => self.draw_upper_one_quarter_block(img), + 0x1fb83 => self.draw_upper_three_eighths_block(img), + 0x1fb84 => self.draw_upper_five_eighths_block(img), + 0x1fb85 => self.draw_upper_three_quarters_block(img), + 0x1fb86 => self.draw_upper_seven_eighths_block(img), + else => return error.InvalidCodepoint, } } diff --git a/src/font/Group.zig b/src/font/Group.zig index 13f2190b3..935af8db1 100644 --- a/src/font/Group.zig +++ b/src/font/Group.zig @@ -217,6 +217,8 @@ pub fn indexForCodepoint( 0x1FB9B, => true, + 0x1FB70...0x1FB8B => true, + else => false, }) { return FontIndex.initSpecial(.box); From 1062a39681b02b6e6b4a5901b2a5d83273886174 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 25 Nov 2022 15:28:47 -0800 Subject: [PATCH 38/40] more glyphs --- src/font/BoxFont.zig | 95 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/src/font/BoxFont.zig b/src/font/BoxFont.zig index 4cea89869..c0cd7df91 100644 --- a/src/font/BoxFont.zig +++ b/src/font/BoxFont.zig @@ -352,6 +352,19 @@ fn draw(self: BoxFont, alloc: Allocator, img: *pixman.Image, cp: u32) !void { 0x1fb85 => self.draw_upper_three_quarters_block(img), 0x1fb86 => self.draw_upper_seven_eighths_block(img), + 0x1fb7c => self.draw_left_and_lower_one_eighth_block(img), + 0x1fb7d => self.draw_left_and_upper_one_eighth_block(img), + 0x1fb7e => self.draw_right_and_upper_one_eighth_block(img), + 0x1fb7f => self.draw_right_and_lower_one_eighth_block(img), + 0x1fb80 => self.draw_upper_and_lower_one_eighth_block(img), + 0x1fb81 => self.draw_horizontal_one_eighth_1358_block(img), + + 0x1fb87 => self.draw_right_one_quarter_block(img), + 0x1fb88 => self.draw_right_three_eighths_block(img), + 0x1fb89 => self.draw_right_five_eighths_block(img), + 0x1fb8a => self.draw_right_three_quarters_block(img), + 0x1fb8b => self.draw_right_seven_eighths_block(img), + else => return error.InvalidCodepoint, } } @@ -1463,6 +1476,88 @@ fn draw_right_one_eighth_block(self: BoxFont, img: *pixman.Image) void { ); } +fn draw_left_and_lower_one_eighth_block(self: BoxFont, img: *pixman.Image) void { + self.draw_left_one_eighth_block(img); + self.draw_lower_one_eighth_block(img); +} + +fn draw_left_and_upper_one_eighth_block(self: BoxFont, img: *pixman.Image) void { + self.draw_left_one_eighth_block(img); + self.draw_upper_one_eighth_block(img); +} + +fn draw_right_and_upper_one_eighth_block(self: BoxFont, img: *pixman.Image) void { + self.draw_right_one_eighth_block(img); + self.draw_upper_one_eighth_block(img); +} + +fn draw_right_and_lower_one_eighth_block(self: BoxFont, img: *pixman.Image) void { + self.draw_right_one_eighth_block(img); + self.draw_lower_one_eighth_block(img); +} + +fn draw_upper_and_lower_one_eighth_block(self: BoxFont, img: *pixman.Image) void { + self.draw_upper_one_eighth_block(img); + self.draw_lower_one_eighth_block(img); +} + +fn draw_horizontal_one_eighth_1358_block(self: BoxFont, img: *pixman.Image) void { + self.draw_upper_one_eighth_block(img); + self.draw_horizontal_one_eighth_block_n(img, 2); + self.draw_horizontal_one_eighth_block_n(img, 4); + self.draw_lower_one_eighth_block(img); +} + +fn draw_right_one_quarter_block(self: BoxFont, img: *pixman.Image) void { + self.rect( + img, + self.width - @floatToInt(u32, @round(@intToFloat(f64, self.width) / 4)), + 0, + self.width, + self.height, + ); +} + +fn draw_right_three_quarters_block(self: BoxFont, img: *pixman.Image) void { + self.rect( + img, + self.width - @floatToInt(u32, @round(3 * @intToFloat(f64, self.width) / 4)), + 0, + self.width, + self.height, + ); +} + +fn draw_right_three_eighths_block(self: BoxFont, img: *pixman.Image) void { + self.rect( + img, + self.width - @floatToInt(u32, @round(3 * @intToFloat(f64, self.width) / 8)), + 0, + self.width, + self.height, + ); +} + +fn draw_right_five_eighths_block(self: BoxFont, img: *pixman.Image) void { + self.rect( + img, + self.width - @floatToInt(u32, @round(5 * @intToFloat(f64, self.width) / 8)), + 0, + self.width, + self.height, + ); +} + +fn draw_right_seven_eighths_block(self: BoxFont, img: *pixman.Image) void { + self.rect( + img, + self.width - @floatToInt(u32, @round(7 * @intToFloat(f64, self.width) / 8)), + 0, + self.width, + self.height, + ); +} + fn quad_upper_left(self: BoxFont, img: *pixman.Image) void { self.rect( img, From f01930695a85770818f8c4710d0fe3191461873d Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 25 Nov 2022 15:30:03 -0800 Subject: [PATCH 39/40] metal: continue to render if single cell fails --- src/renderer/Metal.zig | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/renderer/Metal.zig b/src/renderer/Metal.zig index 0112b0926..9381d70c9 100644 --- a/src/renderer/Metal.zig +++ b/src/renderer/Metal.zig @@ -772,7 +772,7 @@ fn rebuildCells( var iter = self.font_shaper.runIterator(self.font_group, row); while (try iter.next(self.alloc)) |run| { for (try self.font_shaper.shape(run)) |shaper_cell| { - assert(try self.updateCell( + if (self.updateCell( term_selection, screen, row.getCell(shaper_cell.x), @@ -780,7 +780,15 @@ fn rebuildCells( run, shaper_cell.x, y, - )); + )) |update| { + assert(update); + } else |err| { + log.warn("error building cell, will be invalid x={} y={}, err={}", .{ + shaper_cell.x, + y, + err, + }); + } } } From b365ccca0bce69734ec64e939369b0e328b7fcfe Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Fri, 25 Nov 2022 15:38:45 -0800 Subject: [PATCH 40/40] fix comment --- conformance/blocks.zig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conformance/blocks.zig b/conformance/blocks.zig index 9cc3881d9..6ba5e0663 100644 --- a/conformance/blocks.zig +++ b/conformance/blocks.zig @@ -1,4 +1,4 @@ -//! Delete Line (DL) - Esc [ M +//! Outputs various box glyphs for testing. const std = @import("std"); pub fn main() !void {