mirror of
https://github.com/ghostty-org/ghostty.git
synced 2025-08-02 14:57:31 +03:00
font: web canvas sprite font can write to atlas
This commit is contained in:
@ -35,6 +35,7 @@ fetch(url.href).then(response =>
|
|||||||
group_new,
|
group_new,
|
||||||
group_free,
|
group_free,
|
||||||
group_add_face,
|
group_add_face,
|
||||||
|
group_init_sprite_face,
|
||||||
group_index_for_codepoint,
|
group_index_for_codepoint,
|
||||||
group_render_glyph,
|
group_render_glyph,
|
||||||
group_cache_new,
|
group_cache_new,
|
||||||
@ -85,23 +86,32 @@ fetch(url.href).then(response =>
|
|||||||
group_add_face(group, 0 /* regular */, deferred_face_new(font_name.ptr, font_name.len, 0 /* text */));
|
group_add_face(group, 0 /* regular */, deferred_face_new(font_name.ptr, font_name.len, 0 /* text */));
|
||||||
group_add_face(group, 0 /* regular */, deferred_face_new(font_name.ptr, font_name.len, 1 /* emoji */));
|
group_add_face(group, 0 /* regular */, deferred_face_new(font_name.ptr, font_name.len, 1 /* emoji */));
|
||||||
|
|
||||||
|
// Initialize our sprite font, without this we just use the browser.
|
||||||
|
group_init_sprite_face(group);
|
||||||
|
|
||||||
// Create our group cache
|
// Create our group cache
|
||||||
const group_cache = group_cache_new(group);
|
const group_cache = group_cache_new(group);
|
||||||
|
|
||||||
// Render a glyph
|
// Render a glyph
|
||||||
for (let i = 33; i <= 126; i++) {
|
// for (let i = 33; i <= 126; i++) {
|
||||||
|
// const font_idx = group_cache_index_for_codepoint(group_cache, i, 0, -1);
|
||||||
|
// group_cache_render_glyph(group_cache, font_idx, i, 0);
|
||||||
|
// //face_render_glyph(face, atlas, i);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// const emoji = ["🐏","🌞","🌚","🍱","💿","🐈","📃","📀","🕡","🙃"];
|
||||||
|
// for (let i = 0; i < emoji.length; i++) {
|
||||||
|
// const cp = emoji[i].codePointAt(0);
|
||||||
|
// const font_idx = group_cache_index_for_codepoint(group_cache, cp, 0, -1 /* best choice */);
|
||||||
|
// group_cache_render_glyph(group_cache, font_idx, cp, 0);
|
||||||
|
// }
|
||||||
|
|
||||||
|
for (let i = 0x2500; i <= 0x257F; i++) {
|
||||||
const font_idx = group_cache_index_for_codepoint(group_cache, i, 0, -1);
|
const font_idx = group_cache_index_for_codepoint(group_cache, i, 0, -1);
|
||||||
group_cache_render_glyph(group_cache, font_idx, i, 0);
|
group_cache_render_glyph(group_cache, font_idx, i, 0);
|
||||||
//face_render_glyph(face, atlas, i);
|
//face_render_glyph(face, atlas, i);
|
||||||
}
|
}
|
||||||
|
|
||||||
const emoji = ["🐏","🌞","🌚","🍱","💿","🐈","📃","📀","🕡","🙃"];
|
|
||||||
for (let i = 0; i < emoji.length; i++) {
|
|
||||||
const cp = emoji[i].codePointAt(0);
|
|
||||||
const font_idx = group_cache_index_for_codepoint(group_cache, cp, 0, -1 /* best choice */);
|
|
||||||
group_cache_render_glyph(group_cache, font_idx, cp, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
//face_render_glyph(face, atlas, "橋".codePointAt(0));
|
//face_render_glyph(face, atlas, "橋".codePointAt(0));
|
||||||
//face_render_glyph(face, atlas, "p".codePointAt(0));
|
//face_render_glyph(face, atlas, "p".codePointAt(0));
|
||||||
|
|
||||||
|
@ -311,6 +311,29 @@ pub const Wasm = struct {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export fn group_init_sprite_face(self: *Group) void {
|
||||||
|
return group_init_sprite_face_(self) catch |err| {
|
||||||
|
log.warn("error initializing sprite face err={}", .{err});
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn group_init_sprite_face_(self: *Group) !void {
|
||||||
|
const metrics = metrics: {
|
||||||
|
const index = self.indexForCodepoint('M', .regular, .text).?;
|
||||||
|
const face = try self.faceFromIndex(index);
|
||||||
|
break :metrics face.metrics;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Set details for our sprite font
|
||||||
|
self.sprite = font.sprite.Face{
|
||||||
|
.width = @floatToInt(u32, metrics.cell_width),
|
||||||
|
.height = @floatToInt(u32, metrics.cell_height),
|
||||||
|
.thickness = 2,
|
||||||
|
.underline_position = @floatToInt(u32, metrics.underline_position),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export fn group_add_face(self: *Group, style: u16, face: *font.DeferredFace) void {
|
export fn group_add_face(self: *Group, style: u16, face: *font.DeferredFace) void {
|
||||||
return self.addFace(alloc, @intToEnum(Style, style), face.*) catch |err| {
|
return self.addFace(alloc, @intToEnum(Style, style), face.*) catch |err| {
|
||||||
log.warn("error adding face to group err={}", .{err});
|
log.warn("error adding face to group err={}", .{err});
|
||||||
|
@ -104,6 +104,10 @@ const WebCanvasImpl = struct {
|
|||||||
/// The canvas element that is our final image.
|
/// The canvas element that is our final image.
|
||||||
canvas: js.Object,
|
canvas: js.Object,
|
||||||
|
|
||||||
|
/// Store the dimensions for easy access later.
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
|
||||||
pub fn init(alloc: Allocator, width: u32, height: u32) !WebCanvasImpl {
|
pub fn init(alloc: Allocator, width: u32, height: u32) !WebCanvasImpl {
|
||||||
_ = alloc;
|
_ = alloc;
|
||||||
|
|
||||||
@ -119,6 +123,8 @@ const WebCanvasImpl = struct {
|
|||||||
|
|
||||||
return WebCanvasImpl{
|
return WebCanvasImpl{
|
||||||
.canvas = canvas,
|
.canvas = canvas,
|
||||||
|
.width = width,
|
||||||
|
.height = height,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,10 +183,81 @@ const WebCanvasImpl = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn writeAtlas(self: *WebCanvasImpl, alloc: Allocator, atlas: *font.Atlas) !font.Atlas.Region {
|
pub fn writeAtlas(self: *WebCanvasImpl, alloc: Allocator, atlas: *font.Atlas) !font.Atlas.Region {
|
||||||
_ = self;
|
assert(atlas.format == .greyscale);
|
||||||
_ = alloc;
|
|
||||||
_ = atlas;
|
// Reload our context since we resized the canvas
|
||||||
return error.Unimplemented;
|
const ctx = try self.context(null);
|
||||||
|
defer ctx.deinit();
|
||||||
|
|
||||||
|
// Set our width/height. Set to vars in case we just query the canvas later.
|
||||||
|
const width = self.width;
|
||||||
|
const height = self.height;
|
||||||
|
|
||||||
|
// Read the image data and get it into a []u8 on our side
|
||||||
|
const bitmap: []u8 = bitmap: {
|
||||||
|
// Read the raw bitmap data and get the "data" value which is a
|
||||||
|
// Uint8ClampedArray.
|
||||||
|
const data = try ctx.call(js.Object, "getImageData", .{ 0, 0, width, height });
|
||||||
|
defer data.deinit();
|
||||||
|
const src_array = try data.get(js.Object, "data");
|
||||||
|
defer src_array.deinit();
|
||||||
|
|
||||||
|
// Allocate our local memory to copy the data to.
|
||||||
|
const len = try src_array.get(u32, "length");
|
||||||
|
var bitmap = try alloc.alloc(u8, @intCast(usize, len));
|
||||||
|
errdefer alloc.free(bitmap);
|
||||||
|
|
||||||
|
// Create our target Uint8Array that we can use to copy from src.
|
||||||
|
const mem_array = mem_array: {
|
||||||
|
// Get our runtime memory
|
||||||
|
const mem = try js.runtime.get(js.Object, "memory");
|
||||||
|
defer mem.deinit();
|
||||||
|
const buf = try mem.get(js.Object, "buffer");
|
||||||
|
defer buf.deinit();
|
||||||
|
|
||||||
|
// Construct our array to peer into our memory
|
||||||
|
const Uint8Array = try js.global.get(js.Object, "Uint8Array");
|
||||||
|
defer Uint8Array.deinit();
|
||||||
|
const mem_array = try Uint8Array.new(.{ buf, bitmap.ptr });
|
||||||
|
errdefer mem_array.deinit();
|
||||||
|
|
||||||
|
break :mem_array mem_array;
|
||||||
|
};
|
||||||
|
defer mem_array.deinit();
|
||||||
|
|
||||||
|
// Copy
|
||||||
|
try mem_array.call(void, "set", .{src_array});
|
||||||
|
|
||||||
|
break :bitmap bitmap;
|
||||||
|
};
|
||||||
|
errdefer alloc.free(bitmap);
|
||||||
|
|
||||||
|
// Convert the format of the bitmap to A8 since the raw canvas data
|
||||||
|
// is in RGBA.
|
||||||
|
// NOTE(mitchellh): do we need a 1px buffer to avoid artifacts?
|
||||||
|
const bitmap_a8: []u8 = a8: {
|
||||||
|
assert(@mod(bitmap.len, 4) == 0);
|
||||||
|
assert(bitmap.len == width * height * 4);
|
||||||
|
var bitmap_a8 = try alloc.alloc(u8, bitmap.len / 4);
|
||||||
|
errdefer alloc.free(bitmap_a8);
|
||||||
|
var i: usize = 0;
|
||||||
|
while (i < bitmap_a8.len) : (i += 1) {
|
||||||
|
bitmap_a8[i] = bitmap[(i * 4) + 3];
|
||||||
|
}
|
||||||
|
|
||||||
|
break :a8 bitmap_a8;
|
||||||
|
};
|
||||||
|
defer alloc.free(bitmap_a8);
|
||||||
|
|
||||||
|
// Write the glyph information into the atlas
|
||||||
|
const region = try atlas.reserve(alloc, width, height);
|
||||||
|
if (region.width > 0 and region.height > 0) {
|
||||||
|
assert(region.width == width);
|
||||||
|
assert(region.height == height);
|
||||||
|
atlas.set(region, bitmap_a8);
|
||||||
|
}
|
||||||
|
|
||||||
|
return region;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user