mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
font: add method for drawing atlas to canvas
This commit is contained in:
@ -30,6 +30,7 @@ fetch(url.href).then(response =>
|
||||
face_debug_canvas,
|
||||
atlas_new,
|
||||
atlas_free,
|
||||
atlas_debug_canvas,
|
||||
} = results.instance.exports;
|
||||
// Give us access to the zjs value for debugging.
|
||||
globalThis.zjs = zjs;
|
||||
@ -47,14 +48,21 @@ fetch(url.href).then(response =>
|
||||
new Uint8Array(memory.buffer, font_ptr).set(font);
|
||||
|
||||
// Call whatever example you want:
|
||||
const face = face_new(font_ptr, font.byteLength, 144);
|
||||
const face = face_new(font_ptr, font.byteLength, 72);
|
||||
free(font_ptr);
|
||||
|
||||
// Render a glyph
|
||||
face_render_glyph(face, atlas, "A".codePointAt(0));
|
||||
for (let i = 33; i <= 126; i++) {
|
||||
face_render_glyph(face, atlas, i);
|
||||
}
|
||||
// face_render_glyph(face, atlas, "A".codePointAt(0));
|
||||
|
||||
// Debug our canvas
|
||||
face_debug_canvas(face);
|
||||
|
||||
// Debug our atlas canvas
|
||||
const id = atlas_debug_canvas(atlas);
|
||||
document.getElementById("atlas-canvas").append(zjs.deleteValue(id));
|
||||
|
||||
//face_free(face);
|
||||
});
|
||||
|
@ -8,6 +8,8 @@
|
||||
<body>
|
||||
<p>Open your console, we are just debugging here.</p>
|
||||
<p>The font rendering canvas should show below. This shows a single glyph.</p>
|
||||
<div id="face-canvas" style="display: inline-block; border: 1px solid red;"></div>
|
||||
<div><div id="face-canvas" style="display: inline-block; border: 1px solid red;"></div></div>
|
||||
<p>The current font atlas is rendered below.</p>
|
||||
<div><div id="atlas-canvas" style="display: inline-block; border: 1px solid green;"></div></div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -21,6 +21,8 @@ const Allocator = std.mem.Allocator;
|
||||
const testing = std.testing;
|
||||
const fastmem = @import("../fastmem.zig");
|
||||
|
||||
const log = std.log.scoped(.atlas);
|
||||
|
||||
/// Data is the raw texture data.
|
||||
data: []u8,
|
||||
|
||||
@ -309,6 +311,7 @@ pub const Wasm = struct {
|
||||
// just replace this with the allocator you want to use.
|
||||
const wasm = @import("../os/wasm.zig");
|
||||
const alloc = wasm.alloc;
|
||||
const js = @import("zig-js");
|
||||
|
||||
export fn atlas_new(size: u32, format: u8) ?*Atlas {
|
||||
const atlas = init(
|
||||
@ -321,6 +324,13 @@ pub const Wasm = struct {
|
||||
return result;
|
||||
}
|
||||
|
||||
export fn atlas_free(ptr: ?*Atlas) void {
|
||||
if (ptr) |v| {
|
||||
v.deinit(alloc);
|
||||
alloc.destroy(v);
|
||||
}
|
||||
}
|
||||
|
||||
/// The return value for this should be freed by the caller with "free".
|
||||
export fn atlas_reserve(self: *Atlas, width: u32, height: u32) ?*Region {
|
||||
return atlas_reserve_(self, width, height) catch return null;
|
||||
@ -348,11 +358,89 @@ pub const Wasm = struct {
|
||||
self.clear();
|
||||
}
|
||||
|
||||
export fn atlas_free(ptr: ?*Atlas) void {
|
||||
if (ptr) |v| {
|
||||
v.deinit(alloc);
|
||||
alloc.destroy(v);
|
||||
/// This creates a Canvas element identified by the id returned that
|
||||
/// the caller can draw into the DOM to visualize the atlas. The returned
|
||||
/// ID must be freed from the JS runtime by calling "zigjs.deleteValue".
|
||||
export fn atlas_debug_canvas(self: *Atlas) u32 {
|
||||
return atlas_debug_canvas_(self) catch |err| {
|
||||
log.warn("error dumping atlas canvas err={}", .{err});
|
||||
return 0;
|
||||
};
|
||||
}
|
||||
|
||||
fn atlas_debug_canvas_(self: *Atlas) !u32 {
|
||||
// Create our canvas
|
||||
const doc = try js.global.get(js.Object, "document");
|
||||
defer doc.deinit();
|
||||
const canvas = try doc.call(js.Object, "createElement", .{js.string("canvas")});
|
||||
errdefer canvas.deinit();
|
||||
|
||||
// Setup our canvas size
|
||||
{
|
||||
try canvas.set("width", self.size);
|
||||
try canvas.set("height", self.size);
|
||||
|
||||
const width_str = try std.fmt.allocPrint(alloc, "{d}px", .{self.size});
|
||||
defer alloc.free(width_str);
|
||||
|
||||
const style = try canvas.get(js.Object, "style");
|
||||
defer style.deinit();
|
||||
try style.set("width", js.string(width_str));
|
||||
try style.set("height", js.string(width_str));
|
||||
}
|
||||
|
||||
// This will return the same context on subsequent calls so it
|
||||
// is important to reset it.
|
||||
const ctx = try canvas.call(js.Object, "getContext", .{js.string("2d")});
|
||||
errdefer ctx.deinit();
|
||||
|
||||
// We need to draw pixels so this is format dependent.
|
||||
var buf: []u8 = switch (self.format) {
|
||||
// RGBA is the native ImageData format
|
||||
.rgba => self.data,
|
||||
|
||||
.greyscale => buf: {
|
||||
// Convert from A8 to RGBA so every 4th byte is set to a value.
|
||||
var buf: []u8 = try alloc.alloc(u8, self.data.len * 4);
|
||||
errdefer alloc.free(buf);
|
||||
std.mem.set(u8, buf, 0);
|
||||
for (self.data) |value, i| {
|
||||
buf[(i * 4) + 3] = value;
|
||||
}
|
||||
break :buf buf;
|
||||
},
|
||||
|
||||
else => return error.UnsupportedAtlasFormat,
|
||||
};
|
||||
defer if (buf.ptr != self.data.ptr) alloc.free(buf);
|
||||
|
||||
// Create an ImageData from our buffer and then write it to the canvas
|
||||
const image_data: js.Object = data: {
|
||||
// Get our runtime memory
|
||||
const mem = try js.runtime.get(js.Object, "memory");
|
||||
defer mem.deinit();
|
||||
const mem_buf = try mem.get(js.Object, "buffer");
|
||||
defer mem_buf.deinit();
|
||||
|
||||
// Create an array that points to our buffer
|
||||
const Uint8ClampedArray = try js.global.get(js.Object, "Uint8ClampedArray");
|
||||
defer Uint8ClampedArray.deinit();
|
||||
const arr = try Uint8ClampedArray.new(.{ mem_buf, buf.ptr, buf.len });
|
||||
|
||||
// Create the image data from our array
|
||||
const ImageData = try js.global.get(js.Object, "ImageData");
|
||||
defer ImageData.deinit();
|
||||
const data = try ImageData.new(.{ arr, self.size, self.size });
|
||||
errdefer data.deinit();
|
||||
|
||||
break :data data;
|
||||
};
|
||||
|
||||
// Draw it
|
||||
try ctx.call(void, "putImageData", .{ image_data, 0, 0 });
|
||||
|
||||
const id = @bitCast(js.Ref, @enumToInt(canvas.value)).id;
|
||||
return id;
|
||||
}
|
||||
|
||||
test "happy path" {
|
||||
|
@ -215,7 +215,7 @@ pub const Face = struct {
|
||||
defer alloc.free(bitmap_a8);
|
||||
var i: usize = 0;
|
||||
while (i < bitmap_a8.len) : (i += 1) {
|
||||
bitmap_a8[i] = bitmap[i * 4];
|
||||
bitmap_a8[i] = bitmap[(i * 4) + 3];
|
||||
}
|
||||
|
||||
// Put it in our atlas
|
||||
|
2
vendor/zig-js
vendored
2
vendor/zig-js
vendored
@ -1 +1 @@
|
||||
Subproject commit 00eb5166ea3a070ac985b2b4409f2e51877865f4
|
||||
Subproject commit 52eed4daddcf9fa974ad4457691de26ef2351c56
|
Reference in New Issue
Block a user