Save
This commit is contained in:
@@ -2,7 +2,6 @@
|
||||
|
||||
for %%a in (%*) do set "%%~a=1"
|
||||
|
||||
|
||||
set common=-I ../src/ -g -fdiagnostics-absolute-paths -Wno-unsequenced -Wno-single-bit-bitfield-constant-conversion -Wall -Wno-missing-braces -Wextra -Wno-missing-field-initializers
|
||||
set wasm_flags=--target=wasm32 -nostdlib -mbulk-memory -msimd128 -Wl,-export-dynamic,--allow-undefined,--import-memory,--no-entry,--initial-memory=131072000,--max-memory=4294967296
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "core_platform.h"
|
||||
#include "core_ctx.h"
|
||||
#include "stb_sprintf.h"
|
||||
|
||||
#if PLATFORM_WASM && PLATFORM_EMSCRIPTEN == 0
|
||||
#define gb_wasm_export __attribute__((visibility("default")))
|
||||
@@ -69,7 +70,7 @@ fn void os_core_init(void) {
|
||||
perm.commit = perm.reserve = memory_size;
|
||||
os_core_small_init(&perm);
|
||||
tcx->perm = perm;
|
||||
ma_push_arena_ex(&tcx->perm, &tcx->temp, mib(16));
|
||||
ma_push_arena_ex(&tcx->perm, &tcx->temp, mib(64));
|
||||
ma_push_arena_ex(&tcx->perm, &tcx->scratch[0], mib(2));
|
||||
ma_push_arena_ex(&tcx->perm, &tcx->scratch[1], kib(256));
|
||||
ma_push_arena_ex(&tcx->perm, &tcx->scratch[2], kib(64));
|
||||
@@ -134,7 +135,7 @@ static print_buf buf_init(void) {
|
||||
static void buf_printf(print_buf* b, const char* fmt, ...) {
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
const int len = vsnprintf(NULL, 0, fmt, args) + 1;
|
||||
const int len = stbsp_vsnprintf(NULL, 0, fmt, args) + 1;
|
||||
va_end(args);
|
||||
|
||||
const int new_end = b->pos + len;
|
||||
@@ -145,7 +146,7 @@ static void buf_printf(print_buf* b, const char* fmt, ...) {
|
||||
}
|
||||
|
||||
va_start(args, fmt);
|
||||
b->pos += vsnprintf(b->buf + b->pos, len, fmt, args);
|
||||
b->pos += stbsp_vsnprintf(b->buf + b->pos, len, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
@@ -157,7 +158,6 @@ char* b_stacktrace_get_string(void) {
|
||||
}
|
||||
|
||||
#define B_STACKTRACE_MAX_DEPTH 1024
|
||||
|
||||
#define B_STACKTRACE_ERROR_FLAG ((DWORD64)1 << 63)
|
||||
|
||||
typedef struct b_stacktrace_entry {
|
||||
|
||||
@@ -4,57 +4,138 @@
|
||||
<meta charset="UTF-8">
|
||||
<title>Canvas</title>
|
||||
<style>
|
||||
html,body{margin:0;height:100%;overflow:hidden;padding:0;font-size: 100px;font-family: FiraCode;}
|
||||
canvas{display:block;width:100%;height:250px;border-bottom: dotted;}
|
||||
|
||||
@font-face {
|
||||
font-family: 'FiraCode';
|
||||
src: url('./FiraCode-Regular.ttf');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
div{
|
||||
position:relative;
|
||||
display: block;
|
||||
width: 100%;
|
||||
|
||||
text-align: center;
|
||||
border-bottom: dotted;
|
||||
}
|
||||
|
||||
html,body{margin:0;height:100%;overflow:hidden;padding:0;}
|
||||
canvas{display:block;width:100%;height:100%;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<canvas id="glcanvas"></canvas>
|
||||
<div>
|
||||
<p>Hello world (html)</p>
|
||||
</div>
|
||||
<canvas id="2dcanvas"></canvas>
|
||||
<script>
|
||||
function createShader(gl, type, src) {
|
||||
const sh = gl.createShader(type);
|
||||
gl.shaderSource(sh, src);
|
||||
gl.compileShader(sh);
|
||||
if (!gl.getShaderParameter(sh, gl.COMPILE_STATUS)) {
|
||||
console.error('Shader compile error:', gl.getShaderInfoLog(sh));
|
||||
gl.deleteShader(sh);
|
||||
return null;
|
||||
}
|
||||
return sh;
|
||||
}
|
||||
|
||||
function createProgram(gl, vs, fs) {
|
||||
const prg = gl.createProgram();
|
||||
gl.attachShader(prg, vs);
|
||||
gl.attachShader(prg, fs);
|
||||
gl.linkProgram(prg);
|
||||
if (!gl.getProgramParameter(prg, gl.LINK_STATUS)) {
|
||||
console.error('Program link error:', gl.getProgramInfoLog(prg));
|
||||
gl.deleteProgram(prg);
|
||||
return null;
|
||||
CANVAS = null;
|
||||
GL = null;
|
||||
class wglRender {
|
||||
constructor() {
|
||||
const VERTEX_SRC = `#version 300 es
|
||||
precision mediump float;
|
||||
in vec2 a_position;
|
||||
out vec2 v_texCoord;
|
||||
void main() {
|
||||
v_texCoord = (a_position + 1.0) * 0.5; // [0,1]
|
||||
v_texCoord.y = (1.0 - v_texCoord.y); // Flip the Y to get the standard memory behavior
|
||||
gl_Position = vec4(a_position, 0.0, 1.0);
|
||||
}`;
|
||||
|
||||
const FRAGMENT_SRC = `#version 300 es
|
||||
precision mediump float;
|
||||
in vec2 v_texCoord;
|
||||
uniform sampler2D u_texture;
|
||||
out vec4 outColor;
|
||||
void main() {
|
||||
outColor = texture(u_texture, v_texCoord);
|
||||
}`;
|
||||
|
||||
CANVAS = document.getElementById('glcanvas');
|
||||
GL = CANVAS.getContext('webgl2');
|
||||
if (!GL) { alert('WebGL 2 not supported'); return; }
|
||||
|
||||
const vs = this.createShader(GL.VERTEX_SHADER, VERTEX_SRC);
|
||||
const fs = this.createShader(GL.FRAGMENT_SHADER, FRAGMENT_SRC);
|
||||
this.program = this.createProgram(vs, fs);
|
||||
|
||||
this.posLoc = GL.getAttribLocation(this.program, 'a_position');
|
||||
this.texLoc = GL.getUniformLocation(this.program, 'u_texture');
|
||||
|
||||
// Fullscreen quad
|
||||
const quad = new Float32Array([
|
||||
-1, -1, // bottom left
|
||||
1, -1, // bottom right
|
||||
-1, 1, // top left
|
||||
-1, 1, // top left
|
||||
1, -1, // bottom right
|
||||
1, 1 // top right
|
||||
]);
|
||||
|
||||
this.vao = GL.createVertexArray();
|
||||
GL.bindVertexArray(this.vao);
|
||||
this.vbo = GL.createBuffer();
|
||||
GL.bindBuffer(GL.ARRAY_BUFFER, this.vbo);
|
||||
GL.bufferData(GL.ARRAY_BUFFER, quad, GL.STATIC_DRAW);
|
||||
GL.enableVertexAttribArray(this.posLoc);
|
||||
GL.vertexAttribPointer(this.posLoc, 2, GL.FLOAT, false, 0, 0);
|
||||
}
|
||||
|
||||
render() {
|
||||
GL.useProgram(this.program);
|
||||
GL.bindVertexArray(this.vao);
|
||||
GL.activeTexture(GL.TEXTURE0);
|
||||
GL.bindTexture(GL.TEXTURE_2D, this.texture);
|
||||
GL.uniform1i(this.texLoc, 0);
|
||||
GL.drawArrays(GL.TRIANGLES, 0, 6);
|
||||
}
|
||||
|
||||
updateTextureSize(wasm) {
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
const w = Math.floor(CANVAS.clientWidth * dpr);
|
||||
const h = Math.floor(CANVAS.clientHeight * dpr);
|
||||
if (w !== this.w || h !== this.h) {
|
||||
CANVAS.width = w;
|
||||
CANVAS.height = h;
|
||||
GL.viewport(0, 0, w, h);
|
||||
|
||||
this.w = w;
|
||||
this.h = h;
|
||||
if (this.texture) {
|
||||
GL.deleteTexture(this.texture);
|
||||
}
|
||||
const rawPtr = wasm.exports.update(w, h, window.devicePixelRatio || 1.0);
|
||||
const pixels = wasm.getU8View(rawPtr, w*h*4);
|
||||
{
|
||||
this.texture = GL.createTexture();
|
||||
GL.bindTexture(GL.TEXTURE_2D, this.texture);
|
||||
// Allocate an empty texture of the canvas size
|
||||
GL.texImage2D(
|
||||
GL.TEXTURE_2D, 0, GL.RGBA8,
|
||||
w, h, 0,
|
||||
GL.RGBA, GL.UNSIGNED_BYTE, pixels
|
||||
);
|
||||
// Set parameters – no filtering needed for empty data
|
||||
GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MIN_FILTER, GL.NEAREST);
|
||||
GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_MAG_FILTER, GL.NEAREST);
|
||||
GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_WRAP_S, GL.CLAMP_TO_EDGE);
|
||||
GL.texParameteri(GL.TEXTURE_2D, GL.TEXTURE_WRAP_T, GL.CLAMP_TO_EDGE);
|
||||
}
|
||||
|
||||
// GL.activeTexture(GL.TEXTURE0);
|
||||
// GL.bindTexture(GL.TEXTURE_2D, this.texture);
|
||||
// GL.texSubImage2D(GL.TEXTURE_2D, 0, 0, 0, w, h, GL.RGBA, GL.UNSIGNED_BYTE,pixels);
|
||||
}
|
||||
}
|
||||
|
||||
createShader(type, src) {
|
||||
const sh = GL.createShader(type);
|
||||
GL.shaderSource(sh, src);
|
||||
GL.compileShader(sh);
|
||||
if (!GL.getShaderParameter(sh, GL.COMPILE_STATUS)) {
|
||||
console.error('Shader compile error:', GL.getShaderInfoLog(sh));
|
||||
GL.deleteShader(sh);
|
||||
return null;
|
||||
}
|
||||
return sh;
|
||||
}
|
||||
|
||||
createProgram(vs, fs) {
|
||||
const prg = GL.createProgram();
|
||||
GL.attachShader(prg, vs);
|
||||
GL.attachShader(prg, fs);
|
||||
GL.linkProgram(prg);
|
||||
if (!GL.getProgramParameter(prg, GL.LINK_STATUS)) {
|
||||
console.error('Program link error:', GL.getProgramInfoLog(prg));
|
||||
GL.deleteProgram(prg);
|
||||
return null;
|
||||
}
|
||||
return prg;
|
||||
}
|
||||
return prg;
|
||||
}
|
||||
|
||||
class WASMMemory {
|
||||
@@ -67,18 +148,23 @@ class WASMMemory {
|
||||
this.exports = null;
|
||||
}
|
||||
|
||||
readString(str, len) {
|
||||
decodeString(str, len) {
|
||||
const arr = this.u8.subarray(str, str+len);
|
||||
const text = this.utf8decoder.decode(arr);
|
||||
return text;
|
||||
}
|
||||
|
||||
readUint8(p, len) {
|
||||
getU8View(p, len) {
|
||||
const arr = this.u8.subarray(p, p+len);
|
||||
return arr;
|
||||
}
|
||||
|
||||
writeString(ptr, ptr_len, string) {
|
||||
getView(p, len) {
|
||||
const res = new DataView(this.mem.buffer, p, p+len);
|
||||
return res;
|
||||
}
|
||||
|
||||
encodeString(ptr, ptr_len, string) {
|
||||
const bytes = this.utf8encoder.encode(string);
|
||||
let i = 0;
|
||||
for (; i < bytes.length && i < (ptr_len-1); i += 1) {
|
||||
@@ -94,10 +180,10 @@ async function WASMInit(name) {
|
||||
const binary = await request.arrayBuffer();
|
||||
const import_table = {
|
||||
memory: mem.mem,
|
||||
wasm_parse_float: (str, len) => { return parseFloat(mem.readString(str, len)); },
|
||||
wasm_alert: (str, len) => { alert(mem.readString(str,len)); },
|
||||
wasm_parse_float: (str, len) => { return parseFloat(mem.decodeString(str, len)); },
|
||||
wasm_alert: (str, len) => { alert(mem.decodeString(str,len)); },
|
||||
wasm_trap: () => { throw new Error(); },
|
||||
wasm_write_to_console: (str, len) => { console.log(mem.readString(str, len)); },
|
||||
wasm_write_to_console: (str, len) => { console.log(mem.decodeString(str, len)); },
|
||||
};
|
||||
const program = await WebAssembly['instantiate'](binary, { "env": import_table });
|
||||
const instance = program['instance'];
|
||||
@@ -107,146 +193,17 @@ async function WASMInit(name) {
|
||||
}
|
||||
|
||||
(async () => {
|
||||
const render = new wglRender();
|
||||
const wasm = await WASMInit("main.wasm");
|
||||
wasm.exports.init();
|
||||
|
||||
|
||||
const VERTEX_SRC = `#version 300 es
|
||||
precision mediump float;
|
||||
in vec2 a_position;
|
||||
out vec2 v_texCoord;
|
||||
void main() {
|
||||
v_texCoord = (a_position + 1.0) * 0.5; // [0,1]
|
||||
v_texCoord.y = (1.0 - v_texCoord.y); // Flip the Y to get the standard memory behavior
|
||||
gl_Position = vec4(a_position, 0.0, 1.0);
|
||||
}`;
|
||||
|
||||
const FRAGMENT_SRC = `#version 300 es
|
||||
precision mediump float;
|
||||
in vec2 v_texCoord;
|
||||
uniform sampler2D u_texture;
|
||||
out vec4 outColor;
|
||||
void main() {
|
||||
outColor = texture(u_texture, v_texCoord);
|
||||
}`;
|
||||
|
||||
/* ---------- WebGL setup ---------- */
|
||||
const canvas = document.getElementById('glcanvas');
|
||||
const gl = canvas.getContext('webgl2');
|
||||
if (!gl) { alert('WebGL 2 not supported'); return; }
|
||||
|
||||
const vs = createShader(gl, gl.VERTEX_SHADER, VERTEX_SRC);
|
||||
const fs = createShader(gl, gl.FRAGMENT_SHADER, FRAGMENT_SRC);
|
||||
const program = createProgram(gl, vs, fs);
|
||||
|
||||
const posLoc = gl.getAttribLocation(program, 'a_position');
|
||||
const texLoc = gl.getUniformLocation(program, 'u_texture');
|
||||
|
||||
// Fullscreen quad
|
||||
const quad = new Float32Array([
|
||||
-1, -1, // bottom left
|
||||
1, -1, // bottom right
|
||||
-1, 1, // top left
|
||||
-1, 1, // top left
|
||||
1, -1, // bottom right
|
||||
1, 1 // top right
|
||||
]);
|
||||
|
||||
const vao = gl.createVertexArray();
|
||||
gl.bindVertexArray(vao);
|
||||
const vbo = gl.createBuffer();
|
||||
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
|
||||
gl.bufferData(gl.ARRAY_BUFFER, quad, gl.STATIC_DRAW);
|
||||
gl.enableVertexAttribArray(posLoc);
|
||||
gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0);
|
||||
|
||||
|
||||
/* ---------- Dynamic texture handling ---------- */
|
||||
let texture = null;
|
||||
let texWidth = 0, texHeight = 0;
|
||||
let pixels = null;
|
||||
|
||||
function createTexture(w, h) {
|
||||
const tex = gl.createTexture();
|
||||
gl.bindTexture(gl.TEXTURE_2D, tex);
|
||||
// Allocate an empty texture of the canvas size
|
||||
gl.texImage2D(
|
||||
gl.TEXTURE_2D, 0, gl.RGBA8,
|
||||
w, h, 0,
|
||||
gl.RGBA, gl.UNSIGNED_BYTE, null
|
||||
);
|
||||
// Set parameters – no filtering needed for empty data
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
||||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
||||
return tex;
|
||||
function frameLoop() {
|
||||
render.updateTextureSize(wasm);
|
||||
render.render();
|
||||
requestAnimationFrame(frameLoop);
|
||||
}
|
||||
|
||||
function updateTextureSize() {
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
const w = Math.floor(canvas.clientWidth * dpr);
|
||||
const h = Math.floor(canvas.clientHeight * dpr);
|
||||
if (w !== texWidth || h !== texHeight) {
|
||||
texWidth = w; texHeight = h;
|
||||
if (texture) gl.deleteTexture(texture);
|
||||
texture = createTexture(w, h);
|
||||
|
||||
let rawPtr = wasm.exports.update(texWidth, texHeight, window.devicePixelRatio || 1.0);
|
||||
pixels = wasm.readUint8(rawPtr, texWidth*texHeight*4);
|
||||
|
||||
{
|
||||
const canvas = document.getElementById('2dcanvas');
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
const w = Math.floor(canvas.clientWidth * dpr);
|
||||
const h = Math.floor(canvas.clientHeight * dpr);
|
||||
canvas.width = w;
|
||||
canvas.height = h;
|
||||
const ctx = canvas.getContext('2d');
|
||||
let size = 100*dpr;
|
||||
ctx.font = `${size}px FiraCode`;
|
||||
const text = 'Hello world (canvas)';
|
||||
ctx.textAlign = 'center';
|
||||
ctx.textBaseline = 'middle';
|
||||
const centerX = canvas.width / 2;
|
||||
const centerY = canvas.height / 2;
|
||||
ctx.fillStyle = '#000';
|
||||
ctx.fillText(text, centerX, centerY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- Canvas resize routine ---------- */
|
||||
function resizeCanvasToDisplaySize() {
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
const w = Math.floor(canvas.clientWidth * dpr);
|
||||
const h = Math.floor(canvas.clientHeight * dpr);
|
||||
if (canvas.width !== w || canvas.height !== h) {
|
||||
canvas.width = w;
|
||||
canvas.height = h;
|
||||
gl.viewport(0, 0, w, h);
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------- Render loop ---------- */
|
||||
function render() {
|
||||
resizeCanvasToDisplaySize();
|
||||
updateTextureSize();
|
||||
|
||||
|
||||
gl.useProgram(program);
|
||||
gl.bindVertexArray(vao);
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, texture);
|
||||
gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, texWidth, texHeight, gl.RGBA, gl.UNSIGNED_BYTE, pixels);
|
||||
gl.uniform1i(texLoc, 0);
|
||||
|
||||
gl.drawArrays(gl.TRIANGLES, 0, 6);
|
||||
|
||||
requestAnimationFrame(render);
|
||||
}
|
||||
|
||||
requestAnimationFrame(render);
|
||||
requestAnimationFrame(frameLoop);
|
||||
|
||||
|
||||
})();
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
#include "core/core.c"
|
||||
|
||||
#define STB_TRUETYPE_IMPLEMENTATION
|
||||
#define STBTT_ifloor(x) ((int)f64_floor(x))
|
||||
#define STBTT_iceil(x) ((int)f64_ceil(x))
|
||||
#define STBTT_ifloor(x) ((i32)f64_floor(x))
|
||||
#define STBTT_iceil(x) ((i32)f64_ceil(x))
|
||||
#define STBTT_sqrt(x) (f64_sqrt(x))
|
||||
#define STBTT_fmod(x,y) (f64_mod(x,y))
|
||||
#define STBTT_pow(x,y) (f64_pow(x,y))
|
||||
@@ -20,32 +20,123 @@
|
||||
#include "vendor/stb/stb_truetype.h"
|
||||
#include "fira_code.c"
|
||||
|
||||
typedef struct glyph_t glyph_t;
|
||||
struct glyph_t {
|
||||
u8 *data;
|
||||
i32 width, height;
|
||||
i32 xoff, yoff;
|
||||
i32 left_side_bearing, xadvance;
|
||||
};
|
||||
|
||||
typedef struct font_t font_t;
|
||||
struct font_t {
|
||||
glyph_t glyphs[96];
|
||||
i32 ascent, descent, line_gap, size;
|
||||
f32 scale;
|
||||
};
|
||||
|
||||
typedef struct bitmap_t bitmap_t;
|
||||
struct bitmap_t {
|
||||
u32 *data;
|
||||
i32 x, y;
|
||||
};
|
||||
|
||||
typedef struct glyph_t glyph_t;
|
||||
struct glyph_t {
|
||||
bitmap_t bitmap;
|
||||
i32 xoff, yoff;
|
||||
i32 left_side_bearing, xadvance;
|
||||
};
|
||||
|
||||
typedef struct font_t font_t;
|
||||
struct font_t {
|
||||
glyph_t glyphs[256];
|
||||
bitmap_t atlas;
|
||||
i32 ascent, descent, line_gap, size;
|
||||
f32 scale;
|
||||
i32 height;
|
||||
};
|
||||
|
||||
fn font_t create_font(i32 size) {
|
||||
font_t font = {0};
|
||||
font.size = size;
|
||||
stbtt_fontinfo stb_font;
|
||||
i32 rc = stbtt_InitFont(&stb_font, main_font_data, 0);
|
||||
assert_expr(rc != 0);
|
||||
|
||||
font.scale = stbtt_ScaleForPixelHeight(&stb_font, (f32)font.size);
|
||||
stbtt_GetFontVMetrics(&stb_font, &font.ascent, &font.descent, &font.line_gap);
|
||||
font.height = f32_round((font.ascent - font.descent) + font.line_gap) * font.scale;
|
||||
for (i32 c = 0; c < 256; c += 1) {
|
||||
glyph_t *glyph = font.glyphs + (c - ' ');
|
||||
u8 *temp_data = (u8 *)stbtt_GetCodepointBitmap(&stb_font, 0, font.scale, c, &glyph->bitmap.x, &glyph->bitmap.y, &glyph->xoff, &glyph->yoff);
|
||||
glyph->bitmap.data = ma_push_array(&tcx->temp, u32, glyph->bitmap.x * glyph->bitmap.y);
|
||||
for (i32 y = 0; y < glyph->bitmap.y; y += 1) {
|
||||
for (i32 x = 0; x < glyph->bitmap.x; x += 1) {
|
||||
glyph->bitmap.data[x + y * glyph->bitmap.x] = temp_data[x + y * glyph->bitmap.x] << 24;
|
||||
}
|
||||
}
|
||||
stbtt_GetCodepointHMetrics(&stb_font, c, &glyph->xadvance, &glyph->left_side_bearing);
|
||||
}
|
||||
|
||||
return font;
|
||||
}
|
||||
|
||||
fn font_t create_font_atlas(i32 size) {
|
||||
stbtt_fontinfo stb_font;
|
||||
i32 rc = stbtt_InitFont(&stb_font, main_font_data, 0);
|
||||
assert_expr(rc != 0);
|
||||
|
||||
font_t font = { 0 };
|
||||
font.size = size;
|
||||
font.atlas.x = 512;
|
||||
font.atlas.y = 512;
|
||||
font.atlas.data = ma_push_array(&tcx->temp, u32, font.atlas.x * font.atlas.y);
|
||||
font.scale = stbtt_ScaleForPixelHeight(&stb_font, (f32)font.size);
|
||||
stbtt_GetFontVMetrics(&stb_font, &font.ascent, &font.descent, &font.line_gap);
|
||||
|
||||
i32 xiter = 2;
|
||||
i32 yiter = 2;
|
||||
i32 max_yiter_for_row = 0;
|
||||
for (i32 c = ' '; c < '~'; c += 1) {
|
||||
glyph_t *glyph = font.glyphs + (c - ' ');
|
||||
u8 *temp_data = (u8 *)stbtt_GetCodepointBitmap(&stb_font, 0, font.scale, c, &glyph->bitmap.x, &glyph->bitmap.y, &glyph->xoff, &glyph->yoff);
|
||||
if (xiter + glyph->bitmap.x >= font.atlas.x) {
|
||||
xiter = 0;
|
||||
yiter += max_yiter_for_row + 2;
|
||||
}
|
||||
|
||||
max_yiter_for_row = MAX(max_yiter_for_row, glyph->bitmap.y);
|
||||
for (i32 y = 0; y < glyph->bitmap.y; y += 1) {
|
||||
for (i32 x = 0; x < glyph->bitmap.x; x += 1) {
|
||||
font.atlas.data[(xiter + x) + (yiter + y)*font.atlas.x] = temp_data[x + y * glyph->bitmap.x] << 24;
|
||||
}
|
||||
}
|
||||
xiter += glyph->bitmap.x + 2;
|
||||
}
|
||||
return font;
|
||||
}
|
||||
|
||||
fn_wasm_export void init(void) {
|
||||
os_core_init();
|
||||
}
|
||||
|
||||
fn v2i32_t draw_string(bitmap_t *canvas, font_t *font, int ix, int iy, b32 draw, char *string) {
|
||||
fn void draw_rect(bitmap_t *canvas, bitmap_t *bitmap, i32 x, i32 y) {
|
||||
i32 x0 = x;
|
||||
i32 y0 = y;
|
||||
i32 x1 = x + bitmap->x;
|
||||
i32 y1 = y + bitmap->y;
|
||||
|
||||
i32 cx0 = CLAMP(x0, 0, canvas->x);
|
||||
i32 cy0 = CLAMP(y0, 0, canvas->y);
|
||||
i32 cx1 = CLAMP(x1, 0, canvas->x);
|
||||
i32 cy1 = CLAMP(y1, 0, canvas->y);
|
||||
|
||||
|
||||
if (cx0 >= cx1 || cy0 >= cy1) {
|
||||
return;
|
||||
}
|
||||
|
||||
i32 sx_off = cx0 - x0;
|
||||
i32 sy_off = cy0 - y0;
|
||||
|
||||
i32 sy = sy_off;
|
||||
for (i32 y = cy0; y < cy1; y += 1) {
|
||||
i32 sx = sx_off;
|
||||
for (i32 x = cx0; x < cx1; x += 1) {
|
||||
canvas->data[x + y * canvas->x] = bitmap->data[sx + sy * bitmap->x];
|
||||
sx += 1;
|
||||
}
|
||||
sy += 1;
|
||||
}
|
||||
}
|
||||
|
||||
fn v2i32_t draw_string(bitmap_t *canvas, font_t *font, i32 ix, i32 iy, b32 draw, char *string) {
|
||||
i32 xiter = 0;
|
||||
v2i32_t R = {0};
|
||||
for (char *it = string; *it; it += 1) {
|
||||
@@ -53,60 +144,25 @@ fn v2i32_t draw_string(bitmap_t *canvas, font_t *font, int ix, int iy, b32 draw,
|
||||
|
||||
i32 curr_xiter = xiter;
|
||||
xiter += g->xadvance * font->scale;
|
||||
|
||||
i32 x0 = curr_xiter + g->xoff + ix;
|
||||
i32 y0 = g->yoff + font->ascent*font->scale + iy;
|
||||
i32 x1 = g->width + x0;
|
||||
i32 y1 = g->height + y0;
|
||||
|
||||
i32 cx0 = CLAMP(x0, 0, canvas->x);
|
||||
i32 cy0 = CLAMP(y0, 0, canvas->y);
|
||||
i32 cx1 = CLAMP(x1, 0, canvas->x);
|
||||
i32 cy1 = CLAMP(y1, 0, canvas->y);
|
||||
|
||||
R.x = xiter;
|
||||
R.y = MAX(R.y, g->height);
|
||||
R.y = MAX(R.y, g->bitmap.y);
|
||||
|
||||
if (!draw || (cx0 >= cx1 || cy0 >= cy1)) {
|
||||
if (!draw) {
|
||||
continue;
|
||||
}
|
||||
|
||||
i32 sx_off = cx0 - x0;
|
||||
i32 sy_off = cy0 - y0;
|
||||
|
||||
i32 sy = sy_off;
|
||||
for (i32 y = cy0; y < cy1; y += 1) {
|
||||
i32 sx = sx_off;
|
||||
for (i32 x = cx0; x < cx1; x += 1) {
|
||||
canvas->data[x + y * canvas->x] = g->data[sx + sy * g->width] << 24;
|
||||
sx += 1;
|
||||
}
|
||||
sy += 1;
|
||||
}
|
||||
draw_rect(canvas, &g->bitmap, curr_xiter + g->xoff + ix, g->yoff + font->ascent*font->scale + iy);
|
||||
}
|
||||
return R;
|
||||
}
|
||||
|
||||
fn_wasm_export u32 *update(i32 width, i32 height, f32 dpr) {
|
||||
gb font_t font = {0};
|
||||
font.size = (i32)(130.f * dpr);
|
||||
stbtt_fontinfo stb_font;
|
||||
int rc = stbtt_InitFont(&stb_font, main_font_data, stbtt_GetFontOffsetForIndex(main_font_data,0));
|
||||
assert_expr(rc != 0);
|
||||
|
||||
font.scale = stbtt_ScaleForPixelHeight(&stb_font, (f32)font.size);
|
||||
stbtt_GetFontVMetrics(&stb_font, &font.ascent, &font.descent, &font.line_gap);
|
||||
for (int c = ' '; c < '~'; c += 1) {
|
||||
glyph_t *glyph = font.glyphs + (c - ' ');
|
||||
u8 *temp_data = (u8 *)stbtt_GetCodepointBitmap(&stb_font, 0, font.scale, c, &glyph->width, &glyph->height, &glyph->xoff, &glyph->yoff);
|
||||
glyph->data = ma_push_array_copy(&tcx->perm, temp_data, glyph->width*glyph->height);
|
||||
stbtt_GetCodepointHMetrics(&stb_font, c, &glyph->xadvance, &glyph->left_side_bearing);
|
||||
}
|
||||
|
||||
ma_set0(&tcx->temp);
|
||||
u32 *pixels = ma_push_array(&tcx->temp, u32, width * height);
|
||||
bitmap_t canvas = {pixels, width, height};
|
||||
v2i32_t size = draw_string(&canvas, &font, 200, 500, false, "Hello world (stb)");
|
||||
draw_string(&canvas, &font, (width-size.x)/2, height - size.y*2, true, "Hello world (stb)");
|
||||
font_t font = create_font_atlas(60);
|
||||
draw_rect(&canvas, &font.atlas, 0, 0);
|
||||
//v2i32_t size = draw_string(&canvas, &font, 200, 500, false, "Hello world (stb)");
|
||||
//draw_string(&canvas, &font, (width-size.x)/2, height - size.y*2, true, "Hello world (stb)");
|
||||
return pixels;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user