#include "cell.h" #include "prosperon.h" #include "HandmadeMath.h" #include "stb_ds.h" #include #include #include #include #include #include #define MSDF_IMPLEMENTATION #include "msdf.h" #define STB_TRUETYPE_IMPLEMENTATION #define STB_TRUETYPE_NO_STDIO #include "stb_truetype.h" #include "stb_ds.h" #include "HandmadeMath.h" typedef enum { LEFT, RIGHT, CENTER, JUSTIFY } TEXT_ALIGN; typedef enum { WORD, CHARACTER } TEXT_BREAK; typedef enum { FONT_MODE_BITMAP = 0, FONT_MODE_SDF = 1, FONT_MODE_MSDF = 2 } FONT_MODE; struct text_char { rect pos; rect uv; HMM_Vec4 color; }; struct shader; struct window; struct character { float advance; rect quad; rect uv; float xoff; float yoff; float width; float height; }; // text data struct sFont { uint32_t height; /* em_px: glyph size in atlas pixels */ float ascent; // pixels float descent; // pixels float linegap; //pixels float line_height; // pixels struct character Characters[256]; unsigned char *pixels; // RGBA8 pixel data (RGB for MSDF) int atlas_size; // width and height of atlas (square) int mode; // FONT_MODE_BITMAP, FONT_MODE_SDF, or FONT_MODE_MSDF float range_px; // SDF/MSDF distance range in atlas pixels float sharpness; // render-time sharpness multiplier (default 1.0) }; typedef struct sFont font; typedef struct Character glyph; typedef struct { const char *start, *end; float width; } line_t; typedef struct { line_t *lines; float max_width; } layout_lines; struct sFont *use_font; void font_free(JSRuntime *rt, font *f) { if (f->pixels) free(f->pixels); free(f); } // MakeFontSDF: Create SDF font with explicit parameters // em_px: glyph size in atlas (like 64, 96, 128) // range_px: distance field range in atlas pixels (typical: 6-16 for SDF, 2-8 for MSDF) // padding_px: padding around glyphs (should be >= range_px + 2) struct sFont *MakeFontSDF(void *ttf_buffer, size_t len, int em_px, float range_px, int padding_px) { if (!ttf_buffer) return NULL; int packsize = 1024; struct sFont *newfont = calloc(1, sizeof(struct sFont)); newfont->height = em_px; newfont->mode = FONT_MODE_SDF; newfont->range_px = range_px; newfont->sharpness = 1.0f; newfont->atlas_size = packsize; unsigned char *bitmap = calloc(1, packsize * packsize); stbtt_fontinfo fontinfo; if (!stbtt_InitFont(&fontinfo, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer, 0))) { free(newfont); free(bitmap); return NULL; } int ascent, descent, linegap; stbtt_GetFontVMetrics(&fontinfo, &ascent, &descent, &linegap); float scale = stbtt_ScaleForPixelHeight(&fontinfo, em_px); newfont->ascent = ascent * scale; newfont->descent = descent * scale; newfont->linegap = linegap * scale; newfont->line_height = (newfont->ascent - newfont->descent) + newfont->linegap; // Manual SDF packing int x = 0; int y = 0; int row_height = 0; int pad = padding_px; float onedge_value = 128.0f; // pixel_dist_scale controls how distance maps to pixel values // Higher = sharper edges but less range for effects // Formula: encoded = 0.5 + dist / (2 * range_px) // So pixel_dist_scale = 128 / range_px gives us proper encoding float pixel_dist_scale = 128.0f / range_px; for (unsigned char c = 32; c < 127; c++) { int g = stbtt_FindGlyphIndex(&fontinfo, c); int width, height, xoff, yoff; unsigned char *sdf = stbtt_GetGlyphSDF(&fontinfo, scale, g, pad, (unsigned char)onedge_value, pixel_dist_scale, &width, &height, &xoff, &yoff); if (!sdf) { int advance, lsb; stbtt_GetGlyphHMetrics(&fontinfo, g, &advance, &lsb); newfont->Characters[c].advance = advance * scale; continue; } if (x + width + 1 > packsize) { x = 0; y += row_height + 1; row_height = 0; } if (y + height + 1 > packsize) { free(sdf); continue; } for (int sy = 0; sy < height; sy++) { for (int sx = 0; sx < width; sx++) { bitmap[(y + sy) * packsize + (x + sx)] = sdf[sy * width + sx]; } } free(sdf); rect uv; uv.x = (float)x / packsize; uv.y = (float)(y + height) / packsize; uv.w = (float)width / packsize; uv.h = -(float)height / packsize; newfont->Characters[c].uv = uv; rect quad; quad.x = (float)xoff; quad.y = (float)(-yoff - height); quad.w = (float)width; quad.h = (float)height; newfont->Characters[c].quad = quad; int advance, lsb; stbtt_GetGlyphHMetrics(&fontinfo, g, &advance, &lsb); newfont->Characters[c].advance = advance * scale; x += width + 1; if (height > row_height) row_height = height; } // Convert to RGBA8 newfont->pixels = malloc(packsize * packsize * 4); for (int i = 0; i < packsize; i++) { for (int j = 0; j < packsize; j++) { int idx = (i * packsize + j) * 4; newfont->pixels[idx + 0] = 255; newfont->pixels[idx + 1] = 255; newfont->pixels[idx + 2] = 255; newfont->pixels[idx + 3] = bitmap[i * packsize + j]; } } free(bitmap); return newfont; } // MakeFontMSDF: Create MSDF font with explicit parameters struct sFont *MakeFontMSDF(void *ttf_buffer, size_t len, int em_px, float range_px, int padding_px) { if (!ttf_buffer) return NULL; int packsize = 1024; struct sFont *newfont = calloc(1, sizeof(struct sFont)); newfont->height = em_px; newfont->mode = FONT_MODE_MSDF; newfont->range_px = range_px; newfont->sharpness = 1.0f; newfont->atlas_size = packsize; // MSDF uses RGB channels, so we need float RGB buffer then convert unsigned char *bitmap_rgb = calloc(1, packsize * packsize * 3); stbtt_fontinfo fontinfo; if (!stbtt_InitFont(&fontinfo, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer, 0))) { free(newfont); free(bitmap_rgb); return NULL; } int ascent, descent, linegap; stbtt_GetFontVMetrics(&fontinfo, &ascent, &descent, &linegap); float scale = stbtt_ScaleForPixelHeight(&fontinfo, em_px); newfont->ascent = ascent * scale; newfont->descent = descent * scale; newfont->linegap = linegap * scale; newfont->line_height = (newfont->ascent - newfont->descent) + newfont->linegap; int x = 0; int y = 0; int row_height = 0; int border = padding_px; for (unsigned char c = 32; c < 127; c++) { int g = stbtt_FindGlyphIndex(&fontinfo, c); msdf_Result result = {0}; int ok = msdf_genGlyph(&result, &fontinfo, g, border, scale, range_px, NULL); if (!ok || !result.rgb) { int advance, lsb; stbtt_GetGlyphHMetrics(&fontinfo, g, &advance, &lsb); newfont->Characters[c].advance = advance * scale; continue; } int width = result.width; int height = result.height; if (x + width + 1 > packsize) { x = 0; y += row_height + 1; row_height = 0; } if (y + height + 1 > packsize) { free(result.rgb); continue; } // Copy MSDF RGB data to atlas (convert float to uint8) for (int sy = 0; sy < height; sy++) { for (int sx = 0; sx < width; sx++) { int src_idx = 3 * (sy * width + sx); int dst_idx = 3 * ((y + sy) * packsize + (x + sx)); // Clamp float [0,1] to [0,255] float r = result.rgb[src_idx + 0]; float g = result.rgb[src_idx + 1]; float b = result.rgb[src_idx + 2]; bitmap_rgb[dst_idx + 0] = (unsigned char)(fminf(fmaxf(r * 255.0f, 0.0f), 255.0f)); bitmap_rgb[dst_idx + 1] = (unsigned char)(fminf(fmaxf(g * 255.0f, 0.0f), 255.0f)); bitmap_rgb[dst_idx + 2] = (unsigned char)(fminf(fmaxf(b * 255.0f, 0.0f), 255.0f)); } } free(result.rgb); // Get glyph box for positioning int ix0, iy0, ix1, iy1; stbtt_GetGlyphBox(&fontinfo, g, &ix0, &iy0, &ix1, &iy1); rect uv; uv.x = (float)x / packsize; uv.y = (float)(y + height) / packsize; uv.w = (float)width / packsize; uv.h = -(float)height / packsize; newfont->Characters[c].uv = uv; // Calculate quad position // MSDF result includes border, so we need to account for it float xoff = (ix0 * scale) - border; float yoff = (iy0 * scale) - border; rect quad; quad.x = xoff; quad.y = yoff; quad.w = (float)width; quad.h = (float)height; newfont->Characters[c].quad = quad; int advance, lsb; stbtt_GetGlyphHMetrics(&fontinfo, g, &advance, &lsb); newfont->Characters[c].advance = advance * scale; x += width + 1; if (height > row_height) row_height = height; } // Convert RGB to RGBA8 (alpha = 255 for MSDF, color channels hold distance) newfont->pixels = malloc(packsize * packsize * 4); for (int i = 0; i < packsize; i++) { for (int j = 0; j < packsize; j++) { int src_idx = 3 * (i * packsize + j); int dst_idx = (i * packsize + j) * 4; newfont->pixels[dst_idx + 0] = bitmap_rgb[src_idx + 0]; newfont->pixels[dst_idx + 1] = bitmap_rgb[src_idx + 1]; newfont->pixels[dst_idx + 2] = bitmap_rgb[src_idx + 2]; newfont->pixels[dst_idx + 3] = 255; } } free(bitmap_rgb); return newfont; } // Legacy MakeFont for backward compatibility struct sFont *MakeFont(void *ttf_buffer, size_t len, int height, int is_sdf) { if (!ttf_buffer) return NULL; // For SDF mode, use sensible defaults if (is_sdf) { // Default: em_px=height, range_px=12, padding_px=14 return MakeFontSDF(ttf_buffer, len, height, 12.0f, 14); } int packsize = 1024; struct sFont *newfont = calloc(1, sizeof(struct sFont)); newfont->height = height; newfont->mode = FONT_MODE_BITMAP; newfont->range_px = 0; newfont->sharpness = 1.0f; newfont->atlas_size = packsize; unsigned char *bitmap = calloc(1, packsize * packsize); stbtt_fontinfo fontinfo; if (!stbtt_InitFont(&fontinfo, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer, 0))) { free(newfont); free(bitmap); return NULL; } int ascent, descent, linegap; stbtt_GetFontVMetrics(&fontinfo, &ascent, &descent, &linegap); float scale = stbtt_ScaleForPixelHeight(&fontinfo, height); newfont->ascent = ascent * scale; newfont->descent = descent * scale; /* descent is negative */ newfont->linegap = linegap * scale; newfont->line_height = (newfont->ascent - newfont->descent) + newfont->linegap; // Bitmap-only path { // Original Bitmap packing stbtt_packedchar glyphs[95]; stbtt_pack_context pc; int pad = 2; stbtt_PackBegin(&pc, bitmap, packsize, packsize, 0, pad, NULL); stbtt_PackFontRange(&pc, ttf_buffer, 0, height, 32, 95, glyphs); stbtt_PackEnd(&pc); for (unsigned char c = 32; c < 127; c++) { stbtt_packedchar glyph = glyphs[c - 32]; rect uv; uv.x = (glyph.x0) / (float)packsize; uv.w = (glyph.x1-glyph.x0) / (float)packsize; uv.y = (glyph.y1) / (float)packsize; uv.h = (glyph.y0-glyph.y1) / (float)packsize; newfont->Characters[c].uv = uv; rect quad; quad.x = glyph.xoff; quad.y = -glyph.yoff2; // Top of glyph relative to baseline? // Wait, original code had: quad.y = -glyph.yoff2; // But PackFontRange yoff2 is usually bottom? // Inspecting original code: // quad.y = -glyph.yoff2; // This looks like coordinate system flipping? // height = glyph.yoff2 - glyph.yoff; // yoff is top, yoff2 is bottom. quad.x = glyph.xoff; quad.y = glyph.yoff; // Keep standard top-down for consistency with SDF path? // Revert to original behavior for bitmap path to be safe quad.y = -glyph.yoff2; quad.w = glyph.xoff2 - glyph.xoff; quad.h = glyph.yoff2 - glyph.yoff; newfont->Characters[c].quad = quad; newfont->Characters[c].advance = glyph.xadvance; } } // Common pixel expansion newfont->pixels = malloc(packsize * packsize * 4); // RGBA8 for (int i = 0; i < packsize; i++) { for (int j = 0; j < packsize; j++) { int idx = (i * packsize + j) * 4; newfont->pixels[idx + 0] = 255; // R newfont->pixels[idx + 1] = 255; // G newfont->pixels[idx + 2] = 255; // B newfont->pixels[idx + 3] = bitmap[i * packsize + j]; // A } } free(bitmap); return newfont; } // Create SDF font with custom parameters (exposed to JS) struct sFont *MakeFontSDFParams(void *ttf_buffer, size_t len, int em_px, float range_px, int padding_px, float sharpness) { struct sFont *f = MakeFontSDF(ttf_buffer, len, em_px, range_px, padding_px); if (f) f->sharpness = sharpness; return f; } // Create MSDF font with custom parameters (exposed to JS) struct sFont *MakeFontMSDFParams(void *ttf_buffer, size_t len, int em_px, float range_px, int padding_px, float sharpness) { struct sFont *f = MakeFontMSDF(ttf_buffer, len, em_px, range_px, padding_px); if (f) f->sharpness = sharpness; return f; } layout_lines layout_text_lines(const char *text, font *f, float letter_spacing, float wrap, TEXT_BREAK break_at) { layout_lines out = {0}; int break_at_word = (break_at == WORD); const char *line_start = text; const char *word_start = text; float line_width = 0; float word_width = 0; float max_width = 0; for (const char *p = text; *p; p++) { if (*p == '\n') { line_t L = { line_start, p, line_width }; arrput(out.lines, L); if (line_width > max_width) max_width = line_width; line_start = p + 1; word_start = p + 1; line_width = 0; word_width = 0; continue; } unsigned char ch = (unsigned char)*p; float char_w = f->Characters[ch].advance + letter_spacing; if (wrap > 0 && line_width + char_w > wrap) { const char *break_pt = p; float break_w = line_width; if (break_at_word && *p != ' ' && word_width > 0 && word_start > line_start) { break_pt = word_start; break_w = line_width - word_width; } line_t L = { line_start, break_pt, break_w }; arrput(out.lines, L); if (break_w > max_width) max_width = break_w; line_start = break_pt; if (break_at_word && *line_start == ' ') line_start++; p = line_start - 1; word_start = line_start; line_width = 0; word_width = 0; continue; } line_width += char_w; if (break_at_word) { if (*p == ' ') { word_width = 0; word_start = p + 1; } else { word_width += char_w; } } } if (line_start < text + strlen(text)) { line_t L = { line_start, text + strlen(text), line_width }; arrput(out.lines, L); if (line_width > max_width) max_width = line_width; } out.max_width = max_width; return out; } void sdrawCharacter(struct text_vert **buffer, stbtt_packedchar c, HMM_Vec2 cursor, float scale, struct rgba color) { struct text_vert vert; // vert.pos.x = cursor.X + c.leftbearing; // vert.pos.y = cursor.Y + c.topbearing; // vert.wh = c.size; // if (vert.pos.x > frame.l || vert.pos.y > frame.t || (vert.pos.y + vert.wh.y) < frame.b || (vert.pos.x + vert.wh.x) < frame.l) return; // vert.uv.x = c.rect.x; // vert.uv.y = c.rect.y; // vert.st.x = c.rect.w; // vert.st.y = c.rect.h; rgba2floats(vert.color.e, color); arrput(*buffer, vert); } void draw_char_verts(struct text_vert **buffer, struct character c, HMM_Vec2 cursor, colorf color) { // packedchar has // Adds four verts: bottom left, bottom right, top left, top right text_vert bl; bl.pos.x = cursor.X + c.quad.x; bl.pos.y = cursor.Y + c.quad.y; bl.uv.x = c.uv.x; bl.uv.y = c.uv.y; bl.color = color; arrput(*buffer, bl); text_vert br = bl; br.pos.x += c.quad.w; br.uv.x += c.uv.w; arrput(*buffer, br); text_vert ul = bl; ul.pos.y += c.quad.h; ul.uv.y += c.uv.h; arrput(*buffer, ul); text_vert ur = ul; ur.pos.x = br.pos.x; ur.uv.x = br.uv.x; arrput(*buffer, ur); } const char *esc_color(const char *c, struct rgba *color, struct rgba defc) { struct rgba d; if (!color) color = &d; if (*c != '\033') return c; c++; if (*c != '[') return c; c++; if (*c == '0') { *color = defc; c++; return c; } else if (!strncmp(c, "38;2;", 5)) { c += 5; *color = (struct rgba){0,0,0,255}; color->r = atoi(c); c = strchr(c, ';')+1; color->g = atoi(c); c = strchr(c,';')+1; color->b = atoi(c); c = strchr(c,';')+1; return c; } return c; } // text is a string, font f, wrap is how long a line is before wrapping. -1 to not wrap HMM_Vec2 measure_text(const char *text, font *f, float letter_spacing, float wrap, TEXT_BREAK break_at, TEXT_ALIGN align) { layout_lines lay = layout_text_lines(text, f, letter_spacing, wrap, break_at); float lh = f->line_height; int line_count = arrlen(lay.lines); float height = line_count > 0 ? line_count * lh : 0; float width = lay.max_width; if (wrap > 0 && align != LEFT && align != JUSTIFY) { // For RIGHT or CENTER we might shift lines visually, but width is still max(line.width, wrap ? min(wrap, ...) ). // Usually you'd report max(wrap, width) or just wrap, depending on how you want containers to size. } HMM_Vec2 dim = { width, height }; arrfree(lay.lines); return dim; } /* pos given in screen coordinates */ struct text_vert *renderText(const char *text, HMM_Vec2 pos, font *f, colorf color, float wrap, TEXT_BREAK break_at, TEXT_ALIGN align, float letter_spacing) { text_vert *buffer = NULL; float lh = f->line_height; layout_lines lay = layout_text_lines(text, f, letter_spacing, wrap, break_at); line_t *lines = lay.lines; HMM_Vec2 cursor = pos; for (int i = 0; i < arrlen(lines); i++) { line_t *L = &lines[i]; float start_x = pos.x; float extra_space = 0; int space_count = 0; if (wrap > 0) { switch (align) { case RIGHT: start_x = pos.x + wrap - L->width; break; case CENTER: start_x = pos.x + (wrap - L->width) * 0.5f; break; case JUSTIFY: if (i < arrlen(lines) - 1 && *(L->end) != '\n') { for (const char *p = L->start; p < L->end; p++) if (*p == ' ') space_count++; if (space_count > 0) extra_space = (wrap - L->width) / space_count; } break; case LEFT: default: break; } } cursor.x = start_x; const char *p = L->start; while (p < L->end) { unsigned char ch = (unsigned char)*p; struct character g = f->Characters[ch]; if (!isspace(*p)) draw_char_verts(&buffer, g, cursor, color); cursor.x += g.advance; /* add letter spacing unless this is last char of the line */ if (p + 1 < L->end) cursor.x += letter_spacing; if (align == JUSTIFY && *p == ' ') cursor.x += extra_space; p++; } cursor.x = pos.x; cursor.y -= lh; } arrfree(lines); return buffer; } // QuickJS class for font QJSCLASS(font,) // Helper to attach texture and mode info to font JS object static void attach_font_texture(JSContext *js, JSValue ret, font *f) { if (f->pixels) { JSValue texData = JS_NewObject(js); JS_SetPropertyStr(js, texData, "width", JS_NewInt32(js, f->atlas_size)); JS_SetPropertyStr(js, texData, "height", JS_NewInt32(js, f->atlas_size)); JS_SetPropertyStr(js, texData, "format", JS_NewString(js, "rgba8")); size_t byte_size = f->atlas_size * f->atlas_size * 4; JS_SetPropertyStr(js, texData, "pixels", js_new_blob_stoned_copy(js, f->pixels, byte_size)); JS_SetPropertyStr(js, ret, "texture", texData); } // Add mode string const char *mode_str = "bitmap"; if (f->mode == FONT_MODE_SDF) mode_str = "sdf"; else if (f->mode == FONT_MODE_MSDF) mode_str = "msdf"; JS_SetPropertyStr(js, ret, "mode", JS_NewString(js, mode_str)); // Add range_px and sharpness for SDF/MSDF fonts JS_SetPropertyStr(js, ret, "range_px", JS_NewFloat64(js, f->range_px)); JS_SetPropertyStr(js, ret, "sharpness", JS_NewFloat64(js, f->sharpness)); } // Font constructor (legacy: data, height, is_sdf) JSC_CCALL(staef_font_new, size_t len; void *data = js_get_blob_data(js, &len, argv[0]); if (data == (void*)-1) return JS_EXCEPTION; if (!data) return JS_ThrowReferenceError(js, "could not get array buffer data"); double height = js2number(js, argv[1]); int is_sdf = 0; if (argc > 2) is_sdf = JS_ToBool(js, argv[2]); font *f = MakeFont(data, len, (int)height, is_sdf); if (!f) return JS_ThrowReferenceError(js, "could not create font"); ret = font2js(js, f); attach_font_texture(js, ret, f); ) // SDF font constructor: sdf_font(data, em_px, range_px, padding_px, sharpness) JSC_CCALL(staef_sdf_font_new, size_t len; void *data = js_get_blob_data(js, &len, argv[0]); if (data == (void*)-1) return JS_EXCEPTION; if (!data) return JS_ThrowReferenceError(js, "could not get array buffer data"); int em_px = argc > 1 ? (int)js2number(js, argv[1]) : 64; float range_px = argc > 2 ? (float)js2number(js, argv[2]) : 12.0f; int padding_px = argc > 3 ? (int)js2number(js, argv[3]) : 14; float sharpness = argc > 4 ? (float)js2number(js, argv[4]) : 1.0f; font *f = MakeFontSDFParams(data, len, em_px, range_px, padding_px, sharpness); if (!f) return JS_ThrowReferenceError(js, "could not create SDF font"); ret = font2js(js, f); attach_font_texture(js, ret, f); ) // MSDF font constructor: msdf_font(data, em_px, range_px, padding_px, sharpness) JSC_CCALL(staef_msdf_font_new, size_t len; void *data = js_get_blob_data(js, &len, argv[0]); if (data == (void*)-1) return JS_EXCEPTION; if (!data) return JS_ThrowReferenceError(js, "could not get array buffer data"); int em_px = argc > 1 ? (int)js2number(js, argv[1]) : 64; float range_px = argc > 2 ? (float)js2number(js, argv[2]) : 4.0f; int padding_px = argc > 3 ? (int)js2number(js, argv[3]) : 6; float sharpness = argc > 4 ? (float)js2number(js, argv[4]) : 1.0f; font *f = MakeFontMSDFParams(data, len, em_px, range_px, padding_px, sharpness); if (!f) return JS_ThrowReferenceError(js, "could not create MSDF font"); ret = font2js(js, f); attach_font_texture(js, ret, f); ) // 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 && strcmp(breakat, "character") == 0) ? CHARACTER : WORD; const char *align = argc > 4 ? JS_ToCString(js, argv[4]) : NULL; int alignAt = (align && strcmp(align, "right") == 0) ? RIGHT : (align && strcmp(align, "center") == 0) ? CENTER : (align && strcmp(align, "justify") == 0) ? 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 && strcmp(breakat, "character") == 0) ? CHARACTER : WORD; const char *align = argc > 5 ? JS_ToCString(js, argv[5]) : NULL; int alignAt = (align && strcmp(align, "right") == 0) ? RIGHT : (align && strcmp(align, "center") == 0) ? CENTER : (align && strcmp(align, "justify") == 0) ? 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) JSC_GETSET(font, range_px, number) JSC_GETSET(font, sharpness, number) JSC_GETSET(font, mode, 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), CGETSET_ADD(font, range_px), CGETSET_ADD(font, sharpness), CGETSET_ADD(font, mode), */ }; // 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); } // SDF font constructor function static JSValue js_sdf_font_constructor(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv) { return js_staef_sdf_font_new(ctx, JS_NULL, argc, argv); } // MSDF font constructor function static JSValue js_msdf_font_constructor(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv) { return js_staef_msdf_font_new(ctx, JS_NULL, argc, argv); } // Initialize the staef module CELL_USE_INIT( 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 (legacy) JSValue font_ctor = JS_NewCFunction2(js, js_font_constructor, "font", 2, JS_CFUNC_generic, 0); JS_SetPropertyStr(js, mod, "font", font_ctor); // Create SDF font constructor: sdf_font(data, em_px, range_px, padding_px, sharpness) JSValue sdf_font_ctor = JS_NewCFunction2(js, js_sdf_font_constructor, "sdf_font", 5, JS_CFUNC_generic, 0); JS_SetPropertyStr(js, mod, "sdf_font", sdf_font_ctor); // Create MSDF font constructor: msdf_font(data, em_px, range_px, padding_px, sharpness) JSValue msdf_font_ctor = JS_NewCFunction2(js, js_msdf_font_constructor, "msdf_font", 5, JS_CFUNC_generic, 0); JS_SetPropertyStr(js, mod, "msdf_font", msdf_font_ctor); return mod; )