sdf
This commit is contained in:
@@ -588,9 +588,12 @@ function collect_drawables(node, camera, parent_tint, parent_opacity, parent_sci
|
|||||||
text: node.text,
|
text: node.text,
|
||||||
font: node.font,
|
font: node.font,
|
||||||
size: node.size,
|
size: node.size,
|
||||||
sdf: node.sdf,
|
mode: node.mode, // 'bitmap', 'sdf', or 'msdf'
|
||||||
|
sdf: node.sdf, // legacy support
|
||||||
outline_width: node.outline_width,
|
outline_width: node.outline_width,
|
||||||
outline_color: node.outline_color,
|
outline_color: node.outline_color,
|
||||||
|
anchor_x: node.anchor_x,
|
||||||
|
anchor_y: node.anchor_y,
|
||||||
color: tint_to_color(world_tint, world_opacity),
|
color: tint_to_color(world_tint, world_opacity),
|
||||||
scissor: current_scissor
|
scissor: current_scissor
|
||||||
})
|
})
|
||||||
|
|||||||
15
msdf.h
15
msdf.h
@@ -16,6 +16,8 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "stb_truetype.h"
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
@@ -828,9 +830,11 @@ int msdf_genGlyph(msdf_Result* result, stbtt_fontinfo *font, int stbttGlyphIndex
|
|||||||
//float scale = stbtt_ScaleForMappingEmToPixels(font, h);
|
//float scale = stbtt_ScaleForMappingEmToPixels(font, h);
|
||||||
int glyphIdx = stbttGlyphIndex;
|
int glyphIdx = stbttGlyphIndex;
|
||||||
// get glyph bounding box (scaled later)
|
// get glyph bounding box (scaled later)
|
||||||
int ix0, iy0, ix1, iy1;
|
int ix0 = 0, iy0 = 0, ix1 = 0, iy1 = 0;
|
||||||
float xoff = .0, yoff = .0;
|
float xoff = .0, yoff = .0;
|
||||||
stbtt_GetGlyphBox(font, glyphIdx, &ix0, &iy0, &ix1, &iy1);
|
if (!stbtt_GetGlyphBox(font, glyphIdx, &ix0, &iy0, &ix1, &iy1)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
float glyphWidth = ix1 - ix0;
|
float glyphWidth = ix1 - ix0;
|
||||||
float glyphHeight = iy1 - iy0;
|
float glyphHeight = iy1 - iy0;
|
||||||
@@ -839,12 +843,19 @@ int msdf_genGlyph(msdf_Result* result, stbtt_fontinfo *font, int stbttGlyphIndex
|
|||||||
float hF32 = ceilf(glyphHeight * scale);
|
float hF32 = ceilf(glyphHeight * scale);
|
||||||
wF32 += 2.f * borderWidth;
|
wF32 += 2.f * borderWidth;
|
||||||
hF32 += 2.f * borderWidth;
|
hF32 += 2.f * borderWidth;
|
||||||
|
|
||||||
int w = wF32;
|
int w = wF32;
|
||||||
int h = hF32;
|
int h = hF32;
|
||||||
|
|
||||||
float* bitmap = (float*) allocCtx.alloc(w * h * 3 * sizeof(float), allocCtx.ctx);
|
float* bitmap = (float*) allocCtx.alloc(w * h * 3 * sizeof(float), allocCtx.ctx);
|
||||||
|
if (!bitmap) return 0;
|
||||||
memset(bitmap, 0x0, w * h * 3 * sizeof(float));
|
memset(bitmap, 0x0, w * h * 3 * sizeof(float));
|
||||||
|
|
||||||
|
result->rgb = bitmap;
|
||||||
|
result->width = w;
|
||||||
|
result->height = h;
|
||||||
|
result->glyphIdx = glyphIdx;
|
||||||
|
|
||||||
// em scale
|
// em scale
|
||||||
//scale = stbtt_ScaleForMappingEmToPixels(font, h);
|
//scale = stbtt_ScaleForMappingEmToPixels(font, h);
|
||||||
|
|
||||||
|
|||||||
203
sdl_gpu.cm
203
sdl_gpu.cm
@@ -36,6 +36,7 @@ var _mask_frag = null
|
|||||||
var _mask_frag = null
|
var _mask_frag = null
|
||||||
var _crt_frag = null
|
var _crt_frag = null
|
||||||
var _text_sdf_frag = null
|
var _text_sdf_frag = null
|
||||||
|
var _text_msdf_frag = null
|
||||||
|
|
||||||
// Pipelines
|
// Pipelines
|
||||||
var _pipelines = {}
|
var _pipelines = {}
|
||||||
@@ -218,6 +219,18 @@ function _load_shaders() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var text_msdf_frag_code = io.slurp("shaders/msl/text_msdf.frag.msl")
|
||||||
|
if (text_msdf_frag_code) {
|
||||||
|
_text_msdf_frag = new gpu_mod.shader(_gpu, {
|
||||||
|
code: text_msdf_frag_code,
|
||||||
|
stage: "fragment",
|
||||||
|
format: "msl",
|
||||||
|
entrypoint: "fragment_main",
|
||||||
|
num_uniform_buffers: 1,
|
||||||
|
num_samplers: 1
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
var crt_frag_code = io.slurp("shaders/msl/crt.frag.msl")
|
var crt_frag_code = io.slurp("shaders/msl/crt.frag.msl")
|
||||||
if (crt_frag_code) {
|
if (crt_frag_code) {
|
||||||
_crt_frag = new gpu_mod.shader(_gpu, {
|
_crt_frag = new gpu_mod.shader(_gpu, {
|
||||||
@@ -482,6 +495,78 @@ function _create_pipelines() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SDF text pipeline
|
||||||
|
if (_sprite_vert && _text_sdf_frag) {
|
||||||
|
_pipelines.text_sdf = new gpu_mod.graphics_pipeline(_gpu, {
|
||||||
|
vertex: _sprite_vert,
|
||||||
|
fragment: _text_sdf_frag,
|
||||||
|
primitive: "triangle",
|
||||||
|
cull: "none",
|
||||||
|
face: "counter_clockwise",
|
||||||
|
fill: "fill",
|
||||||
|
vertex_buffer_descriptions: [{
|
||||||
|
slot: 0,
|
||||||
|
pitch: 32,
|
||||||
|
input_rate: "vertex"
|
||||||
|
}],
|
||||||
|
vertex_attributes: [
|
||||||
|
{location: 0, buffer_slot: 0, format: "float2", offset: 0},
|
||||||
|
{location: 1, buffer_slot: 0, format: "float2", offset: 8},
|
||||||
|
{location: 2, buffer_slot: 0, format: "float4", offset: 16}
|
||||||
|
],
|
||||||
|
target: {
|
||||||
|
color_targets: [{
|
||||||
|
format: _swapchain_format,
|
||||||
|
blend: {
|
||||||
|
enabled: true,
|
||||||
|
src_rgb: "src_alpha",
|
||||||
|
dst_rgb: "one_minus_src_alpha",
|
||||||
|
op_rgb: "add",
|
||||||
|
src_alpha: "one",
|
||||||
|
dst_alpha: "one_minus_src_alpha",
|
||||||
|
op_alpha: "add"
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// MSDF text pipeline
|
||||||
|
if (_sprite_vert && _text_msdf_frag) {
|
||||||
|
_pipelines.text_msdf = new gpu_mod.graphics_pipeline(_gpu, {
|
||||||
|
vertex: _sprite_vert,
|
||||||
|
fragment: _text_msdf_frag,
|
||||||
|
primitive: "triangle",
|
||||||
|
cull: "none",
|
||||||
|
face: "counter_clockwise",
|
||||||
|
fill: "fill",
|
||||||
|
vertex_buffer_descriptions: [{
|
||||||
|
slot: 0,
|
||||||
|
pitch: 32,
|
||||||
|
input_rate: "vertex"
|
||||||
|
}],
|
||||||
|
vertex_attributes: [
|
||||||
|
{location: 0, buffer_slot: 0, format: "float2", offset: 0},
|
||||||
|
{location: 1, buffer_slot: 0, format: "float2", offset: 8},
|
||||||
|
{location: 2, buffer_slot: 0, format: "float4", offset: 16}
|
||||||
|
],
|
||||||
|
target: {
|
||||||
|
color_targets: [{
|
||||||
|
format: _swapchain_format,
|
||||||
|
blend: {
|
||||||
|
enabled: true,
|
||||||
|
src_rgb: "src_alpha",
|
||||||
|
dst_rgb: "one_minus_src_alpha",
|
||||||
|
op_rgb: "add",
|
||||||
|
src_alpha: "one",
|
||||||
|
dst_alpha: "one_minus_src_alpha",
|
||||||
|
op_alpha: "add"
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
@@ -1140,21 +1225,15 @@ function _render_batch(cmd_buffer, pass, batch, camera, target) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function _render_text(cmd_buffer, pass, drawable, camera, target) {
|
function _render_text(cmd_buffer, pass, drawable, camera, target) {
|
||||||
// Get font
|
// Get font - support mode tag: 'bitmap', 'sdf', 'msdf'
|
||||||
var font_path = drawable.font
|
var font_path = drawable.font
|
||||||
var size = drawable.size || 16
|
var size = drawable.size || 16
|
||||||
var is_sdf = drawable.sdf || false
|
var mode = drawable.mode || (drawable.sdf ? 'sdf' : 'bitmap')
|
||||||
var font = _get_font_cache(font_path, size, is_sdf)
|
var font = _get_font_cache(font_path, size, mode)
|
||||||
if (!font) return
|
if (!font) return
|
||||||
|
|
||||||
// Generate vertices using staef
|
// Generate vertices using staef
|
||||||
var pos = drawable.pos
|
var pos = drawable.pos
|
||||||
// Convert world/camera pos to screen/local
|
|
||||||
// Note: staef generates raw vertex positions. We usually want to transform them by camera matrix.
|
|
||||||
// The sprite pipeline applies the camera matrix (proj) to the positions.
|
|
||||||
// So we should feed "local" positions (or world positions) into the buffer, and let vertex shader transform them.
|
|
||||||
// staef's make_text_buffer generates vertices relative to the input position.
|
|
||||||
|
|
||||||
var text_pos = {x: pos.x, y: pos.y, width: 0, height: 0}
|
var text_pos = {x: pos.x, y: pos.y, width: 0, height: 0}
|
||||||
var color = drawable.color || {r:1, g:1, b:1, a:1}
|
var color = drawable.color || {r:1, g:1, b:1, a:1}
|
||||||
|
|
||||||
@@ -1164,10 +1243,9 @@ function _render_text(cmd_buffer, pass, drawable, camera, target) {
|
|||||||
|
|
||||||
if (ax != 0 || ay != 0) {
|
if (ax != 0 || ay != 0) {
|
||||||
var dim = font.text_size(drawable.text)
|
var dim = font.text_size(drawable.text)
|
||||||
// dim is {x, y} (width, height)
|
|
||||||
if (dim) {
|
if (dim) {
|
||||||
text_pos.x -= dim.x * ax
|
text_pos.x -= dim.x * ax
|
||||||
text_pos.y -= dim.y * ay // staef usually draws from top-left, need to verify
|
text_pos.y -= dim.y * ay
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1178,7 +1256,7 @@ function _render_text(cmd_buffer, pass, drawable, camera, target) {
|
|||||||
var num_verts = mesh.num_vertices
|
var num_verts = mesh.num_vertices
|
||||||
var interleaved = geometry.weave([{data:mesh.xy, stride: mesh.xy_stride}, {data:mesh.uv, stride: mesh.uv_stride}, {data:mesh.color, stride: mesh.color_stride}])
|
var interleaved = geometry.weave([{data:mesh.xy, stride: mesh.xy_stride}, {data:mesh.uv, stride: mesh.uv_stride}, {data:mesh.color, stride: mesh.color_stride}])
|
||||||
|
|
||||||
var indices = mesh.indices // This is a blob of uint16
|
var indices = mesh.indices
|
||||||
var num_indices = mesh.num_indices
|
var num_indices = mesh.num_indices
|
||||||
|
|
||||||
// Upload
|
// Upload
|
||||||
@@ -1204,37 +1282,58 @@ function _render_text(cmd_buffer, pass, drawable, camera, target) {
|
|||||||
// Setup pipeline
|
// Setup pipeline
|
||||||
var proj = _build_camera_matrix(camera, target.width, target.height)
|
var proj = _build_camera_matrix(camera, target.width, target.height)
|
||||||
|
|
||||||
if (is_sdf && _pipelines.text_sdf) {
|
// Select pipeline based on mode
|
||||||
|
var is_sdf = (mode == 'sdf')
|
||||||
|
var is_msdf = (mode == 'msdf')
|
||||||
|
|
||||||
|
if (is_msdf && _pipelines.text_msdf) {
|
||||||
|
pass.bind_pipeline(_pipelines.text_msdf)
|
||||||
|
|
||||||
|
// Build uniforms for MSDF
|
||||||
|
// Struct: float outline_width, float sharpness, float2 _pad, float4 outline_color
|
||||||
|
var u_data = new blob_mod(32)
|
||||||
|
|
||||||
|
// Convert outline_width from pixel-ish units to normalized SDF units
|
||||||
|
// outline_width in drawable is in "visual" units, we need to normalize
|
||||||
|
// A typical range is 0.0-0.3 in SDF units
|
||||||
|
var outline_w = drawable.outline_width || 0
|
||||||
|
if (outline_w > 0) outline_w = outline_w / 100.0 // Scale down from user units
|
||||||
|
|
||||||
|
u_data.wf(outline_w) // outline_width
|
||||||
|
u_data.wf(font.sharpness || 1.0) // sharpness from font
|
||||||
|
u_data.wf(0) // _pad.x
|
||||||
|
u_data.wf(0) // _pad.y
|
||||||
|
|
||||||
|
var oc = drawable.outline_color || {r:0, g:0, b:0, a:1}
|
||||||
|
u_data.wf(oc.r) // outline_color.r
|
||||||
|
u_data.wf(oc.g) // outline_color.g
|
||||||
|
u_data.wf(oc.b) // outline_color.b
|
||||||
|
u_data.wf(oc.a || 1) // outline_color.a
|
||||||
|
|
||||||
|
cmd_buffer.push_fragment_uniform_data(0, stone(u_data))
|
||||||
|
|
||||||
|
} else if (is_sdf && _pipelines.text_sdf) {
|
||||||
pass.bind_pipeline(_pipelines.text_sdf)
|
pass.bind_pipeline(_pipelines.text_sdf)
|
||||||
|
|
||||||
// Upload uniforms for SDF (outline)
|
// Build uniforms for SDF
|
||||||
|
// Struct: float outline_width, float sharpness, float2 _pad, float4 outline_color
|
||||||
var u_data = new blob_mod(32)
|
var u_data = new blob_mod(32)
|
||||||
u_data.wf(drawable.outline_width || 0)
|
|
||||||
u_data.wf(0) // padding/unused
|
|
||||||
u_data.wf(0) // padding
|
|
||||||
u_data.wf(0) // padding
|
|
||||||
|
|
||||||
var oc = drawable.outline_color || {r:0, g:0, b:0, a:0}
|
var outline_w = drawable.outline_width || 0
|
||||||
|
if (outline_w > 0) outline_w = outline_w / 100.0
|
||||||
|
|
||||||
|
u_data.wf(outline_w) // outline_width
|
||||||
|
u_data.wf(font.sharpness || 1.0) // sharpness from font
|
||||||
|
u_data.wf(0) // _pad.x
|
||||||
|
u_data.wf(0) // _pad.y
|
||||||
|
|
||||||
|
var oc = drawable.outline_color || {r:0, g:0, b:0, a:1}
|
||||||
u_data.wf(oc.r)
|
u_data.wf(oc.r)
|
||||||
u_data.wf(oc.g)
|
u_data.wf(oc.g)
|
||||||
u_data.wf(oc.b)
|
u_data.wf(oc.b)
|
||||||
u_data.wf(oc.a) // Used as alpha?? Shader expects float3 color. Structure has float4 color?
|
u_data.wf(oc.a || 1)
|
||||||
// Shader: float outline_width; float3 outline_color;
|
|
||||||
// Layout: offset 0 (4 bytes), offset 16 (16 bytes vec4 alignment usually for float3)
|
|
||||||
// Actually metal float3 is 16 byte aligned/sized often in buffers?
|
|
||||||
// Let's assume standard packed: float (4), float3 (12 needed, but alignment constraints).
|
|
||||||
// Uniforms struct: width (4), padding (12) -> size 16. Color (12/16) -> offset 16.
|
|
||||||
|
|
||||||
// Let's rewrite struct in shader or be careful.
|
cmd_buffer.push_fragment_uniform_data(0, stone(u_data))
|
||||||
// Struct: float outline_width; float3 outline_color;
|
|
||||||
// If strict metal alignment:
|
|
||||||
// width at 0.
|
|
||||||
// float3 at 16 (since it's a type that requires 16 byte alignment? No, float4 does. float3 is usually float4 size/alignment in buffers).
|
|
||||||
|
|
||||||
pass.push_fragment_uniform_data(0, stone(u_data)) // Wait, push_fragment_uniform_data on cmd_buffer?
|
|
||||||
// The code below uses cmd_buffer.push_vertex_uniform_data(0, proj)
|
|
||||||
// We need push_fragment_uniform_data.
|
|
||||||
cmd_buffer.push_fragment_uniform_data(0, stone(u_data)) // Bind to buffer(0) in fragment
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
pass.bind_pipeline(_pipelines.sprite_alpha)
|
pass.bind_pipeline(_pipelines.sprite_alpha)
|
||||||
@@ -1243,32 +1342,46 @@ function _render_text(cmd_buffer, pass, drawable, camera, target) {
|
|||||||
pass.bind_vertex_buffers(0, [{buffer: vb, offset: 0}])
|
pass.bind_vertex_buffers(0, [{buffer: vb, offset: 0}])
|
||||||
pass.bind_index_buffer({buffer: ib, offset: 0}, 16)
|
pass.bind_index_buffer({buffer: ib, offset: 0}, 16)
|
||||||
|
|
||||||
// Bind font texture
|
// Bind font texture - use linear filtering for SDF/MSDF
|
||||||
// staef font has 'texture' property which is pixel blob + dims. We need to upload it to GPU if not already.
|
var font_tex = _get_font_texture(font, mode)
|
||||||
var font_tex = _get_font_texture(font, is_sdf)
|
var sampler = (is_sdf || is_msdf) ? _sampler_linear : _sampler_nearest
|
||||||
|
|
||||||
pass.bind_fragment_samplers(0, [{texture: font_tex, sampler: _sampler_nearest}])
|
pass.bind_fragment_samplers(0, [{texture: font_tex, sampler: sampler}])
|
||||||
cmd_buffer.push_vertex_uniform_data(0, proj)
|
cmd_buffer.push_vertex_uniform_data(0, proj)
|
||||||
pass.draw_indexed(num_indices, 1, 0, 0, 0)
|
pass.draw_indexed(num_indices, 1, 0, 0, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
function _get_font_cache(path, size, is_sdf) {
|
function _get_font_cache(path, size, mode) {
|
||||||
var key = `${path}.${size}.${is_sdf ? 'sdf' : 'bmp'}`
|
// mode can be 'bitmap', 'sdf', 'msdf', or boolean (legacy)
|
||||||
|
if (mode == true) mode = 'sdf'
|
||||||
|
else if (mode == false || !mode) mode = 'bitmap'
|
||||||
|
|
||||||
|
var key = `${path}.${size}.${mode}`
|
||||||
if (_font_cache[key]) return _font_cache[key]
|
if (_font_cache[key]) return _font_cache[key]
|
||||||
|
|
||||||
var fullpath = res.find_font(path) // Assuming this resolves correctly
|
var fullpath = res.find_font(path)
|
||||||
if (!fullpath) return null
|
if (!fullpath) return null
|
||||||
|
|
||||||
var data = io.slurp(fullpath)
|
var data = io.slurp(fullpath)
|
||||||
if (!data) return null
|
if (!data) return null
|
||||||
|
|
||||||
// Create staef font
|
// Create staef font based on mode
|
||||||
try {
|
try {
|
||||||
var font = new staef.font(data, size, is_sdf)
|
var font
|
||||||
|
if (mode == 'msdf') {
|
||||||
|
// MSDF: em_px=size, range_px=4, padding_px=6, sharpness=1.0
|
||||||
|
font = new staef.msdf_font(data, size, 4.0, 6, 1.0)
|
||||||
|
} else if (mode == 'sdf') {
|
||||||
|
// SDF: em_px=size, range_px=12, padding_px=14, sharpness=1.0
|
||||||
|
font = new staef.sdf_font(data, size, 12.0, 14, 1.0)
|
||||||
|
} else {
|
||||||
|
// Bitmap
|
||||||
|
font = new staef.font(data, size, false)
|
||||||
|
}
|
||||||
_font_cache[key] = font
|
_font_cache[key] = font
|
||||||
return font
|
return font
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
log.console(`sdl_gpu: Failed to load font ${path}:${size}: ${e.message}`)
|
log.console(`sdl_gpu: Failed to load font ${path}:${size}:${mode}: ${e.message}`)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
498
staef.c
498
staef.c
@@ -34,6 +34,12 @@ typedef enum {
|
|||||||
CHARACTER
|
CHARACTER
|
||||||
} TEXT_BREAK;
|
} TEXT_BREAK;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
FONT_MODE_BITMAP = 0,
|
||||||
|
FONT_MODE_SDF = 1,
|
||||||
|
FONT_MODE_MSDF = 2
|
||||||
|
} FONT_MODE;
|
||||||
|
|
||||||
struct text_char {
|
struct text_char {
|
||||||
rect pos;
|
rect pos;
|
||||||
rect uv;
|
rect uv;
|
||||||
@@ -55,15 +61,17 @@ struct character {
|
|||||||
|
|
||||||
// text data
|
// text data
|
||||||
struct sFont {
|
struct sFont {
|
||||||
uint32_t height; /* in pixels */
|
uint32_t height; /* em_px: glyph size in atlas pixels */
|
||||||
float ascent; // pixels
|
float ascent; // pixels
|
||||||
float descent; // pixels
|
float descent; // pixels
|
||||||
float linegap; //pixels
|
float linegap; //pixels
|
||||||
float line_height; // pixels
|
float line_height; // pixels
|
||||||
struct character Characters[256];
|
struct character Characters[256];
|
||||||
unsigned char *pixels; // RGBA8 pixel data
|
unsigned char *pixels; // RGBA8 pixel data (RGB for MSDF)
|
||||||
int atlas_size; // width and height of atlas (square)
|
int atlas_size; // width and height of atlas (square)
|
||||||
int is_sdf;
|
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 sFont font;
|
||||||
@@ -81,15 +89,269 @@ void font_free(JSRuntime *rt, font *f)
|
|||||||
free(f);
|
free(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sFont *MakeFont(void *ttf_buffer, size_t len, int height, int is_sdf) {
|
// 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)
|
if (!ttf_buffer)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
int packsize = 1024;
|
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));
|
struct sFont *newfont = calloc(1, sizeof(struct sFont));
|
||||||
newfont->height = height;
|
newfont->height = height;
|
||||||
newfont->is_sdf = is_sdf;
|
newfont->mode = FONT_MODE_BITMAP;
|
||||||
|
newfont->range_px = 0;
|
||||||
|
newfont->sharpness = 1.0f;
|
||||||
newfont->atlas_size = packsize;
|
newfont->atlas_size = packsize;
|
||||||
|
|
||||||
unsigned char *bitmap = calloc(1, packsize * packsize);
|
unsigned char *bitmap = calloc(1, packsize * packsize);
|
||||||
@@ -109,99 +371,8 @@ struct sFont *MakeFont(void *ttf_buffer, size_t len, int height, int is_sdf) {
|
|||||||
newfont->linegap = linegap * scale;
|
newfont->linegap = linegap * scale;
|
||||||
newfont->line_height = (newfont->ascent - newfont->descent) + newfont->linegap;
|
newfont->line_height = (newfont->ascent - newfont->descent) + newfont->linegap;
|
||||||
|
|
||||||
if (is_sdf) {
|
// Bitmap-only path
|
||||||
// Manual SDF packing
|
{
|
||||||
int x = 0;
|
|
||||||
int y = 0;
|
|
||||||
int row_height = 0;
|
|
||||||
int pad = 5; // padding for SDF
|
|
||||||
float onedge_value = 127.5f; // 128ish
|
|
||||||
float pixel_dist_scale = 150.f; // Distance field range
|
|
||||||
|
|
||||||
for (unsigned char c = 32; c < 127; c++) {
|
|
||||||
int glyph_index = c - 29; // Simple ASCII mapping? verify if font has proper map
|
|
||||||
// Actually standard packing uses PackFontRange which maps ASCII 32..126
|
|
||||||
// We should use stbtt_FindGlyphIndex(&fontinfo, 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) {
|
|
||||||
// Handle invisible characters (space)
|
|
||||||
int advance, lsb;
|
|
||||||
stbtt_GetGlyphHMetrics(&fontinfo, g, &advance, &lsb);
|
|
||||||
newfont->Characters[c].advance = advance * scale;
|
|
||||||
// Keep quad/uv as 0
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (x + width + 1 > packsize) {
|
|
||||||
x = 0;
|
|
||||||
y += row_height + 1;
|
|
||||||
row_height = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (y + height + 1 > packsize) {
|
|
||||||
// Out of space
|
|
||||||
free(sdf);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Blit SDF to atlas
|
|
||||||
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);
|
|
||||||
|
|
||||||
// Store character info
|
|
||||||
rect uv;
|
|
||||||
uv.x = (float)x / packsize;
|
|
||||||
uv.y = (float)(y + height) / packsize; // Bottom (Top of glyph in SDF?)
|
|
||||||
// To match bitmap path which produces UPSIDE DOWN results if not flipped:
|
|
||||||
// Bitmap path: uv.h = Negative.
|
|
||||||
// So UVs are (y1, y0). (Top Texture is at Bottom vertex).
|
|
||||||
// Here we want to match that.
|
|
||||||
// Top Vertex (v0) should get Bottom Texture.
|
|
||||||
// Bottom Vertex (v1) should get Top Texture.
|
|
||||||
|
|
||||||
// Let's make uv.h negative.
|
|
||||||
// Top Texture is at 'y'. Bottom Texture is at 'y+height'.
|
|
||||||
// uv.y = (y+height)/size.
|
|
||||||
// uv.h = -height/size.
|
|
||||||
// v0 (Top Vert) = uv.y = y+height (Bottom Tex).
|
|
||||||
// v1 (Bottom Vert) = uv.y + uv.h = y (Top Tex).
|
|
||||||
// This flips the texture on Y.
|
|
||||||
|
|
||||||
uv.w = (float)width / packsize;
|
|
||||||
uv.h = -(float)height / packsize;
|
|
||||||
newfont->Characters[c].uv = uv;
|
|
||||||
|
|
||||||
rect quad;
|
|
||||||
quad.x = (float)xoff;
|
|
||||||
// Bitmap path: quad.y = -yoff2. (Bottom in Y-Up).
|
|
||||||
// SDF path: yoff is Top in Y-Down (-Top in Y-Up).
|
|
||||||
// height is height.
|
|
||||||
// We want Bottom in Y-Up.
|
|
||||||
// Bottom = - (yoff + height).
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Original Bitmap packing
|
// Original Bitmap packing
|
||||||
stbtt_packedchar glyphs[95];
|
stbtt_packedchar glyphs[95];
|
||||||
stbtt_pack_context pc;
|
stbtt_pack_context pc;
|
||||||
@@ -259,6 +430,20 @@ struct sFont *MakeFont(void *ttf_buffer, size_t len, int height, int is_sdf) {
|
|||||||
return newfont;
|
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,
|
layout_lines layout_text_lines(const char *text, font *f,
|
||||||
float letter_spacing,
|
float letter_spacing,
|
||||||
float wrap,
|
float wrap,
|
||||||
@@ -492,23 +677,8 @@ struct text_vert *renderText(const char *text, HMM_Vec2 pos, font *f, colorf col
|
|||||||
// QuickJS class for font
|
// QuickJS class for font
|
||||||
QJSCLASS(font,)
|
QJSCLASS(font,)
|
||||||
|
|
||||||
// Font constructor
|
// Helper to attach texture and mode info to font JS object
|
||||||
JSC_CCALL(staef_font_new,
|
static void attach_font_texture(JSContext *js, JSValue ret, font *f) {
|
||||||
size_t len;
|
|
||||||
void *data = js_get_blob_data(js, &len, argv[0]);
|
|
||||||
if (data == -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);
|
|
||||||
|
|
||||||
// Create texture data object for the font's atlas
|
|
||||||
if (f->pixels) {
|
if (f->pixels) {
|
||||||
JSValue texData = JS_NewObject(js);
|
JSValue texData = JS_NewObject(js);
|
||||||
JS_SetPropertyStr(js, texData, "width", JS_NewInt32(js, f->atlas_size));
|
JS_SetPropertyStr(js, texData, "width", JS_NewInt32(js, f->atlas_size));
|
||||||
@@ -520,6 +690,72 @@ JSC_CCALL(staef_font_new,
|
|||||||
|
|
||||||
JS_SetPropertyStr(js, ret, "texture", texData);
|
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
|
// Calculate text size
|
||||||
@@ -565,6 +801,9 @@ JSC_GETSET(font, ascent, number)
|
|||||||
JSC_GETSET(font, descent, number)
|
JSC_GETSET(font, descent, number)
|
||||||
JSC_GETSET(font, line_height, number)
|
JSC_GETSET(font, line_height, number)
|
||||||
JSC_GETSET(font, 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
|
// Font methods
|
||||||
static const JSCFunctionListEntry js_font_funcs[] = {
|
static const JSCFunctionListEntry js_font_funcs[] = {
|
||||||
@@ -575,6 +814,9 @@ static const JSCFunctionListEntry js_font_funcs[] = {
|
|||||||
CGETSET_ADD(font, descent),
|
CGETSET_ADD(font, descent),
|
||||||
CGETSET_ADD(font, line_height),
|
CGETSET_ADD(font, line_height),
|
||||||
CGETSET_ADD(font, height),
|
CGETSET_ADD(font, height),
|
||||||
|
CGETSET_ADD(font, range_px),
|
||||||
|
CGETSET_ADD(font, sharpness),
|
||||||
|
CGETSET_ADD(font, mode),
|
||||||
};
|
};
|
||||||
|
|
||||||
// Font constructor function
|
// Font constructor function
|
||||||
@@ -583,6 +825,18 @@ static JSValue js_font_constructor(JSContext *ctx, JSValueConst new_target, int
|
|||||||
return js_staef_font_new(ctx, JS_NULL, argc, 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
|
// Initialize the staef module
|
||||||
CELL_USE_INIT(
|
CELL_USE_INIT(
|
||||||
JSValue mod = JS_NewObject(js);
|
JSValue mod = JS_NewObject(js);
|
||||||
@@ -595,12 +849,20 @@ CELL_USE_INIT(
|
|||||||
JS_SetPropertyFunctionList(js, proto, js_font_funcs, countof(js_font_funcs));
|
JS_SetPropertyFunctionList(js, proto, js_font_funcs, countof(js_font_funcs));
|
||||||
JS_SetClassProto(js, js_font_id, proto);
|
JS_SetClassProto(js, js_font_id, proto);
|
||||||
|
|
||||||
// Create font constructor
|
// Create font constructor (legacy)
|
||||||
JSValue font_ctor = JS_NewCFunction2(js, js_font_constructor, "font", 2, JS_CFUNC_constructor, 0);
|
JSValue font_ctor = JS_NewCFunction2(js, js_font_constructor, "font", 2, JS_CFUNC_constructor, 0);
|
||||||
JS_SetConstructor(js, font_ctor, proto);
|
JS_SetConstructor(js, font_ctor, proto);
|
||||||
|
|
||||||
// Add font constructor to module (lowercase to match "new staef.font")
|
|
||||||
JS_SetPropertyStr(js, mod, "font", font_ctor);
|
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_constructor, 0);
|
||||||
|
JS_SetConstructor(js, sdf_font_ctor, proto);
|
||||||
|
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_constructor, 0);
|
||||||
|
JS_SetConstructor(js, msdf_font_ctor, proto);
|
||||||
|
JS_SetPropertyStr(js, mod, "msdf_font", msdf_font_ctor);
|
||||||
|
|
||||||
return mod;
|
return mod;
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user