Files
cell/source/qjs_staef.c
2025-11-25 21:05:48 -06:00

147 lines
5.2 KiB
C

#include "font.h"
#include "qjs_macros.h"
#include "HandmadeMath.h"
#include "render.h"
#include "stb_ds.h"
#include "cell.h"
#include "qjs_sdl.h"
#include <SDL3/SDL.h>
// External functions from jsffi.c
typedef HMM_Vec4 colorf;
extern colorf js2color(JSContext *js, JSValue v);
extern JSValue vec22js(JSContext *js, HMM_Vec2 v);
extern HMM_Vec2 js2vec2(JSContext *js, JSValue v);
extern rect js2rect(JSContext *js, JSValue v);
extern double js2number(JSContext *js, JSValue v);
extern JSValue number2js(JSContext *js, double g);
extern void *js_get_blob_data(JSContext *js, size_t *len, JSValue v);
extern JSValue js_new_blob_stoned_copy(JSContext *js, void *data, size_t size);
extern JSValue quads_to_mesh(JSContext *js, text_vert *buffer);
extern int uncaught_exception(JSContext *js, JSValue ret);
// QuickJS class for font
QJSCLASS(font,)
// Font constructor
JSC_CCALL(staef_font_new,
size_t len;
void *data = js_get_blob_data(js, &len, argv[0]);
if (!data) return JS_ThrowReferenceError(js, "could not get array buffer data");
double height = js2number(js, argv[1]);
font *f = MakeFont(data, len, (int)height);
if (!f) return JS_ThrowReferenceError(js, "could not create font");
ret = font2js(js, f);
// Create surface data object for the font's atlas
if (f->surface) {
JSValue surfData = JS_NewObject(js);
JS_SetPropertyStr(js, surfData, "width", JS_NewInt32(js, f->surface->w));
JS_SetPropertyStr(js, surfData, "height", JS_NewInt32(js, f->surface->h));
JS_SetPropertyStr(js, surfData, "format", pixelformat2js(js, f->surface->format));
JS_SetPropertyStr(js, surfData, "pitch", JS_NewInt32(js, f->surface->pitch));
// Lock surface if needed
int locked = 0;
if (SDL_MUSTLOCK(f->surface)) {
if (SDL_LockSurface(f->surface) < 0)
return JS_ThrowInternalError(js, "Lock surface failed: %s", SDL_GetError());
locked = 1;
}
size_t byte_size = f->surface->pitch * f->surface->h;
JS_SetPropertyStr(js, surfData, "pixels", js_new_blob_stoned_copy(js, f->surface->pixels, byte_size));
if (locked)
SDL_UnlockSurface(f->surface);
JS_SetPropertyStr(js, ret, "surface", surfData);
}
)
// Calculate text size
JSC_CCALL(staef_font_text_size,
font *f = js2font(js, self);
const char *str = JS_ToCString(js, argv[0]);
float letterSpacing = argc > 1 ? js2number(js, argv[1]) : 0;
float wrap = argc > 2 ? js2number(js, argv[2]) : -1;
const char *breakat = argc > 3 ? JS_ToCString(js, argv[3]) : NULL;
int breakAt = (breakat == "character") ? CHARACTER : WORD;
const char *align = argc > 4 ? JS_ToCString(js, argv[4]) : NULL;
int alignAt = (align == "right") ? RIGHT : (align == "center") ? CENTER : (align == "justify") ? JUSTIFY : LEFT;
ret = vec22js(js, measure_text(str, f, letterSpacing, wrap, breakAt, alignAt));
JS_FreeCString(js, str);
if (breakat) JS_FreeCString(js, breakat);
if (align) JS_FreeCString(js, align);
)
// Generate text buffer (mesh data)
JSC_CCALL(staef_font_make_text_buffer,
font *f = js2font(js, self);
const char *s = JS_ToCString(js, argv[0]);
rect rectpos = js2rect(js, argv[1]);
colorf c = js2color(js, argv[2]);
float wrap = argc > 3 ? js2number(js, argv[3]) : -1;
const char *breakat = argc > 4 ? JS_ToCString(js, argv[4]) : NULL;
int breakAt = (breakat == "character") ? CHARACTER : WORD;
const char *align = argc > 5 ? JS_ToCString(js, argv[5]) : NULL;
int alignAt = (align == "right") ? RIGHT : (align == "center") ? CENTER : (align == "justify") ? JUSTIFY : LEFT;
HMM_Vec2 startpos = {.x = rectpos.x, .y = rectpos.y };
text_vert *buffer = renderText(s, startpos, f, c, wrap, breakAt, alignAt, 0);
ret = quads_to_mesh(js, buffer);
JS_FreeCString(js, s);
if (breakat) JS_FreeCString(js, breakat);
if (align) JS_FreeCString(js, align);
arrfree(buffer);
)
// Font property getters/setters
JSC_GETSET(font, linegap, number)
JSC_GETSET(font, ascent, number)
JSC_GETSET(font, descent, number)
JSC_GETSET(font, line_height, number)
JSC_GETSET(font, height, number)
// Font methods
static const JSCFunctionListEntry js_font_funcs[] = {
MIST_FUNC_DEF(staef_font, text_size, 5),
MIST_FUNC_DEF(staef_font, make_text_buffer, 6),
CGETSET_ADD(font, linegap),
CGETSET_ADD(font, ascent),
CGETSET_ADD(font, descent),
CGETSET_ADD(font, line_height),
CGETSET_ADD(font, height),
};
// Font constructor function
static JSValue js_font_constructor(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv)
{
return js_staef_font_new(ctx, JS_NULL, argc, argv);
}
// Initialize the staef module
JSValue js_staef_use(JSContext *js)
{
JSValue mod = JS_NewObject(js);
JSValue proto;
// Initialize font class
JS_NewClassID(&js_font_id);
JS_NewClass(JS_GetRuntime(js), js_font_id, &js_font_class);
proto = JS_NewObject(js);
JS_SetPropertyFunctionList(js, proto, js_font_funcs, countof(js_font_funcs));
JS_SetClassProto(js, js_font_id, proto);
// Create font constructor
JSValue font_ctor = JS_NewCFunction2(js, js_font_constructor, "font", 2, JS_CFUNC_constructor, 0);
JS_SetConstructor(js, font_ctor, proto);
// Add font constructor to module (lowercase to match "new staef.font")
JS_SetPropertyStr(js, mod, "font", font_ctor);
return mod;
}