mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-07-19 18:26:13 +03:00
Merge pull request #56 from mitchellh/pixman
Procedurally generate and support box-drawing glyphs
This commit is contained in:
3
.gitmodules
vendored
3
.gitmodules
vendored
@ -31,3 +31,6 @@
|
|||||||
[submodule "vendor/cimgui"]
|
[submodule "vendor/cimgui"]
|
||||||
path = vendor/cimgui
|
path = vendor/cimgui
|
||||||
url = https://github.com/cimgui/cimgui.git
|
url = https://github.com/cimgui/cimgui.git
|
||||||
|
[submodule "vendor/pixman"]
|
||||||
|
path = vendor/pixman
|
||||||
|
url = https://github.com/freedesktop/pixman.git
|
||||||
|
@ -12,6 +12,7 @@ const libuv = @import("pkg/libuv/build.zig");
|
|||||||
const libpng = @import("pkg/libpng/build.zig");
|
const libpng = @import("pkg/libpng/build.zig");
|
||||||
const macos = @import("pkg/macos/build.zig");
|
const macos = @import("pkg/macos/build.zig");
|
||||||
const objc = @import("pkg/objc/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 stb_image_resize = @import("pkg/stb_image_resize/build.zig");
|
||||||
const utf8proc = @import("pkg/utf8proc/build.zig");
|
const utf8proc = @import("pkg/utf8proc/build.zig");
|
||||||
const zlib = @import("pkg/zlib/build.zig");
|
const zlib = @import("pkg/zlib/build.zig");
|
||||||
@ -211,6 +212,7 @@ fn addDeps(
|
|||||||
step.addPackage(imgui.pkg);
|
step.addPackage(imgui.pkg);
|
||||||
step.addPackage(glfw.pkg);
|
step.addPackage(glfw.pkg);
|
||||||
step.addPackage(libuv.pkg);
|
step.addPackage(libuv.pkg);
|
||||||
|
step.addPackage(pixman.pkg);
|
||||||
step.addPackage(stb_image_resize.pkg);
|
step.addPackage(stb_image_resize.pkg);
|
||||||
step.addPackage(utf8proc.pkg);
|
step.addPackage(utf8proc.pkg);
|
||||||
|
|
||||||
@ -263,6 +265,7 @@ fn addDeps(
|
|||||||
step.linkSystemLibrary("harfbuzz");
|
step.linkSystemLibrary("harfbuzz");
|
||||||
step.linkSystemLibrary("libpng");
|
step.linkSystemLibrary("libpng");
|
||||||
step.linkSystemLibrary("libuv");
|
step.linkSystemLibrary("libuv");
|
||||||
|
step.linkSystemLibrary("pixman-1");
|
||||||
step.linkSystemLibrary("zlib");
|
step.linkSystemLibrary("zlib");
|
||||||
|
|
||||||
if (enable_fontconfig) step.linkSystemLibrary("fontconfig");
|
if (enable_fontconfig) step.linkSystemLibrary("fontconfig");
|
||||||
@ -307,6 +310,10 @@ fn addDeps(
|
|||||||
});
|
});
|
||||||
system_sdk.include(b, harfbuzz_step, .{});
|
system_sdk.include(b, harfbuzz_step, .{});
|
||||||
|
|
||||||
|
// Pixman
|
||||||
|
const pixman_step = try pixman.link(b, step, .{});
|
||||||
|
_ = pixman_step;
|
||||||
|
|
||||||
// Libuv
|
// Libuv
|
||||||
const libuv_step = try libuv.link(b, step);
|
const libuv_step = try libuv.link(b, step);
|
||||||
system_sdk.include(b, libuv_step, .{});
|
system_sdk.include(b, libuv_step, .{});
|
||||||
|
99
conformance/blocks.zig
Normal file
99
conformance/blocks.zig
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
//! Outputs various box glyphs for testing.
|
||||||
|
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", .{});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
try stdout.print("\x1b[4mWedge Triangles\x1b[0m\n", .{});
|
||||||
|
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", .{});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
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", .{});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -31,6 +31,7 @@
|
|||||||
, libXi
|
, libXi
|
||||||
, libXinerama
|
, libXinerama
|
||||||
, libXrandr
|
, libXrandr
|
||||||
|
, pixman
|
||||||
, zlib
|
, zlib
|
||||||
}:
|
}:
|
||||||
let
|
let
|
||||||
@ -87,6 +88,7 @@ in mkShell rec {
|
|||||||
harfbuzz
|
harfbuzz
|
||||||
libpng
|
libpng
|
||||||
libuv
|
libuv
|
||||||
|
pixman
|
||||||
zlib
|
zlib
|
||||||
|
|
||||||
libX11
|
libX11
|
||||||
|
141
pkg/pixman/build.zig
Normal file
141
pkg/pixman/build.zig
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
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();
|
||||||
|
|
||||||
|
const tests = b.addTestExe("pixman-test", "main.zig");
|
||||||
|
tests.setBuildMode(mode);
|
||||||
|
tests.setTarget(target);
|
||||||
|
_ = 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",
|
||||||
|
|
||||||
|
// There is ubsan
|
||||||
|
"-fno-sanitize=undefined",
|
||||||
|
"-fno-sanitize-trap=undefined",
|
||||||
|
});
|
||||||
|
|
||||||
|
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",
|
||||||
|
};
|
3
pkg/pixman/c.zig
Normal file
3
pkg/pixman/c.zig
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
pub usingnamespace @cImport({
|
||||||
|
@cInclude("pixman.h");
|
||||||
|
});
|
4
pkg/pixman/error.zig
Normal file
4
pkg/pixman/error.zig
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
pub const Error = error{
|
||||||
|
// Pixman doesn't really have errors so we just have a single error.
|
||||||
|
PixmanFailure,
|
||||||
|
};
|
118
pkg/pixman/format.zig
Normal file
118
pkg/pixman/format.zig
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
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) & 3);
|
||||||
|
return ((v1 & (v2 - 1)) << v3);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
test "bpp" {
|
||||||
|
const testing = std.testing;
|
||||||
|
|
||||||
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
207
pkg/pixman/image.zig
Normal file
207
pkg/pixman/image.zig
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
const std = @import("std");
|
||||||
|
const c = @import("c.zig");
|
||||||
|
const pixman = @import("main.zig");
|
||||||
|
|
||||||
|
pub const Image = opaque {
|
||||||
|
pub fn createBitsNoClear(
|
||||||
|
format: pixman.FormatCode,
|
||||||
|
width: c_int,
|
||||||
|
height: c_int,
|
||||||
|
bits: [*]u32,
|
||||||
|
stride: c_int,
|
||||||
|
) 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 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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,
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
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,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
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" {
|
||||||
|
const testing = std.testing;
|
||||||
|
const alloc = testing.allocator;
|
||||||
|
|
||||||
|
const width = 10;
|
||||||
|
const height = 10;
|
||||||
|
const format: pixman.FormatCode = .g1;
|
||||||
|
const stride = format.strideForWidth(width);
|
||||||
|
|
||||||
|
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.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());
|
||||||
|
}
|
||||||
|
|
||||||
|
test "fill boxes a1" {
|
||||||
|
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);
|
||||||
|
}
|
10
pkg/pixman/main.zig
Normal file
10
pkg/pixman/main.zig
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
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());
|
||||||
|
}
|
54
pkg/pixman/pixman-version.h
Normal file
54
pkg/pixman/pixman-version.h
Normal file
@ -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 <cworth@cworth.org>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#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__ */
|
131
pkg/pixman/types.zig
Normal file
131
pkg/pixman/types.zig
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
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 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 Triangle = extern struct {
|
||||||
|
p1: PointFixed,
|
||||||
|
p2: PointFixed,
|
||||||
|
p3: PointFixed,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Trapezoid = extern struct {
|
||||||
|
top: Fixed,
|
||||||
|
bottom: Fixed,
|
||||||
|
left: LineFixed,
|
||||||
|
right: LineFixed,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Rectangle16 = extern struct {
|
||||||
|
x: i16,
|
||||||
|
y: i16,
|
||||||
|
width: u16,
|
||||||
|
height: u16,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Box32 = extern struct {
|
||||||
|
x1: i32,
|
||||||
|
y1: i32,
|
||||||
|
x2: i32,
|
||||||
|
y2: i32,
|
||||||
|
};
|
||||||
|
|
||||||
|
pub const Indexed = extern struct {
|
||||||
|
color: bool,
|
||||||
|
rgba: [256]u32,
|
||||||
|
ent: [32768]u8,
|
||||||
|
};
|
||||||
|
|
||||||
|
test {
|
||||||
|
std.testing.refAllDecls(@This());
|
||||||
|
}
|
@ -289,6 +289,13 @@ pub fn create(alloc: Allocator, app: *App, config: *const Config) !*Window {
|
|||||||
// Pre-calculate our initial cell size ourselves.
|
// Pre-calculate our initial cell size ourselves.
|
||||||
const cell_size = try renderer.CellSize.init(alloc, font_group);
|
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,
|
||||||
|
};
|
||||||
|
|
||||||
// Convert our padding from points to pixels
|
// Convert our padding from points to pixels
|
||||||
const padding_x = (@intToFloat(f32, config.@"window-padding-x") * x_dpi) / 72;
|
const padding_x = (@intToFloat(f32, config.@"window-padding-x") * x_dpi) / 72;
|
||||||
const padding_y = (@intToFloat(f32, config.@"window-padding-y") * y_dpi) / 72;
|
const padding_y = (@intToFloat(f32, config.@"window-padding-y") * y_dpi) / 72;
|
||||||
|
2770
src/font/BoxFont.zig
Normal file
2770
src/font/BoxFont.zig
Normal file
File diff suppressed because it is too large
Load Diff
@ -4,6 +4,10 @@
|
|||||||
//! a codepoint doesn't map cleanly. For example, if a user requests a bold
|
//! 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
|
//! char and it doesn't exist we can fallback to a regular non-bold char so
|
||||||
//! we show SOMETHING.
|
//! 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 Group = @This();
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
@ -43,9 +47,14 @@ size: font.face.DesiredSize,
|
|||||||
faces: StyleArray,
|
faces: StyleArray,
|
||||||
|
|
||||||
/// If discovery is available, we'll look up fonts where we can't find
|
/// 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,
|
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(
|
pub fn init(
|
||||||
alloc: Allocator,
|
alloc: Allocator,
|
||||||
lib: Library,
|
lib: Library,
|
||||||
@ -82,7 +91,12 @@ pub fn deinit(self: *Group) void {
|
|||||||
/// The group takes ownership of the face. The face will be deallocated when
|
/// The group takes ownership of the face. The face will be deallocated when
|
||||||
/// the group is deallocated.
|
/// the group is deallocated.
|
||||||
pub fn addFace(self: *Group, alloc: Allocator, style: Style, face: DeferredFace) !void {
|
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.
|
/// Resize the fonts to the desired size.
|
||||||
@ -110,14 +124,36 @@ pub const FontIndex = packed struct {
|
|||||||
const idx_bits = 8 - @typeInfo(@typeInfo(Style).Enum.tag_type).Int.bits;
|
const idx_bits = 8 - @typeInfo(@typeInfo(Style).Enum.tag_type).Int.bits;
|
||||||
pub const IndexInt = @Type(.{ .Int = .{ .signedness = .unsigned, .bits = idx_bits } });
|
pub const IndexInt = @Type(.{ .Int = .{ .signedness = .unsigned, .bits = idx_bits } });
|
||||||
|
|
||||||
|
/// 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);
|
||||||
|
|
||||||
|
/// Box drawing, this is rendered JIT using 2D graphics APIs.
|
||||||
|
box = start,
|
||||||
|
};
|
||||||
|
|
||||||
style: Style = .regular,
|
style: Style = .regular,
|
||||||
idx: IndexInt = 0,
|
idx: IndexInt = 0,
|
||||||
|
|
||||||
|
/// Initialize a special font index.
|
||||||
|
pub fn initSpecial(v: Special) FontIndex {
|
||||||
|
return .{ .style = .regular, .idx = @enumToInt(v) };
|
||||||
|
}
|
||||||
|
|
||||||
/// Convert to int
|
/// Convert to int
|
||||||
pub fn int(self: FontIndex) u8 {
|
pub fn int(self: FontIndex) u8 {
|
||||||
return @bitCast(u8, self);
|
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) ?Special {
|
||||||
|
if (self.idx < Special.start) return null;
|
||||||
|
return @intToEnum(Special, self.idx);
|
||||||
|
}
|
||||||
|
|
||||||
test {
|
test {
|
||||||
// We never want to take up more than a byte since font indexes are
|
// 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
|
// everywhere so if we increase the size of this we'll dramatically
|
||||||
@ -142,6 +178,53 @@ pub fn indexForCodepoint(
|
|||||||
style: Style,
|
style: Style,
|
||||||
p: ?Presentation,
|
p: ?Presentation,
|
||||||
) ?FontIndex {
|
) ?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 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,
|
||||||
|
|
||||||
|
// "Block Elements" block
|
||||||
|
0x2580...0x259f => true,
|
||||||
|
|
||||||
|
// "Braille" block
|
||||||
|
0x2800...0x28FF => true,
|
||||||
|
|
||||||
|
// "Symbols for Legacy Computing" block
|
||||||
|
0x1FB00...0x1FB3B => true,
|
||||||
|
|
||||||
|
0x1FB3C...0x1FB40,
|
||||||
|
0x1FB47...0x1FB4B,
|
||||||
|
0x1FB57...0x1FB5B,
|
||||||
|
0x1FB62...0x1FB66,
|
||||||
|
0x1FB6C...0x1FB6F,
|
||||||
|
=> true,
|
||||||
|
|
||||||
|
0x1FB41...0x1FB45,
|
||||||
|
0x1FB4C...0x1FB50,
|
||||||
|
0x1FB52...0x1FB56,
|
||||||
|
0x1FB5D...0x1FB61,
|
||||||
|
0x1FB68...0x1FB6B,
|
||||||
|
=> true,
|
||||||
|
|
||||||
|
0x1FB46,
|
||||||
|
0x1FB51,
|
||||||
|
0x1FB5C,
|
||||||
|
0x1FB67,
|
||||||
|
0x1FB9A,
|
||||||
|
0x1FB9B,
|
||||||
|
=> true,
|
||||||
|
|
||||||
|
0x1FB70...0x1FB8B => true,
|
||||||
|
|
||||||
|
else => false,
|
||||||
|
}) {
|
||||||
|
return FontIndex.initSpecial(.box);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// If we can find the exact value, then return that.
|
// If we can find the exact value, then return that.
|
||||||
if (self.indexForCodepointExact(cp, style, p)) |value| return value;
|
if (self.indexForCodepointExact(cp, style, p)) |value| return value;
|
||||||
|
|
||||||
@ -188,8 +271,21 @@ fn indexForCodepointExact(self: Group, cp: u32, style: Style, p: ?Presentation)
|
|||||||
return null;
|
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 {
|
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)];
|
const deferred = &self.faces.get(index.style).items[@intCast(usize, index.idx)];
|
||||||
try deferred.load(self.lib, self.size);
|
try deferred.load(self.lib, self.size);
|
||||||
return deferred.face.?;
|
return deferred.face.?;
|
||||||
@ -214,6 +310,15 @@ pub fn renderGlyph(
|
|||||||
glyph_index: u32,
|
glyph_index: u32,
|
||||||
max_height: ?u16,
|
max_height: ?u16,
|
||||||
) !Glyph {
|
) !Glyph {
|
||||||
|
// 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)];
|
const face = &self.faces.get(index.style).items[@intCast(usize, index.idx)];
|
||||||
try face.load(self.lib, self.size);
|
try face.load(self.lib, self.size);
|
||||||
return try face.face.?.renderGlyph(alloc, atlas, glyph_index, max_height);
|
return try face.face.?.renderGlyph(alloc, atlas, glyph_index, max_height);
|
||||||
@ -276,6 +381,43 @@ test {
|
|||||||
try testing.expectEqual(Style.regular, idx.style);
|
try testing.expectEqual(Style.regular, idx.style);
|
||||||
try testing.expectEqual(@as(FontIndex.IndexInt, 1), idx.idx);
|
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" {
|
||||||
|
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();
|
||||||
|
|
||||||
|
// 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(@enumToInt(FontIndex.Special.box), idx.idx);
|
||||||
|
|
||||||
|
// Should render it
|
||||||
|
const glyph = try group.renderGlyph(
|
||||||
|
alloc,
|
||||||
|
&atlas_greyscale,
|
||||||
|
idx,
|
||||||
|
0x2500,
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
try testing.expectEqual(@as(u32, 36), glyph.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
test "resize" {
|
test "resize" {
|
||||||
|
@ -132,8 +132,10 @@ pub fn renderGlyph(
|
|||||||
if (gop.found_existing) return gop.value_ptr.*;
|
if (gop.found_existing) return gop.value_ptr.*;
|
||||||
|
|
||||||
// Uncached, render it
|
// Uncached, render it
|
||||||
const face = try self.group.faceFromIndex(index);
|
const atlas: *Atlas = switch (try self.group.presentationFromIndex(index)) {
|
||||||
const atlas: *Atlas = if (face.presentation == .emoji) &self.atlas_color else &self.atlas_greyscale;
|
.text => &self.atlas_greyscale,
|
||||||
|
.emoji => &self.atlas_color,
|
||||||
|
};
|
||||||
const glyph = self.group.renderGlyph(
|
const glyph = self.group.renderGlyph(
|
||||||
alloc,
|
alloc,
|
||||||
atlas,
|
atlas,
|
||||||
|
@ -7,6 +7,7 @@ const Allocator = std.mem.Allocator;
|
|||||||
const harfbuzz = @import("harfbuzz");
|
const harfbuzz = @import("harfbuzz");
|
||||||
const trace = @import("tracy").trace;
|
const trace = @import("tracy").trace;
|
||||||
const Atlas = @import("../Atlas.zig");
|
const Atlas = @import("../Atlas.zig");
|
||||||
|
const font = @import("main.zig");
|
||||||
const Face = @import("main.zig").Face;
|
const Face = @import("main.zig").Face;
|
||||||
const DeferredFace = @import("main.zig").DeferredFace;
|
const DeferredFace = @import("main.zig").DeferredFace;
|
||||||
const Group = @import("main.zig").Group;
|
const Group = @import("main.zig").Group;
|
||||||
@ -56,14 +57,18 @@ pub fn shape(self: *Shaper, run: TextRun) ![]Cell {
|
|||||||
const tracy = trace(@src());
|
const tracy = trace(@src());
|
||||||
defer tracy.end();
|
defer tracy.end();
|
||||||
|
|
||||||
// TODO: we do not want to hardcode these
|
// We only do shaping if the font is not a special-case. For special-case
|
||||||
const hb_feats = &[_]harfbuzz.Feature{
|
// fonts, the codepoint == glyph_index so we don't need to run any shaping.
|
||||||
harfbuzz.Feature.fromString("dlig").?,
|
if (run.font_index.special() == null) {
|
||||||
harfbuzz.Feature.fromString("liga").?,
|
// 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);
|
const face = try run.group.group.faceFromIndex(run.font_index);
|
||||||
harfbuzz.shape(face.hb_font, self.hb_buf, hb_feats);
|
harfbuzz.shape(face.hb_font, self.hb_buf, hb_feats);
|
||||||
|
}
|
||||||
|
|
||||||
// If our buffer is empty, we short-circuit the rest of the work
|
// If our buffer is empty, we short-circuit the rest of the work
|
||||||
// return nothing.
|
// return nothing.
|
||||||
@ -569,6 +574,47 @@ test "shape Chinese characters" {
|
|||||||
try testing.expectEqual(@as(usize, 1), count);
|
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 {
|
const TestShaper = struct {
|
||||||
alloc: Allocator,
|
alloc: Allocator,
|
||||||
shaper: Shaper,
|
shaper: Shaper,
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const build_options = @import("build_options");
|
const build_options = @import("build_options");
|
||||||
|
|
||||||
|
pub const BoxFont = @import("BoxFont.zig");
|
||||||
pub const discovery = @import("discovery.zig");
|
pub const discovery = @import("discovery.zig");
|
||||||
pub const face = @import("face.zig");
|
pub const face = @import("face.zig");
|
||||||
pub const DeferredFace = @import("DeferredFace.zig");
|
pub const DeferredFace = @import("DeferredFace.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;
|
if (std.meta.eql(self.cell_size, new_cell_size)) return;
|
||||||
self.cell_size = new_cell_size;
|
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.
|
// Notify the window that the cell size changed.
|
||||||
_ = self.window_mailbox.push(.{
|
_ = self.window_mailbox.push(.{
|
||||||
.cell_size = new_cell_size,
|
.cell_size = new_cell_size,
|
||||||
@ -766,7 +772,7 @@ fn rebuildCells(
|
|||||||
var iter = self.font_shaper.runIterator(self.font_group, row);
|
var iter = self.font_shaper.runIterator(self.font_group, row);
|
||||||
while (try iter.next(self.alloc)) |run| {
|
while (try iter.next(self.alloc)) |run| {
|
||||||
for (try self.font_shaper.shape(run)) |shaper_cell| {
|
for (try self.font_shaper.shape(run)) |shaper_cell| {
|
||||||
assert(try self.updateCell(
|
if (self.updateCell(
|
||||||
term_selection,
|
term_selection,
|
||||||
screen,
|
screen,
|
||||||
row.getCell(shaper_cell.x),
|
row.getCell(shaper_cell.x),
|
||||||
@ -774,7 +780,15 @@ fn rebuildCells(
|
|||||||
run,
|
run,
|
||||||
shaper_cell.x,
|
shaper_cell.x,
|
||||||
y,
|
y,
|
||||||
));
|
)) |update| {
|
||||||
|
assert(update);
|
||||||
|
} else |err| {
|
||||||
|
log.warn("error building cell, will be invalid x={} y={}, err={}", .{
|
||||||
|
shaper_cell.x,
|
||||||
|
y,
|
||||||
|
err,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -880,8 +894,11 @@ pub fn updateCell(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// If we're rendering a color font, we use the color atlas
|
// 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 presentation = try self.font_group.group.presentationFromIndex(shaper_run.font_index);
|
||||||
const mode: GPUCellMode = if (face.presentation == .emoji) .fg_color else .fg;
|
const mode: GPUCellMode = switch (presentation) {
|
||||||
|
.text => .fg,
|
||||||
|
.emoji => .fg_color,
|
||||||
|
};
|
||||||
|
|
||||||
self.cells.appendAssumeCapacity(.{
|
self.cells.appendAssumeCapacity(.{
|
||||||
.mode = mode,
|
.mode = mode,
|
||||||
|
@ -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;
|
if (std.meta.eql(self.cell_size, new_cell_size)) return;
|
||||||
self.cell_size = new_cell_size;
|
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.
|
// Notify the window that the cell size changed.
|
||||||
_ = self.window_mailbox.push(.{
|
_ = self.window_mailbox.push(.{
|
||||||
.cell_size = new_cell_size,
|
.cell_size = new_cell_size,
|
||||||
@ -763,7 +769,7 @@ pub fn rebuildCells(
|
|||||||
var iter = self.font_shaper.runIterator(self.font_group, row);
|
var iter = self.font_shaper.runIterator(self.font_group, row);
|
||||||
while (try iter.next(self.alloc)) |run| {
|
while (try iter.next(self.alloc)) |run| {
|
||||||
for (try self.font_shaper.shape(run)) |shaper_cell| {
|
for (try self.font_shaper.shape(run)) |shaper_cell| {
|
||||||
assert(try self.updateCell(
|
if (self.updateCell(
|
||||||
term_selection,
|
term_selection,
|
||||||
screen,
|
screen,
|
||||||
row.getCell(shaper_cell.x),
|
row.getCell(shaper_cell.x),
|
||||||
@ -771,7 +777,15 @@ pub fn rebuildCells(
|
|||||||
run,
|
run,
|
||||||
shaper_cell.x,
|
shaper_cell.x,
|
||||||
y,
|
y,
|
||||||
));
|
)) |update| {
|
||||||
|
assert(update);
|
||||||
|
} else |err| {
|
||||||
|
log.warn("error building cell, will be invalid x={} y={}, err={}", .{
|
||||||
|
shaper_cell.x,
|
||||||
|
y,
|
||||||
|
err,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -952,7 +966,6 @@ pub fn updateCell(
|
|||||||
// If the cell has a character, draw it
|
// If the cell has a character, draw it
|
||||||
if (cell.char > 0) {
|
if (cell.char > 0) {
|
||||||
// Render
|
// Render
|
||||||
const face = try self.font_group.group.faceFromIndex(shaper_run.font_index);
|
|
||||||
const glyph = try self.font_group.renderGlyph(
|
const glyph = try self.font_group.renderGlyph(
|
||||||
self.alloc,
|
self.alloc,
|
||||||
shaper_run.font_index,
|
shaper_run.font_index,
|
||||||
@ -961,8 +974,11 @@ pub fn updateCell(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// If we're rendering a color font, we use the color atlas
|
// If we're rendering a color font, we use the color atlas
|
||||||
var mode: GPUCellMode = .fg;
|
const presentation = try self.font_group.group.presentationFromIndex(shaper_run.font_index);
|
||||||
if (face.presentation == .emoji) mode = .fg_color;
|
const mode: GPUCellMode = switch (presentation) {
|
||||||
|
.text => .fg,
|
||||||
|
.emoji => .fg_color,
|
||||||
|
};
|
||||||
|
|
||||||
self.cells.appendAssumeCapacity(.{
|
self.cells.appendAssumeCapacity(.{
|
||||||
.mode = mode,
|
.mode = mode,
|
||||||
|
1
vendor/pixman
vendored
Submodule
1
vendor/pixman
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 713077d0a3c310ca1955bc331d46d55d0ae4a72b
|
Reference in New Issue
Block a user