game-2026-01-30/src/engine/fontstash/context.zig
2026-01-30 23:28:18 +02:00

251 lines
5.5 KiB
Zig

const std = @import("std");
const Font = @import("./font.zig");
const Align = Font.Align;
const Context = @This();
const c = @cImport({
@cInclude("fontstash.h");
@cInclude("sokol/sokol_gfx.h");
@cInclude("sokol/sokol_gl.h");
@cInclude("sokol_fontstash.h");
});
pub const FONScontext = c.FONScontext;
pub const Size = struct {
width: u32,
height: u32,
};
pub const TextBounds = struct {
advance: f32,
min_x: f32,
max_x: f32,
min_y: f32,
max_y: f32,
};
pub const LineBounds = struct {
min_y: f32,
max_y: f32,
};
pub const VertMetrics = struct {
ascender: f32,
descender: f32,
lineh: f32
};
pub const Quad = struct {
x0: f32,
y0: f32,
s0: f32,
t0: f32,
x1: f32,
y1: f32,
s1: f32,
t1: f32,
};
pub const TextIterator = struct {
context: Context,
iter: c.FONStextIter,
pub fn init(ctx: Context, x: f32, y: f32, text: []const u8) TextIterator {
var self = TextIterator{
.context = ctx,
.iter = undefined
};
const success = c.fonsTextIterInit(
self.context.ctx,
&self.iter,
x,
y,
text.ptr,
text.ptr + text.len
);
if (success != 1) {
return error.fonsTextIterInit;
}
return self;
}
pub fn next(self: TextIterator) ?Quad {
var quad: c.FONSquad = undefined;
const success = c.fonsTextIterNext(self.context, &self.iter, &quad);
if (success != 1) {
return null;
}
return Quad{
.x0 = quad.x0,
.y0 = quad.y0,
.s0 = quad.s0,
.t0 = quad.t0,
.x1 = quad.x0,
.y1 = quad.y0,
.s1 = quad.s0,
.t1 = quad.t0,
};
}
};
ctx: *FONScontext,
pub fn init(desc: c.sfons_desc_t) !Context {
const ctx = c.sfons_create(&desc);
if (ctx == null) {
return error.sfons_create;
}
return Context{
.ctx = ctx.?
};
}
pub fn deinit(self: Context) void {
c.sfons_destroy(self.ctx);
}
pub fn flush(self: Context) void {
c.sfons_flush(self.ctx);
}
pub fn addFont(self: Context, name: [*c]const u8, data: []const u8) !Font.Id {
const font_id = c.fonsAddFontMem(
self.ctx,
name,
@constCast(data.ptr),
@intCast(data.len),
0
);
if (font_id == c.FONS_INVALID) {
return error.fonsAddFontMem;
}
return @enumFromInt(font_id);
}
pub fn addFallbackFont(self: Context, base: Font.Id, fallback: Font.Id) void {
const success = c.fonsAddFallbackFont(self.ctx, @intFromEnum(base), @intFromEnum(fallback));
if (success != 1) {
return error.fonsAddFallbackFont;
}
}
pub fn getFontByName(self: Context, name: [*c]const u8) ?Font.Id {
const font_id = c.fonsGetFontByName(self.ctx, name);
if (font_id == c.FONS_INVALID) {
return null;
}
return @enumFromInt(font_id);
}
// TODO: fonsSetErrorCallback
pub fn getAtlasSize(self: Context) Size {
var result: Size = .{
.width = 0,
.height = 0
};
c.fonsGetAtlasSize(self.ctx, &result.width, &result.height);
return result;
}
pub fn expandAtlas(self: Context, width: u32, height: u32) !void {
const success = c.fonsExpandAtlas(self.ctx, @bitCast(width), @bitCast(height));
if (success != 1) {
return error.fonsExpandAtlas;
}
}
pub fn resetAtlas(self: Context) !void {
const success = c.fonsResetAtlas(self.ctx);
if (success != 1) {
return error.fonsResetAtlas;
}
}
pub fn pushState(self: Context) void {
c.fonsPushState(self.ctx);
}
pub fn popState(self: Context) void {
c.fonsPopState(self.ctx);
}
pub fn clearState(self: Context) void {
c.fonsClearState(self.ctx);
}
pub fn setSize(self: Context, size: f32) void {
c.fonsSetSize(self.ctx, size);
}
pub fn setColor(self: Context, color: u32) void {
c.fonsSetColor(self.ctx, color);
}
pub fn setSpacing(self: Context, spacing: f32) void {
c.fonsSetSpacing(self.ctx, spacing);
}
pub fn setBlur(self: Context, blur: f32) void {
c.fonsSetSpacing(self.ctx, blur);
}
pub fn setAlign(self: Context, alignment: Align) void {
c.fonsSetAlign(self.ctx, @intFromEnum(alignment.x) | @intFromEnum(alignment.y));
}
pub fn setFont(self: Context, id: Font.Id) void {
c.fonsSetFont(self.ctx, @intFromEnum(id));
}
pub fn drawText(self: Context, x: f32, y: f32, text: []const u8) void {
const advance = c.fonsDrawText(self.ctx, x, y, text.ptr, text.ptr + text.len);
_ = advance;
}
pub fn textBounds(self: Context, x: f32, y: f32, text: []const u8) TextBounds {
var bounds: f32[4] = undefined;
const advance = c.fonsTextBounds(self.ctx, x, y, text.ptr, text.ptr + text.len, &bounds);
return TextBounds{
.advance = advance,
.min_x = bounds[0],
.max_x = bounds[1],
.min_y = bounds[2],
.max_y = bounds[3]
};
}
pub fn lineBounds(self: Context, y: f32) LineBounds {
var result: LineBounds = .{
.max_y = 0,
.min_y = 0
};
c.fonsLineBounds(self.ctx, y, &result.min_y, &result.max_y);
return result;
}
pub fn vertMetrics(self: Context) void {
var result: VertMetrics = .{
.ascender = 0,
.descender = 0,
.lineh = 0
};
c.fonsVertMetrics(self.ctx, &result.ascender, &result.descender, &result.lineh);
return result;
}
pub fn drawDebug(self: Context, x: f32, y: f32) void {
c.fonsDrawDebug(self.ctx, x, y);
}