win32 drawing text

This commit is contained in:
Krzosa Karol
2025-01-08 09:34:53 +01:00
parent 7c282bacb2
commit 189902dae6
17 changed files with 11794 additions and 46 deletions

View File

@@ -61,7 +61,7 @@ int main(int argc, char **argv) {
if (win32_target) { if (win32_target) {
os_delete_file(s8_lit("win32_app.pdb")); os_delete_file(s8_lit("win32_app.pdb"));
ok = os_systemf( ok = os_systemf(
"cl ../src/app/app_win32.c -Fe:win32_app.exe -Fd:win32_app.pdb" "cl ../src/wasm_app/main.c -Fe:win32_app.exe -Fd:win32_app.pdb"
" -I ../src" " -I ../src"
" /Zi /FC /nologo /Oi" " /Zi /FC /nologo /Oi"
" /WX /W3 /wd4200 /diagnostics:column" " /WX /W3 /wd4200 /diagnostics:column"

View File

@@ -1,2 +1,7 @@
#include "app.gen.c" #include "app.gen.c"
#include "app_wasm.c"
#if PLATFORM_WINDOWS
#include "app_win32.c"
#elif PLATFORM_WASM
#include "app_wasm.c"
#endif

View File

@@ -186,6 +186,12 @@ void meta_app(ma_arena_t *arena) {
app_event_t *last; app_event_t *last;
i32 len; i32 len;
}; };
// struct app_frame_t {
// f64 delta;
// f64 time;
// app_event_list_t event_list;
// };
)); ));
sb8_serial_ast_to_code(h, decls); sb8_serial_ast_to_code(h, decls);

View File

@@ -1,15 +1,15 @@
#include "core/core_inc.h"
#include "app.gen.h"
#include "core/core_inc.c"
#include "app.gen.c"
#include "app_win32_opengl.c" #include "app_win32_opengl.c"
#include "glad/glad.h"
#include "glad/glad.c"
#pragma comment(linker, "/subsystem:windows") #pragma comment(linker, "/subsystem:windows")
#pragma comment(lib, "gdi32.lib") #pragma comment(lib, "gdi32.lib")
#pragma comment(lib, "user32.lib") #pragma comment(lib, "user32.lib")
#pragma comment(lib, "winmm.lib") #pragma comment(lib, "winmm.lib")
fn void app_init(void);
fn b32 app_update(app_event_list_t events);
gb b32 w32_good_scheduling; gb b32 w32_good_scheduling;
gb WNDCLASSW w32_wc; gb WNDCLASSW w32_wc;
gb HWND w32_window_handle; gb HWND w32_window_handle;
@@ -179,12 +179,17 @@ fn LRESULT CALLBACK w32_window_proc(HWND wnd, UINT msg, WPARAM wparam, LPARAM lp
return 0; return 0;
} }
app_event_list_t w32_get_events(ma_arena_t *arena, b32 wait) { app_event_list_t w32_get_events(ma_arena_t *arena, b32 wait, b32 *waited) {
w32_event_arena = arena; w32_event_arena = arena;
zero_struct(&w32_event_list); zero_struct(&w32_event_list);
*waited = false;
MSG msg; MSG msg;
if (wait) { if (wait) {
if (PeekMessageW(&msg, NULL, 0, 0, PM_NOREMOVE) == 0) {
*waited = true;
}
GetMessageW(&msg, NULL, 0, 0); GetMessageW(&msg, NULL, 0, 0);
TranslateMessage(&msg); TranslateMessage(&msg);
DispatchMessageW(&msg); DispatchMessageW(&msg);
@@ -331,7 +336,13 @@ int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int n
ShowWindow(w32_window_handle, SW_SHOW); ShowWindow(w32_window_handle, SW_SHOW);
} }
// w32_canvas_t canvas = w32_create_canvas(w32_window_handle); if (!gladLoadGLLoader((GLADloadproc)w32_load_opengl_fn)) {
fatalf("couldn't load opengl!");
}
if (wglSwapIntervalEXT) {
wglSwapIntervalEXT(1); // vsync
}
f64 refresh_rate = 60; f64 refresh_rate = 60;
DEVMODEW devmodew = {0}; DEVMODEW devmodew = {0};
@@ -339,16 +350,23 @@ int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int n
refresh_rate = (f64)devmodew.dmDisplayFrequency; refresh_rate = (f64)devmodew.dmDisplayFrequency;
} }
app_init();
f64 time_frame_start = w32_seconds_now(); f64 time_frame_start = w32_seconds_now();
f64 time_delta = 1.0 / refresh_rate; f64 time_delta = 1.0 / refresh_rate;
f64 time_total = 0.0; f64 time_total = 0.0;
f64 time_update = 0.0; f64 time_update = 0.0;
b32 wait_for_events = false;
u64 consecutive_missed_frames = 0; u64 consecutive_missed_frames = 0;
u64 missed_frames = 0; u64 missed_frames = 0;
u64 frame = 0; u64 frame = 0;
for (;;) { for (;;) {
app_event_list_t event_list = w32_get_events(tcx.temp, false); b32 waited_for_events = false;
app_event_list_t event_list = w32_get_events(tcx.temp, wait_for_events, &waited_for_events);
if (waited_for_events) {
// delta_time = 0.0001
}
for (app_event_t *ev = event_list.first; ev; ev = ev->next) { for (app_event_t *ev = event_list.first; ev; ev = ev->next) {
if (ev->kind == app_event_kind_key_down && ev->key == app_key_escape) { if (ev->kind == app_event_kind_key_down && ev->key == app_key_escape) {
@@ -356,18 +374,15 @@ int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int n
} }
} }
// w32_try_resizing_canvas(&canvas, w32_window_handle); if (event_list.len == 0) {
// debugf("time_update: %f", time_update); w32_push_event((app_event_t){.kind = app_event_kind_update});
event_list = w32_event_list;
#if 0
for (i32 y = 0; y < canvas.window_size.y; y++) {
for (i32 x = 0; x < canvas.window_size.x; x++) {
canvas.memory[x + y * (int)canvas.window_size.x] = 0xFFFF0000;
}
} }
#endif
// w32_present_canvas(&canvas); b32 animating = app_update(event_list);
wait_for_events = !animating;
SwapBuffers(w32_dc);
ma_set0(tcx.temp); ma_set0(tcx.temp);
/////////////////////////////// ///////////////////////////////
@@ -378,7 +393,7 @@ int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int n
if (time_update < time_delta) { if (time_update < time_delta) {
consecutive_missed_frames = 0; consecutive_missed_frames = 0;
// @todo: we are currently busy looping when we don't get the good schduling // @todo: we are currently busy looping when we don't get the good scheduling
// is that actually a good tactic?? // is that actually a good tactic??
if (w32_good_scheduling) { if (w32_good_scheduling) {
f64 time_to_sleep = time_delta - time_update; f64 time_to_sleep = time_delta - time_update;

1819
src/app/glad/glad.c Normal file

File diff suppressed because it is too large Load Diff

3655
src/app/glad/glad.h Normal file

File diff suppressed because it is too large Load Diff

311
src/app/glad/khrplatform.h Normal file
View File

@@ -0,0 +1,311 @@
#ifndef __khrplatform_h_
#define __khrplatform_h_
/*
** Copyright (c) 2008-2018 The Khronos Group Inc.
**
** Permission is hereby granted, free of charge, to any person obtaining a
** copy of this software and/or associated documentation files (the
** "Materials"), to deal in the Materials without restriction, including
** without limitation the rights to use, copy, modify, merge, publish,
** distribute, sublicense, and/or sell copies of the Materials, and to
** permit persons to whom the Materials are furnished to do so, subject to
** the following conditions:
**
** The above copyright notice and this permission notice shall be included
** in all copies or substantial portions of the Materials.
**
** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
*/
/* Khronos platform-specific types and definitions.
*
* The master copy of khrplatform.h is maintained in the Khronos EGL
* Registry repository at https://github.com/KhronosGroup/EGL-Registry
* The last semantic modification to khrplatform.h was at commit ID:
* 67a3e0864c2d75ea5287b9f3d2eb74a745936692
*
* Adopters may modify this file to suit their platform. Adopters are
* encouraged to submit platform specific modifications to the Khronos
* group so that they can be included in future versions of this file.
* Please submit changes by filing pull requests or issues on
* the EGL Registry repository linked above.
*
*
* See the Implementer's Guidelines for information about where this file
* should be located on your system and for more details of its use:
* http://www.khronos.org/registry/implementers_guide.pdf
*
* This file should be included as
* #include <KHR/khrplatform.h>
* by Khronos client API header files that use its types and defines.
*
* The types in khrplatform.h should only be used to define API-specific types.
*
* Types defined in khrplatform.h:
* khronos_int8_t signed 8 bit
* khronos_uint8_t unsigned 8 bit
* khronos_int16_t signed 16 bit
* khronos_uint16_t unsigned 16 bit
* khronos_int32_t signed 32 bit
* khronos_uint32_t unsigned 32 bit
* khronos_int64_t signed 64 bit
* khronos_uint64_t unsigned 64 bit
* khronos_intptr_t signed same number of bits as a pointer
* khronos_uintptr_t unsigned same number of bits as a pointer
* khronos_ssize_t signed size
* khronos_usize_t unsigned size
* khronos_float_t signed 32 bit floating point
* khronos_time_ns_t unsigned 64 bit time in nanoseconds
* khronos_utime_nanoseconds_t unsigned time interval or absolute time in
* nanoseconds
* khronos_stime_nanoseconds_t signed time interval in nanoseconds
* khronos_boolean_enum_t enumerated boolean type. This should
* only be used as a base type when a client API's boolean type is
* an enum. Client APIs which use an integer or other type for
* booleans cannot use this as the base type for their boolean.
*
* Tokens defined in khrplatform.h:
*
* KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values.
*
* KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0.
* KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0.
*
* Calling convention macros defined in this file:
* KHRONOS_APICALL
* KHRONOS_APIENTRY
* KHRONOS_APIATTRIBUTES
*
* These may be used in function prototypes as:
*
* KHRONOS_APICALL void KHRONOS_APIENTRY funcname(
* int arg1,
* int arg2) KHRONOS_APIATTRIBUTES;
*/
#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC)
# define KHRONOS_STATIC 1
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APICALL
*-------------------------------------------------------------------------
* This precedes the return type of the function in the function prototype.
*/
#if defined(KHRONOS_STATIC)
/* If the preprocessor constant KHRONOS_STATIC is defined, make the
* header compatible with static linking. */
# define KHRONOS_APICALL
#elif defined(_WIN32)
# define KHRONOS_APICALL __declspec(dllimport)
#elif defined (__SYMBIAN32__)
# define KHRONOS_APICALL IMPORT_C
#elif defined(__ANDROID__)
# define KHRONOS_APICALL __attribute__((visibility("default")))
#else
# define KHRONOS_APICALL
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APIENTRY
*-------------------------------------------------------------------------
* This follows the return type of the function and precedes the function
* name in the function prototype.
*/
#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__)
/* Win32 but not WinCE */
# define KHRONOS_APIENTRY __stdcall
#else
# define KHRONOS_APIENTRY
#endif
/*-------------------------------------------------------------------------
* Definition of KHRONOS_APIATTRIBUTES
*-------------------------------------------------------------------------
* This follows the closing parenthesis of the function prototype arguments.
*/
#if defined (__ARMCC_2__)
#define KHRONOS_APIATTRIBUTES __softfp
#else
#define KHRONOS_APIATTRIBUTES
#endif
/*-------------------------------------------------------------------------
* basic type definitions
*-----------------------------------------------------------------------*/
#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__)
/*
* Using <stdint.h>
*/
#include <stdint.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
/*
* To support platform where unsigned long cannot be used interchangeably with
* inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t.
* Ideally, we could just use (u)intptr_t everywhere, but this could result in
* ABI breakage if khronos_uintptr_t is changed from unsigned long to
* unsigned long long or similar (this results in different C++ name mangling).
* To avoid changes for existing platforms, we restrict usage of intptr_t to
* platforms where the size of a pointer is larger than the size of long.
*/
#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__)
#if __SIZEOF_POINTER__ > __SIZEOF_LONG__
#define KHRONOS_USE_INTPTR_T
#endif
#endif
#elif defined(__VMS ) || defined(__sgi)
/*
* Using <inttypes.h>
*/
#include <inttypes.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(_WIN32) && !defined(__SCITECH_SNAP__)
/*
* Win32
*/
typedef __int32 khronos_int32_t;
typedef unsigned __int32 khronos_uint32_t;
typedef __int64 khronos_int64_t;
typedef unsigned __int64 khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif defined(__sun__) || defined(__digital__)
/*
* Sun or Digital
*/
typedef int khronos_int32_t;
typedef unsigned int khronos_uint32_t;
#if defined(__arch64__) || defined(_LP64)
typedef long int khronos_int64_t;
typedef unsigned long int khronos_uint64_t;
#else
typedef long long int khronos_int64_t;
typedef unsigned long long int khronos_uint64_t;
#endif /* __arch64__ */
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#elif 0
/*
* Hypothetical platform with no float or int64 support
*/
typedef int khronos_int32_t;
typedef unsigned int khronos_uint32_t;
#define KHRONOS_SUPPORT_INT64 0
#define KHRONOS_SUPPORT_FLOAT 0
#else
/*
* Generic fallback
*/
#include <stdint.h>
typedef int32_t khronos_int32_t;
typedef uint32_t khronos_uint32_t;
typedef int64_t khronos_int64_t;
typedef uint64_t khronos_uint64_t;
#define KHRONOS_SUPPORT_INT64 1
#define KHRONOS_SUPPORT_FLOAT 1
#endif
/*
* Types that are (so far) the same on all platforms
*/
typedef signed char khronos_int8_t;
typedef unsigned char khronos_uint8_t;
typedef signed short int khronos_int16_t;
typedef unsigned short int khronos_uint16_t;
/*
* Types that differ between LLP64 and LP64 architectures - in LLP64,
* pointers are 64 bits, but 'long' is still 32 bits. Win64 appears
* to be the only LLP64 architecture in current use.
*/
#ifdef KHRONOS_USE_INTPTR_T
typedef intptr_t khronos_intptr_t;
typedef uintptr_t khronos_uintptr_t;
#elif defined(_WIN64)
typedef signed long long int khronos_intptr_t;
typedef unsigned long long int khronos_uintptr_t;
#else
typedef signed long int khronos_intptr_t;
typedef unsigned long int khronos_uintptr_t;
#endif
#if defined(_WIN64)
typedef signed long long int khronos_ssize_t;
typedef unsigned long long int khronos_usize_t;
#else
typedef signed long int khronos_ssize_t;
typedef unsigned long int khronos_usize_t;
#endif
#if KHRONOS_SUPPORT_FLOAT
/*
* Float type
*/
typedef float khronos_float_t;
#endif
#if KHRONOS_SUPPORT_INT64
/* Time types
*
* These types can be used to represent a time interval in nanoseconds or
* an absolute Unadjusted System Time. Unadjusted System Time is the number
* of nanoseconds since some arbitrary system event (e.g. since the last
* time the system booted). The Unadjusted System Time is an unsigned
* 64 bit value that wraps back to 0 every 584 years. Time intervals
* may be either signed or unsigned.
*/
typedef khronos_uint64_t khronos_utime_nanoseconds_t;
typedef khronos_int64_t khronos_stime_nanoseconds_t;
#endif
/*
* Dummy value used to pad enum types to 32 bits.
*/
#ifndef KHRONOS_MAX_ENUM
#define KHRONOS_MAX_ENUM 0x7FFFFFFF
#endif
/*
* Enumerated boolean type
*
* Values other than zero should be considered to be true. Therefore
* comparisons should not be made against KHRONOS_TRUE.
*/
typedef enum {
KHRONOS_FALSE = 0,
KHRONOS_TRUE = 1,
KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM
} khronos_boolean_enum_t;
#endif /* __khrplatform_h_ */

View File

@@ -183,6 +183,7 @@ fn void utf8_advance(utf8_iter_t *iter) {
iter->item = r.out_str; iter->item = r.out_str;
} }
// @todo: s8_t, change name
fn utf8_iter_t utf8_iterate_ex(char *str, int len) { fn utf8_iter_t utf8_iterate_ex(char *str, int len) {
utf8_iter_t result = {str, len}; utf8_iter_t result = {str, len};
if (len) utf8_advance(&result); if (len) utf8_advance(&result);

146
src/draw/gfx2d.c Normal file
View File

@@ -0,0 +1,146 @@
fn_wasm_import void wasm_clear(void);
fn_wasm_import void wasm_draw_text(isize str, i32 len, f64 x, f64 y, isize font_str, i32 font_len, i32 font_size, f32 r, f32 g, f32 b, f32 a);
fn_wasm_import void wasm_draw_rect(f64 x, f64 y, f64 w, f64 h, f32 r, f32 g, f32 b, f32 a);
fn_wasm_import f64 wasm_measure_text(isize str, i32 len, isize font_str, i32 font_len, i32 font_size);
fn_wasm_import f64 wasm_get_font_height(isize font_str, i32 font_len, i32 font_size);
fn_wasm_import void wasm_set_clip(f64 x, f64 y, f64 w, f64 h);
// gb_read_only s8_t font_face = s8_const_lit("open_sans_regular");
gb_read_only s8_t font_face = s8_const_lit("consolas");
fn void set_clip(r2f64_t rect) {
wasm_set_clip(wasm_dpr * rect.min.x, wasm_dpr * rect.min.y, wasm_dpr * (rect.max.x - rect.min.x), wasm_dpr * (rect.max.y - rect.min.y));
}
fn f64 get_font_height(void) {
return wasm_get_font_height((isize) font_face.str, font_face.len, 20*wasm_dpr) / wasm_dpr;
}
fn f64 measure_text_ex(s8_t string) {
return wasm_measure_text((isize)string.str, string.len, (isize) font_face.str, font_face.len, 20*wasm_dpr) / wasm_dpr;
}
fn f64 measure_text(char *str) {
return measure_text_ex(s8_from_char(str));
}
fn void draw_text(v2f64_t pos, v4f32_t color, s8_t string) {
wasm_draw_text((isize)string.str, string.len, wasm_dpr * pos.x, wasm_dpr * pos.y, (isize) font_face.str, font_face.len, 20*wasm_dpr, color.r * 255.f, color.g * 255.f, color.b * 255.f, color.a);
}
fn void draw_textf(v2f64_t pos, char *str, ...) {
char buff[1024];
va_list args;
va_start(args, str);
i32 len = stbsp_vsnprintf(buff, sizeof(buff), str, args);
va_end(args);
wasm_draw_text((isize)buff, len, wasm_dpr * pos.x, wasm_dpr * pos.y, (isize) font_face.str, font_face.len, 20*wasm_dpr, 0, 0, 0, 1);
}
fn void draw_rect(r2f64_t rect, v4f32_t color) {
wasm_draw_rect(wasm_dpr * rect.min.x, wasm_dpr * rect.min.y, wasm_dpr * (rect.max.x - rect.min.x), wasm_dpr * (rect.max.y - rect.min.y), color.r * 255.f, color.g * 255.f, color.b * 255.f, color.a);
}
typedef enum {
gfx_kind_null,
gfx_kind_clear,
gfx_kind_draw_rect,
gfx_kind_draw_text,
gfx_kind_set_clip,
} gfx_kind_t;
typedef struct gfx_cmd_t gfx_cmd_t;
struct gfx_cmd_t {
gfx_cmd_t *next;
gfx_kind_t kind;
r2f64_t rect;
v4f32_t color;
s8_t text;
};
typedef struct gfx_t gfx_t;
struct gfx_t {
gfx_cmd_t *first;
gfx_cmd_t *last;
app_event_t *ev;
};
// @todo:
typedef struct gfx_group_t gfx_group_t;
struct gfx_group_t {
gfx_cmd_t *first;
gfx_cmd_t *last;
};
void gfx_begin(gfx_t *gfx, app_event_t *ev) {
gfx->ev = ev;
}
void gfx_end(gfx_t *gfx) {
app_event_t *ev = gfx->ev;
r2f64_t window = (r2f64_t){0, 0, ev->window_size.x, ev->window_size.y};
for (gfx_cmd_t *cmd = gfx->first; cmd; cmd = cmd->next) {
if (cmd->kind == gfx_kind_clear) {
wasm_clear();
draw_rect(window, cmd->color);
} else if (cmd->kind == gfx_kind_draw_rect) {
draw_rect(cmd->rect, cmd->color);
} else if (cmd->kind == gfx_kind_draw_text) {
draw_text(cmd->rect.min, cmd->color, cmd->text);
} else if (cmd->kind == gfx_kind_set_clip) {
set_clip(cmd->rect);
} else {
invalid_codepath;
}
}
gfx->first = gfx->last = NULL;
}
void gfx_add_cmd(gfx_t *gfx, gfx_cmd_t cmd) {
gfx_cmd_t *c = ma_push_type(tcx.temp, gfx_cmd_t);
*c = cmd;
SLLQ_APPEND(gfx->first, gfx->last, c);
}
void gfx_clear(gfx_t *gfx, v4f32_t color) {
gfx_add_cmd(gfx, (gfx_cmd_t){
.kind = gfx_kind_clear,
.color = color,
});
}
void gfx_rect(gfx_t *gfx, r2f64_t rect, v4f32_t color) {
gfx_add_cmd(gfx, (gfx_cmd_t){
.kind = gfx_kind_draw_rect,
.color = color,
.rect = rect,
});
}
void gfx_set_clip(gfx_t *gfx, r2f64_t rect) {
gfx_add_cmd(gfx, (gfx_cmd_t){
.kind = gfx_kind_set_clip,
.rect = rect,
});
}
void gfx_text(gfx_t *gfx, v2f64_t pos, v4f32_t color, s8_t string) {
gfx_add_cmd(gfx, (gfx_cmd_t){
.kind = gfx_kind_draw_text,
.text = s8_copy(tcx.temp, string),
.color = color,
.rect.min = pos,
});
}
void gfx_textf(gfx_t *gfx, v2f64_t pos, v4f32_t color, char *str, ...) {
S8_FMT(tcx.temp, str, text);
gfx_add_cmd(gfx, (gfx_cmd_t){
.kind = gfx_kind_draw_text,
.text = text,
.color = color,
.rect.min = pos,
});
}

View File

@@ -30,4 +30,5 @@ fn f64 os_get_milliseconds(void) {
f64 secs = os_seconds_now(); f64 secs = os_seconds_now();
f64 result = secs * 1000; f64 result = secs * 1000;
return result; return result;
} }

247
src/render/backup_font.c Normal file
View File

@@ -0,0 +1,247 @@
static unsigned int Decode85Byte(char c) { return c >= '\\' ? c - 36 : c - 35; }
static void Decode85(const unsigned char *src, unsigned char *dst) {
while (*src) {
unsigned int tmp = Decode85Byte(src[0]) + 85 * (Decode85Byte(src[1]) + 85 * (Decode85Byte(src[2]) + 85 * (Decode85Byte(src[3]) + 85 * Decode85Byte(src[4]))));
dst[0] = ((tmp >> 0) & 0xFF);
dst[1] = ((tmp >> 8) & 0xFF);
dst[2] = ((tmp >> 16) & 0xFF);
dst[3] = ((tmp >> 24) & 0xFF); // We can't assume little-endianness.
src += 5;
dst += 4;
}
}
//-----------------------------------------------------------------------------
// Compressed with stb_compress() then converted to a C array and encoded as base85.
// Use the program in misc/fonts/binary_to_compressed_c.cpp to create the array from a TTF file.
// The purpose of encoding as base85 instead of "0x00,0x01,..." style is only save on _source code_ size.
// Decompression from stb.h (public domain) by Sean Barrett https://github.com/nothings/stb/blob/master/stb.h
//-----------------------------------------------------------------------------
static unsigned int stb_decompress_length(const unsigned char *input) {
return (input[8] << 24) + (input[9] << 16) + (input[10] << 8) + input[11];
}
static unsigned char *stb__barrier_out_e, *stb__barrier_out_b;
static const unsigned char *stb__barrier_in_b;
static unsigned char *stb__dout;
static void stb__match(const unsigned char *data, unsigned int length) {
// INVERSE of memmove... write each byte before copying the next...
assert(stb__dout + length <= stb__barrier_out_e);
if (stb__dout + length > stb__barrier_out_e) {
stb__dout += length;
return;
}
if (data < stb__barrier_out_b) {
stb__dout = stb__barrier_out_e + 1;
return;
}
while (length--) *stb__dout++ = *data++;
}
static void stb__lit(const unsigned char *data, unsigned int length) {
assert(stb__dout + length <= stb__barrier_out_e);
if (stb__dout + length > stb__barrier_out_e) {
stb__dout += length;
return;
}
if (data < stb__barrier_in_b) {
stb__dout = stb__barrier_out_e + 1;
return;
}
memcpy(stb__dout, data, length);
stb__dout += length;
}
#define stb__in2(x) ((i[x] << 8) + i[(x) + 1])
#define stb__in3(x) ((i[x] << 16) + stb__in2((x) + 1))
#define stb__in4(x) ((i[x] << 24) + stb__in3((x) + 1))
static const unsigned char *stb_decompress_token(const unsigned char *i) {
if (*i >= 0x20) { // use fewer if's for cases that expand small
if (*i >= 0x80) stb__match(stb__dout - i[1] - 1, i[0] - 0x80 + 1), i += 2;
else if (*i >= 0x40) stb__match(stb__dout - (stb__in2(0) - 0x4000 + 1), i[2] + 1), i += 3;
else /* *i >= 0x20 */ stb__lit(i + 1, i[0] - 0x20 + 1), i += 1 + (i[0] - 0x20 + 1);
} else { // more ifs for cases that expand large, since overhead is amortized
if (*i >= 0x18) stb__match(stb__dout - (stb__in3(0) - 0x180000 + 1), i[3] + 1), i += 4;
else if (*i >= 0x10) stb__match(stb__dout - (stb__in3(0) - 0x100000 + 1), stb__in2(3) + 1), i += 5;
else if (*i >= 0x08) stb__lit(i + 2, stb__in2(0) - 0x0800 + 1), i += 2 + (stb__in2(0) - 0x0800 + 1);
else if (*i == 0x07) stb__lit(i + 3, stb__in2(1) + 1), i += 3 + (stb__in2(1) + 1);
else if (*i == 0x06) stb__match(stb__dout - (stb__in3(1) + 1), i[4] + 1), i += 5;
else if (*i == 0x04) stb__match(stb__dout - (stb__in3(1) + 1), stb__in2(4) + 1), i += 6;
}
return i;
}
static unsigned int stb_adler32(unsigned int adler32, unsigned char *buffer, unsigned int buflen) {
const unsigned long ADLER_MOD = 65521;
unsigned long s1 = adler32 & 0xffff, s2 = adler32 >> 16;
unsigned long blocklen = buflen % 5552;
unsigned long i;
while (buflen) {
for (i = 0; i + 7 < blocklen; i += 8) {
s1 += buffer[0], s2 += s1;
s1 += buffer[1], s2 += s1;
s1 += buffer[2], s2 += s1;
s1 += buffer[3], s2 += s1;
s1 += buffer[4], s2 += s1;
s1 += buffer[5], s2 += s1;
s1 += buffer[6], s2 += s1;
s1 += buffer[7], s2 += s1;
buffer += 8;
}
for (; i < blocklen; ++i)
s1 += *buffer++, s2 += s1;
s1 %= ADLER_MOD, s2 %= ADLER_MOD;
buflen -= blocklen;
blocklen = 5552;
}
return (unsigned int)(s2 << 16) + (unsigned int)s1;
}
static unsigned int stb_decompress(unsigned char *output, const unsigned char *i, unsigned int length) {
if (stb__in4(0) != 0x57bC0000) return 0;
if (stb__in4(4) != 0) return 0; // error! stream is > 4GB
const unsigned int olen = stb_decompress_length(i);
stb__barrier_in_b = i;
stb__barrier_out_e = output + olen;
stb__barrier_out_b = output;
i += 16;
stb__dout = output;
for (;;) {
const unsigned char *old_i = i;
i = stb_decompress_token(i);
if (i == old_i) {
if (*i == 0x05 && i[1] == 0xfa) {
assert(stb__dout == output + olen);
if (stb__dout != output + olen) return 0;
if (stb_adler32(1, output, olen) != (unsigned int)stb__in4(2))
return 0;
return olen;
} else {
assert(0); /* NOTREACHED */
return 0;
}
}
assert(stb__dout <= output + olen);
if (stb__dout > output + olen)
return 0;
}
}
// ProggyClean.ttf
// Copyright (c) 2004, 2005 Tristan Grimmer
// MIT license (see License.txt in http://www.proggyfonts.net/index.php?menu=download)
// Download and more information at http://www.proggyfonts.net or http://upperboundsinteractive.com/fonts.php
//-----------------------------------------------------------------------------
// File: 'ProggyClean.ttf' (41208 bytes)
// Exported using misc/fonts/binary_to_compressed_c.cpp (with compression + base85 string encoding).
// The purpose of encoding as base85 instead of "0x00,0x01,..." style is only save on _source code_ size.
//-----------------------------------------------------------------------------
static const char proggy_clean_ttf_compressed_data_base85[11980 + 1] =
"7])#######hV0qs'/###[),##/l:$#Q6>##5[n42>c-TH`->>#/e>11NNV=Bv(*:.F?uu#(gRU.o0XGH`$vhLG1hxt9?W`#,5LsCp#-i>.r$<$6pD>Lb';9Crc6tgXmKVeU2cD4Eo3R/"
"2*>]b(MC;$jPfY.;h^`IWM9<Lh2TlS+f-s$o6Q<BWH`YiU.xfLq$N;$0iR/GX:U(jcW2p/W*q?-qmnUCI;jHSAiFWM.R*kU@C=GH?a9wp8f$e.-4^Qg1)Q-GL(lf(r/7GrRgwV%MS=C#"
"`8ND>Qo#t'X#(v#Y9w0#1D$CIf;W'#pWUPXOuxXuU(H9M(1<q-UE31#^-V'8IRUo7Qf./L>=Ke$$'5F%)]0^#0X@U.a<r:QLtFsLcL6##lOj)#.Y5<-R&KgLwqJfLgN&;Q?gI^#DY2uL"
"i@^rMl9t=cWq6##weg>$FBjVQTSDgEKnIS7EM9>ZY9w0#L;>>#Mx&4Mvt//L[MkA#W@lK.N'[0#7RL_&#w+F%HtG9M#XL`N&.,GM4Pg;-<nLENhvx>-VsM.M0rJfLH2eTM`*oJMHRC`N"
"kfimM2J,W-jXS:)r0wK#@Fge$U>`w'N7G#$#fB#$E^$#:9:hk+eOe--6x)F7*E%?76%^GMHePW-Z5l'&GiF#$956:rS?dA#fiK:)Yr+`&#0j@'DbG&#^$PG.Ll+DNa<XCMKEV*N)LN/N"
"*b=%Q6pia-Xg8I$<MR&,VdJe$<(7G;Ckl'&hF;;$<_=X(b.RS%%)###MPBuuE1V:v&cX&#2m#(&cV]`k9OhLMbn%s$G2,B$BfD3X*sp5#l,$R#]x_X1xKX%b5U*[r5iMfUo9U`N99hG)"
"tm+/Us9pG)XPu`<0s-)WTt(gCRxIg(%6sfh=ktMKn3j)<6<b5Sk_/0(^]AaN#(p/L>&VZ>1i%h1S9u5o@YaaW$e+b<TWFn/Z:Oh(Cx2$lNEoN^e)#CFY@@I;BOQ*sRwZtZxRcU7uW6CX"
"ow0i(?$Q[cjOd[P4d)]>ROPOpxTO7Stwi1::iB1q)C_=dV26J;2,]7op$]uQr@_V7$q^%lQwtuHY]=DX,n3L#0PHDO4f9>dC@O>HBuKPpP*E,N+b3L#lpR/MrTEH.IAQk.a>D[.e;mc."
"x]Ip.PH^'/aqUO/$1WxLoW0[iLA<QT;5HKD+@qQ'NQ(3_PLhE48R.qAPSwQ0/WK?Z,[x?-J;jQTWA0X@KJ(_Y8N-:/M74:/-ZpKrUss?d#dZq]DAbkU*JqkL+nwX@@47`5>w=4h(9.`G"
"CRUxHPeR`5Mjol(dUWxZa(>STrPkrJiWx`5U7F#.g*jrohGg`cg:lSTvEY/EV_7H4Q9[Z%cnv;JQYZ5q.l7Zeas:HOIZOB?G<Nald$qs]@]L<J7bR*>gv:[7MI2k).'2($5FNP&EQ(,)"
"U]W]+fh18.vsai00);D3@4ku5P?DP8aJt+;qUM]=+b'8@;mViBKx0DE[-auGl8:PJ&Dj+M6OC]O^((##]`0i)drT;-7X`=-H3[igUnPG-NZlo.#k@h#=Ork$m>a>$-?Tm$UV(?#P6YY#"
"'/###xe7q.73rI3*pP/$1>s9)W,JrM7SN]'/4C#v$U`0#V.[0>xQsH$fEmPMgY2u7Kh(G%siIfLSoS+MK2eTM$=5,M8p`A.;_R%#u[K#$x4AG8.kK/HSB==-'Ie/QTtG?-.*^N-4B/ZM"
"_3YlQC7(p7q)&](`6_c)$/*JL(L-^(]$wIM`dPtOdGA,U3:w2M-0<q-]L_?^)1vw'.,MRsqVr.L;aN&#/EgJ)PBc[-f>+WomX2u7lqM2iEumMTcsF?-aT=Z-97UEnXglEn1K-bnEO`gu"
"Ft(c%=;Am_Qs@jLooI&NX;]0#j4#F14;gl8-GQpgwhrq8'=l_f-b49'UOqkLu7-##oDY2L(te+Mch&gLYtJ,MEtJfLh'x'M=$CS-ZZ%P]8bZ>#S?YY#%Q&q'3^Fw&?D)UDNrocM3A76/"
"/oL?#h7gl85[qW/NDOk%16ij;+:1a'iNIdb-ou8.P*w,v5#EI$TWS>Pot-R*H'-SEpA:g)f+O$%%`kA#G=8RMmG1&O`>to8bC]T&$,n.LoO>29sp3dt-52U%VM#q7'DHpg+#Z9%H[K<L"
"%a2E-grWVM3@2=-k22tL]4$##6We'8UJCKE[d_=%wI;'6X-GsLX4j^SgJ$##R*w,vP3wK#iiW&#*h^D&R?jp7+/u&#(AP##XU8c$fSYW-J95_-Dp[g9wcO&#M-h1OcJlc-*vpw0xUX&#"
"OQFKNX@QI'IoPp7nb,QU//MQ&ZDkKP)X<WSVL(68uVl&#c'[0#(s1X&xm$Y%B7*K:eDA323j998GXbA#pwMs-jgD$9QISB-A_(aN4xoFM^@C58D0+Q+q3n0#3U1InDjF682-SjMXJK)("
"h$hxua_K]ul92%'BOU&#BRRh-slg8KDlr:%L71Ka:.A;%YULjDPmL<LYs8i#XwJOYaKPKc1h:'9Ke,g)b),78=I39B;xiY$bgGw-&.Zi9InXDuYa%G*f2Bq7mn9^#p1vv%#(Wi-;/Z5h"
"o;#2:;%d&#x9v68C5g?ntX0X)pT`;%pB3q7mgGN)3%(P8nTd5L7GeA-GL@+%J3u2:(Yf>et`e;)f#Km8&+DC$I46>#Kr]]u-[=99tts1.qb#q72g1WJO81q+eN'03'eM>&1XxY-caEnO"
"j%2n8)),?ILR5^.Ibn<-X-Mq7[a82Lq:F&#ce+S9wsCK*x`569E8ew'He]h:sI[2LM$[guka3ZRd6:t%IG:;$%YiJ:Nq=?eAw;/:nnDq0(CYcMpG)qLN4$##&J<j$UpK<Q4a1]MupW^-"
"sj_$%[HK%'F####QRZJ::Y3EGl4'@%FkiAOg#p[##O`gukTfBHagL<LHw%q&OV0##F=6/:chIm0@eCP8X]:kFI%hl8hgO@RcBhS-@Qb$%+m=hPDLg*%K8ln(wcf3/'DW-$.lR?n[nCH-"
"eXOONTJlh:.RYF%3'p6sq:UIMA945&^HFS87@$EP2iG<-lCO$%c`uKGD3rC$x0BL8aFn--`ke%#HMP'vh1/R&O_J9'um,.<tx[@%wsJk&bUT2`0uMv7gg#qp/ij.L56'hl;.s5CUrxjO"
"M7-##.l+Au'A&O:-T72L]P`&=;ctp'XScX*rU.>-XTt,%OVU4)S1+R-#dg0/Nn?Ku1^0f$B*P:Rowwm-`0PKjYDDM'3]d39VZHEl4,.j']Pk-M.h^&:0FACm$maq-&sgw0t7/6(^xtk%"
"LuH88Fj-ekm>GA#_>568x6(OFRl-IZp`&b,_P'$M<Jnq79VsJW/mWS*PUiq76;]/NM_>hLbxfc$mj`,O;&%W2m`Zh:/)Uetw:aJ%]K9h:TcF]u_-Sj9,VK3M.*'&0D[Ca]J9gp8,kAW]"
"%(?A%R$f<->Zts'^kn=-^@c4%-pY6qI%J%1IGxfLU9CP8cbPlXv);C=b),<2mOvP8up,UVf3839acAWAW-W?#ao/^#%KYo8fRULNd2.>%m]UK:n%r$'sw]J;5pAoO_#2mO3n,'=H5(et"
"Hg*`+RLgv>=4U8guD$I%D:W>-r5V*%j*W:Kvej.Lp$<M-SGZ':+Q_k+uvOSLiEo(<aD/K<CCc`'Lx>'?;++O'>()jLR-^u68PHm8ZFWe+ej8h:9r6L*0//c&iH&R8pRbA#Kjm%upV1g:"
"a_#Ur7FuA#(tRh#.Y5K+@?3<-8m0$PEn;J:rh6?I6uG<-`wMU'ircp0LaE_OtlMb&1#6T.#FDKu#1Lw%u%+GM+X'e?YLfjM[VO0MbuFp7;>Q&#WIo)0@F%q7c#4XAXN-U&VB<HFF*qL("
"$/V,;(kXZejWO`<[5?\?ewY(*9=%wDc;,u<'9t3W-(H1th3+G]ucQ]kLs7df($/*JL]@*t7Bu_G3_7mp7<iaQjO@.kLg;x3B0lqp7Hf,^Ze7-##@/c58Mo(3;knp0%)A7?-W+eI'o8)b<"
"nKnw'Ho8C=Y>pqB>0ie&jhZ[?iLR@@_AvA-iQC(=ksRZRVp7`.=+NpBC%rh&3]R:8XDmE5^V8O(x<<aG/1N$#FX$0V5Y6x'aErI3I$7x%E`v<-BY,)%-?Psf*l?%C3.mM(=/M0:JxG'?"
"7WhH%o'a<-80g0NBxoO(GH<dM]n.+%q@jH?f.UsJ2Ggs&4<-e47&Kl+f//9@`b+?.TeN_&B8Ss?v;^Trk;f#YvJkl&w$]>-+k?'(<S:68tq*WoDfZu';mM?8X[ma8W%*`-=;D.(nc7/;"
")g:T1=^J$&BRV(-lTmNB6xqB[@0*o.erM*<SWF]u2=st-*(6v>^](H.aREZSi,#1:[IXaZFOm<-ui#qUq2$##Ri;u75OK#(RtaW-K-F`S+cF]uN`-KMQ%rP/Xri.LRcB##=YL3BgM/3M"
"D?@f&1'BW-)Ju<L25gl8uhVm1hL$##*8###'A3/LkKW+(^rWX?5W_8g)a(m&K8P>#bmmWCMkk&#TR`C,5d>g)F;t,4:@_l8G/5h4vUd%&%950:VXD'QdWoY-F$BtUwmfe$YqL'8(PWX("
"P?^@Po3$##`MSs?DWBZ/S>+4%>fX,VWv/w'KD`LP5IbH;rTV>n3cEK8U#bX]l-/V+^lj3;vlMb&[5YQ8#pekX9JP3XUC72L,,?+Ni&co7ApnO*5NK,((W-i:$,kp'UDAO(G0Sq7MVjJs"
"bIu)'Z,*[>br5fX^:FPAWr-m2KgL<LUN098kTF&#lvo58=/vjDo;.;)Ka*hLR#/k=rKbxuV`>Q_nN6'8uTG&#1T5g)uLv:873UpTLgH+#FgpH'_o1780Ph8KmxQJ8#H72L4@768@Tm&Q"
"h4CB/5OvmA&,Q&QbUoi$a_%3M01H)4x7I^&KQVgtFnV+;[Pc>[m4k//,]1?#`VY[Jr*3&&slRfLiVZJ:]?=K3Sw=[$=uRB?3xk48@aeg<Z'<$#4H)6,>e0jT6'N#(q%.O=?2S]u*(m<-"
"V8J'(1)G][68hW$5'q[GC&5j`TE?m'esFGNRM)j,ffZ?-qx8;->g4t*:CIP/[Qap7/9'#(1sao7w-.qNUdkJ)tCF&#B^;xGvn2r9FEPFFFcL@.iFNkTve$m%#QvQS8U@)2Z+3K:AKM5i"
"sZ88+dKQ)W6>J%CL<KE>`.d*(B`-n8D9oK<Up]c$X$(,)M8Zt7/[rdkqTgl-0cuGMv'?>-XV1q['-5k'cAZ69e;D_?$ZPP&s^+7])$*$#@QYi9,5P&#9r+$%CE=68>K8r0=dSC%%(@p7"
".m7jilQ02'0-VWAg<a/''3u.=4L$Y)6k/K:_[3=&jvL<L0C/2'v:^;-DIBW,B4E68:kZ;%?8(Q8BH=kO65BW?xSG&#@uU,DS*,?.+(o(#1vCS8#CHF>TlGW'b)Tq7VT9q^*^$$.:&N@@"
"$&)WHtPm*5_rO0&e%K&#-30j(E4#'Zb.o/(Tpm$>K'f@[PvFl,hfINTNU6u'0pao7%XUp9]5.>%h`8_=VYbxuel.NTSsJfLacFu3B'lQSu/m6-Oqem8T+oE--$0a/k]uj9EwsG>%veR*"
"hv^BFpQj:K'#SJ,sB-'#](j.Lg92rTw-*n%@/;39rrJF,l#qV%OrtBeC6/,;qB3ebNW[?,Hqj2L.1NP&GjUR=1D8QaS3Up&@*9wP?+lo7b?@%'k4`p0Z$22%K3+iCZj?XJN4Nm&+YF]u"
"@-W$U%VEQ/,,>>#)D<h#`)h0:<Q6909ua+&VU%n2:cG3FJ-%@Bj-DgLr`Hw&HAKjKjseK</xKT*)B,N9X3]krc12t'pgTV(Lv-tL[xg_%=M_q7a^x?7Ubd>#%8cY#YZ?=,`Wdxu/ae&#"
"w6)R89tI#6@s'(6Bf7a&?S=^ZI_kS&ai`&=tE72L_D,;^R)7[$s<Eh#c&)q.MXI%#v9ROa5FZO%sF7q7Nwb&#ptUJ:aqJe$Sl68%.D###EC><?-aF&#RNQv>o8lKN%5/$(vdfq7+ebA#"
"u1p]ovUKW&Y%q]'>$1@-[xfn$7ZTp7mM,G,Ko7a&Gu%G[RMxJs[0MM%wci.LFDK)(<c`Q8N)jEIF*+?P2a8g%)$q]o2aH8C&<SibC/q,(e:v;-b#6[$NtDZ84Je2KNvB#$P5?tQ3nt(0"
"d=j.LQf./Ll33+(;q3L-w=8dX$#WF&uIJ@-bfI>%:_i2B5CsR8&9Z&#=mPEnm0f`<&c)QL5uJ#%u%lJj+D-r;BoF&#4DoS97h5g)E#o:&S4weDF,9^Hoe`h*L+_a*NrLW-1pG_&2UdB8"
"6e%B/:=>)N4xeW.*wft-;$'58-ESqr<b?UI(_%@[P46>#U`'6AQ]m&6/`Z>#S?YY#Vc;r7U2&326d=w&H####?TZ`*4?&.MK?LP8Vxg>$[QXc%QJv92.(Db*B)gb*BM9dM*hJMAo*c&#"
"b0v=Pjer]$gG&JXDf->'StvU7505l9$AFvgYRI^&<^b68?j#q9QX4SM'RO#&sL1IM.rJfLUAj221]d##DW=m83u5;'bYx,*Sl0hL(W;;$doB&O/TQ:(Z^xBdLjL<Lni;''X.`$#8+1GD"
":k$YUWsbn8ogh6rxZ2Z9]%nd+>V#*8U_72Lh+2Q8Cj0i:6hp&$C/:p(HK>T8Y[gHQ4`4)'$Ab(Nof%V'8hL&#<NEdtg(n'=S1A(Q1/I&4([%dM`,Iu'1:_hL>SfD07&6D<fp8dHM7/g+"
"tlPN9J*rKaPct&?'uBCem^jn%9_K)<,C5K3s=5g&GmJb*[SYq7K;TRLGCsM-$$;S%:Y@r7AK0pprpL<Lrh,q7e/%KWK:50I^+m'vi`3?%Zp+<-d+$L-Sv:@.o19n$s0&39;kn;S%BSq*"
"$3WoJSCLweV[aZ'MQIjO<7;X-X;&+dMLvu#^UsGEC9WEc[X(wI7#2.(F0jV*eZf<-Qv3J-c+J5AlrB#$p(H68LvEA'q3n0#m,[`*8Ft)FcYgEud]CWfm68,(aLA$@EFTgLXoBq/UPlp7"
":d[/;r_ix=:TF`S5H-b<LI&HY(K=h#)]Lk$K14lVfm:x$H<3^Ql<M`$OhapBnkup'D#L$Pb_`N*g]2e;X/Dtg,bsj&K#2[-:iYr'_wgH)NUIR8a1n#S?Yej'h8^58UbZd+^FKD*T@;6A"
"7aQC[K8d-(v6GI$x:T<&'Gp5Uf>@M.*J:;$-rv29'M]8qMv-tLp,'886iaC=Hb*YJoKJ,(j%K=H`K.v9HggqBIiZu'QvBT.#=)0ukruV&.)3=(^1`o*Pj4<-<aN((^7('#Z0wK#5GX@7"
"u][`*S^43933A4rl][`*O4CgLEl]v$1Q3AeF37dbXk,.)vj#x'd`;qgbQR%FW,2(?LO=s%Sc68%NP'##Aotl8x=BE#j1UD([3$M(]UI2LX3RpKN@;/#f'f/&_mt&F)XdF<9t4)Qa.*kT"
"LwQ'(TTB9.xH'>#MJ+gLq9-##@HuZPN0]u:h7.T..G:;$/Usj(T7`Q8tT72LnYl<-qx8;-HV7Q-&Xdx%1a,hC=0u+HlsV>nuIQL-5<N?)NBS)QN*_I,?&)2'IM%L3I)X((e/dl2&8'<M"
":^#M*Q+[T.Xri.LYS3v%fF`68h;b-X[/En'CR.q7E)p'/kle2HM,u;^%OKC-N+Ll%F9CF<Nf'^#t2L,;27W:0O@6##U6W7:$rJfLWHj$#)woqBefIZ.PK<b*t7ed;p*_m;4ExK#h@&]>"
"_>@kXQtMacfD.m-VAb8;IReM3$wf0''hra*so568'Ip&vRs849'MRYSp%:t:h5qSgwpEr$B>Q,;s(C#$)`svQuF$##-D,##,g68@2[T;.XSdN9Qe)rpt._K-#5wF)sP'##p#C0c%-Gb%"
"hd+<-j'Ai*x&&HMkT]C'OSl##5RG[JXaHN;d'uA#x._U;.`PU@(Z3dt4r152@:v,'R.Sj'w#0<-;kPI)FfJ&#AYJ&#//)>-k=m=*XnK$>=)72L]0I%>.G690a:$##<,);?;72#?x9+d;"
"^V'9;jY@;)br#q^YQpx:X#Te$Z^'=-=bGhLf:D6&bNwZ9-ZD#n^9HhLMr5G;']d&6'wYmTFmL<LD)F^%[tC'8;+9E#C$g%#5Y>q9wI>P(9mI[>kC-ekLC/R&CH+s'B;K-M6$EB%is00:"
"+A4[7xks.LrNk0&E)wILYF@2L'0Nb$+pv<(2.768/FrY&h$^3i&@+G%JT'<-,v`3;_)I9M^AE]CN?Cl2AZg+%4iTpT3<n-&%H%b<FDj2M<hH=&Eh<2Len$b*aTX=-8QxN)k11IM1c^j%"
"9s<L<NFSo)B?+<-(GxsF,^-Eh@$4dXhN$+#rxK8'je'D7k`e;)2pYwPA'_p9&@^18ml1^[@g4t*[JOa*[=Qp7(qJ_oOL^('7fB&Hq-:sf,sNj8xq^>$U4O]GKx'm9)b@p7YsvK3w^YR-"
"CdQ*:Ir<($u&)#(&?L9Rg3H)4fiEp^iI9O8KnTj,]H?D*r7'M;PwZ9K0E^k&-cpI;.p/6_vwoFMV<->#%Xi.LxVnrU(4&8/P+:hLSKj$#U%]49t'I:rgMi'FL@a:0Y-uA[39',(vbma*"
"hU%<-SRF`Tt:542R_VV$p@[p8DV[A,?1839FWdF<TddF<9Ah-6&9tWoDlh]&1SpGMq>Ti1O*H&#(AL8[_P%.M>v^-))qOT*F5Cq0`Ye%+$B6i:7@0IX<N+T+0MlMBPQ*Vj>SsD<U4JHY"
"8kD2)2fU/M#$e.)T4,_=8hLim[&);?UkK'-x?'(:siIfL<$pFM`i<?%W(mGDHM%>iWP,##P`%/L<eXi:@Z9C.7o=@(pXdAO/NLQ8lPl+HPOQa8wD8=^GlPa8TKI1CjhsCTSLJM'/Wl>-"
"S(qw%sf/@%#B6;/U7K]uZbi^Oc^2n<bhPmUkMw>%t<)'mEVE''n`WnJra$^TKvX5B>;_aSEK',(hwa0:i4G?.Bci.(X[?b*($,=-n<.Q%`(X=?+@Am*Js0&=3bh8K]mL<LoNs'6,'85`"
"0?t/'_U59@]ddF<#LdF<eWdF<OuN/45rY<-L@&#+fm>69=Lb,OcZV/);TTm8VI;?%OtJ<(b4mq7M6:u?KRdF<gR@2L=FNU-<b[(9c/ML3m;Z[$oF3g)GAWqpARc=<ROu7cL5l;-[A]%/"
"+fsd;l#SafT/f*W]0=O'$(Tb<[)*@e775R-:Yob%g*>l*:xP?Yb.5)%w_I?7uk5JC+FS(m#i'k.'a0i)9<7b'fs'59hq$*5Uhv##pi^8+hIEBF`nvo`;'l0.^S1<-wUK2/Coh58KKhLj"
"M=SO*rfO`+qC`W-On.=AJ56>>i2@2LH6A:&5q`?9I3@@'04&p2/LVa*T-4<-i3;M9UvZd+N7>b*eIwg:CC)c<>nO&#<IGe;__.thjZl<%w(Wk2xmp4Q@I#I9,DF]u7-P=.-_:YJ]aS@V"
"?6*C()dOp7:WL,b&3Rg/.cmM9&r^>$(>.Z-I&J(Q0Hd5Q%7Co-b`-c<N(6r@ip+AurK<m86QIth*#v;-OBqi+L7wDE-Ir8K['m+DDSLwK&/.?-V%U_%3:qKNu$_b*B-kp7NaD'QdWQPK"
"Yq[@>P)hI;*_F]u`Rb[.j8_Q/<&>uu+VsH$sM9TA%?)(vmJ80),P7E>)tjD%2L=-t#fK[%`v=Q8<FfNkgg^oIbah*#8/Qt$F&:K*-(N/'+1vMB,u()-a.VUU*#[e%gAAO(S>WlA2);Sa"
">gXm8YB`1d@K#n]76-a$U,mF<fX]idqd)<3,]J7JmW4`6]uks=4-72L(jEk+:bJ0M^q-8Dm_Z?0olP1C9Sa&H[d&c$ooQUj]Exd*3ZM@-WGW2%s',B-_M%>%Ul:#/'xoFM9QX-$.QN'>"
"[%$Z$uF6pA6Ki2O5:8w*vP1<-1`[G,)-m#>0`P&#eb#.3i)rtB61(o'$?X3B</R90;eZ]%Ncq;-Tl]#F>2Qft^ae_5tKL9MUe9b*sLEQ95C&`=G?@Mj=wh*'3E>=-<)Gt*Iw)'QG:`@I"
"wOf7&]1i'S01B+Ev/Nac#9S;=;YQpg_6U`*kVY39xK,[/6Aj7:'1Bm-_1EYfa1+o&o4hp7KN_Q(OlIo@S%;jVdn0'1<Vc52=u`3^o-n1'g4v58Hj&6_t7$##?M)c<$bgQ_'SY((-xkA#"
"Y(,p'H9rIVY-b,'%bCPF7.J<Up^,(dU1VY*5#WkTU>h19w,WQhLI)3S#f$2(eb,jr*b;3Vw]*7NH%$c4Vs,eD9>XW8?N]o+(*pgC%/72LV-u<Hp,3@e^9UB1J+ak9-TN/mhKPg+AJYd$"
"MlvAF_jCK*.O-^(63adMT->W%iewS8W6m2rtCpo'RS1R84=@paTKt)>=%&1[)*vp'u+x,VrwN;&]kuO9JDbg=pO$J*.jVe;u'm0dr9l,<*wMK*Oe=g8lV_KEBFkO'oU]^=[-792#ok,)"
"i]lR8qQ2oA8wcRCZ^7w/Njh;?.stX?Q1>S1q4Bn$)K1<-rGdO'$Wr.Lc.CG)$/*JL4tNR/,SVO3,aUw'DJN:)Ss;wGn9A32ijw%FL+Z0Fn.U9;reSq)bmI32U==5ALuG&#Vf1398/pVo"
"1*c-(aY168o<`JsSbk-,1N;$>0:OUas(3:8Z972LSfF8eb=c-;>SPw7.6hn3m`9^Xkn(r.qS[0;T%&Qc=+STRxX'q1BNk3&*eu2;&8q$&x>Q#Q7^Tf+6<(d%ZVmj2bDi%.3L2n+4W'$P"
"iDDG)g,r%+?,$@?uou5tSe2aN_AQU*<h`e-GI7)?OK2A.d7_c)?wQ5AS@DL3r#7fSkgl6-++D:'A,uq7SvlB$pcpH'q3n0#_%dY#xCpr-l<F0NR@-##FEV6NTF6##$l84N1w?AO>'IAO"
"URQ##V^Fv-XFbGM7Fl(N<3DhLGF%q.1rC$#:T__&Pi68%0xi_&[qFJ(77j_&JWoF.V735&T,[R*:xFR*K5>>#`bW-?4Ne_&6Ne_&6Ne_&n`kr-#GJcM6X;uM6X;uM(.a..^2TkL%oR(#"
";u.T%fAr%4tJ8&><1=GHZ_+m9/#H1F^R#SC#*N=BA9(D?v[UiFY>>^8p,KKF.W]L29uLkLlu/+4T<XoIB&hx=T1PcDaB&;HH+-AFr?(m9HZV)FKS8JCw;SD=6[^/DZUL`EUDf]GGlG&>"
"w$)F./^n3+rlo+DB;5sIYGNk+i1t-69Jg--0pao7Sm#K)pdHW&;LuDNH@H>#/X-TI(;P>#,Gc>#0Su>#4`1?#8lC?#<xU?#@.i?#D:%@#HF7@#LRI@#P_[@#Tkn@#Xw*A#]-=A#a9OA#"
"d<F&#*;G##.GY##2Sl##6`($#:l:$#>xL$#B.`$#F:r$#JF.%#NR@%#R_R%#Vke%#Zww%#_-4&#3^Rh%Sflr-k'MS.o?.5/sWel/wpEM0%3'/1)K^f1-d>G21&v(35>V`39V7A4=onx4"
"A1OY5EI0;6Ibgr6M$HS7Q<)58C5w,;WoA*#[%T*#`1g*#d=#+#hI5+#lUG+#pbY+#tnl+#x$),#&1;,#*=M,#.I`,#2Ur,#6b.-#;w[H#iQtA#m^0B#qjBB#uvTB##-hB#'9$C#+E6C#"
"/QHC#3^ZC#7jmC#;v)D#?,<D#C8ND#GDaD#KPsD#O]/E#g1A5#KA*1#gC17#MGd;#8(02#L-d3#rWM4#Hga1#,<w0#T.j<#O#'2#CYN1#qa^:#_4m3#o@/=#eG8=#t8J5#`+78#4uI-#"
"m3B2#SB[8#Q0@8#i[*9#iOn8#1Nm;#^sN9#qh<9#:=x-#P;K2#$%X9#bC+.#Rg;<#mN=.#MTF.#RZO.#2?)4#Y#(/#[)1/#b;L/#dAU/#0Sv;#lY$0#n`-0#sf60#(F24#wrH0#%/e0#"
"TmD<#%JSMFove:CTBEXI:<eh2g)B,3h2^G3i;#d3jD>)4kMYD4lVu`4m`:&5niUA5@(A5BA1]PBB:xlBCC=2CDLXMCEUtiCf&0g2'tN?PGT4CPGT4CPGT4CPGT4CPGT4CPGT4CPGT4CP"
"GT4CPGT4CPGT4CPGT4CPGT4CPGT4CP-qekC`.9kEg^+F$kwViFJTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5KTB&5o,^<-28ZI'O?;xp"
"O?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xpO?;xp;7q-#lLYI:xvD=#";
s8_t rn_get_default_font(ma_arena_t *arena) {
ma_temp_t scratch = ma_begin_scratch1(arena);
int compressed_ttf_size = (((int)sizeof(proggy_clean_ttf_compressed_data_base85) + 4) / 5) * 4;
void *compressed_ttf = ma_push_size(scratch.arena, (size_t)compressed_ttf_size);
Decode85((const unsigned char *)proggy_clean_ttf_compressed_data_base85, (unsigned char *)compressed_ttf);
const unsigned int buf_decompressed_size = stb_decompress_length((const unsigned char *)compressed_ttf);
unsigned char *buf_decompressed_data = (unsigned char *)ma_push_size(arena, buf_decompressed_size);
stb_decompress(buf_decompressed_data, (const unsigned char *)compressed_ttf, (unsigned int)compressed_ttf_size);
s8_t result = s8((char *)buf_decompressed_data, buf_decompressed_size);
ma_end_scratch(scratch);
return result;
}

156
src/render/font.c Normal file
View File

@@ -0,0 +1,156 @@
typedef struct rn_glyph_t rn_glyph_t;
struct rn_glyph_t {
v2f32_t size;
v2f32_t offset;
f32 xadvance;
f32 left_side_bearing;
r2f32_t atlas_bounding_box;
};
typedef struct rn_font_t rn_font_t;
struct rn_font_t {
rn_glyph_t *glyphs;
i32 glyph_count;
u32 first_char, last_char;
u32 texture_id;
f32 size;
f32 descent;
f32 ascent;
f32 line_gap;
r2f32_t white_texture_bounding_box;
};
typedef struct rn_atlas_t rn_atlas_t;
struct rn_atlas_t {
u8 *bitmap;
v2i32_t size;
v2f32_t inverse_size;
i32 xcursor;
i32 ycursor;
i32 biggest_height;
r2f32_t white_texture_bounding_box;
u32 texture_id;
};
rn_atlas_t *rn_create_atlas(ma_arena_t *arena, v2i32_t size) {
rn_atlas_t *result = ma_push_type(arena, rn_atlas_t);
result->size = size;
result->inverse_size.x = 1.f / (f32)result->size.x;
result->inverse_size.y = 1.f / (f32)result->size.y;
result->bitmap = ma_push_array(arena, u8, size.x * size.y);
// Add a whitebox first for rectangle rendering
for (i32 y = 0; y < 16; y++) {
for (i32 x = 0; x < 16; x++) {
u8 *dst = result->bitmap + x + y * result->size.x;
*dst = 0xff;
}
}
result->white_texture_bounding_box = (r2f32_t){
.min = { 2.f * result->inverse_size.x, 2.f / (f32)result->size.y},
.max = {14.f * result->inverse_size.x, 14.f / (f32)result->size.y}
};
result->xcursor += 16;
result->biggest_height += 16;
return result;
}
r2f32_t rn_pack_bitmap(rn_atlas_t *atlas, u8 *bitmap, i32 width, i32 height) {
// Packing into a texture atlas
// @Inefficient The algorithm is a simplest thing I had in mind, first we advance
// through the atlas in X packing consecutive glyphs. After we get to the end of the row
// we advance to the next row by the Y size of the biggest packed glyph. If we get to the
// end of atlas and fail to pack everything the app panics.
i32 spacing = 4;
if (atlas->xcursor + width > atlas->size.x) {
if (atlas->ycursor + height < atlas->size.y) {
atlas->xcursor = 0;
atlas->ycursor += atlas->biggest_height + spacing;
} else {
fatalf("error while packing a font into atlas. rn_atlas_t size for this font scale is a bit too small");
}
}
u8 *src = bitmap;
for (i32 y = atlas->ycursor; y < atlas->ycursor + height; y += 1) {
for (i32 x = atlas->xcursor; x < atlas->xcursor + width; x += 1) {
u8 *dst = atlas->bitmap + x + y * atlas->size.x;
*dst = *src++;
}
}
v2f32_t size = {(f32)width * atlas->inverse_size.x, (f32)height * atlas->inverse_size.y};
v2f32_t pos = {(f32)atlas->xcursor * atlas->inverse_size.x, (f32)atlas->ycursor * atlas->inverse_size.y};
r2f32_t result = {pos.x, pos.y, pos.x + size.x, pos.y + size.y};
atlas->xcursor += width + spacing;
if (height > atlas->biggest_height) {
atlas->biggest_height = height;
}
return result;
}
rn_font_t rn_create_font(ma_arena_t *glyph_arena, s8_t font_data, rn_atlas_t *atlas, i32 size) {
rn_font_t result = {};
stbtt_fontinfo stb_font;
i32 success = stbtt_InitFont(&stb_font, (const unsigned char *)font_data.str, 0);
if (!success) {
return result;
}
f32 scale = stbtt_ScaleForPixelHeight(&stb_font, (f32)size);
// f32 em_scale = stbtt_ScaleForMappingEmToPixels(&stb_font, (f32)size);
i32 ascent, descent, line_gap;
stbtt_GetFontVMetrics(&stb_font, &ascent, &descent, &line_gap);
result.ascent = (f32)ascent * scale;
result.descent = (f32)descent * scale;
result.line_gap = (f32)line_gap * scale;
result.size = (f32)size;
result.first_char = ' ';
result.last_char = '~';
result.white_texture_bounding_box = atlas->white_texture_bounding_box;
i32 glyph_count = result.last_char - result.first_char + 1; // + 1 because it's '<=' not '<'
result.glyphs = ma_push_array(glyph_arena, rn_glyph_t, glyph_count);
for (u32 ascii_symbol = result.first_char; ascii_symbol <= result.last_char; ascii_symbol++) {
i32 width, height, xoff, yoff;
u8 *bitmap = (u8 *)stbtt_GetCodepointBitmap(&stb_font, 0, scale, ascii_symbol, &width, &height, &xoff, &yoff);
i32 xadvance, left_side_bearing;
stbtt_GetCodepointHMetrics(&stb_font, ascii_symbol, &xadvance, &left_side_bearing);
rn_glyph_t glyph = {};
glyph.atlas_bounding_box = rn_pack_bitmap(atlas, bitmap, width, height);
glyph.size = (v2f32_t){(f32)width, (f32)height};
glyph.offset = (v2f32_t){(f32)xoff, (f32)yoff};
glyph.xadvance = (f32)xadvance * scale;
glyph.left_side_bearing = (f32)left_side_bearing * scale;
assert(result.glyph_count + 1 <= glyph_count);
result.glyphs[result.glyph_count++] = glyph;
stbtt_FreeBitmap(bitmap, 0);
}
return result;
}
rn_glyph_t *rn_get_glyph(rn_font_t *font, u32 codepoint) {
b32 is_in_range = codepoint >= font->first_char && codepoint <= font->last_char;
if (is_in_range) {
u32 index = codepoint - font->first_char;
return &font->glyphs[index];
} else {
u32 index = '?' - font->first_char;
return &font->glyphs[index];
}
}

306
src/render/render_opengl.c Normal file
View File

@@ -0,0 +1,306 @@
typedef struct rn_shader_t rn_shader_t;
struct rn_shader_t {
u32 pipeline;
u32 fshader;
u32 vshader;
};
typedef struct rn_vertex_t rn_vertex_t;
struct rn_vertex_t {
v2f32_t pos;
v2f32_t tex;
v4f32_t color;
};
typedef enum {
rn_cmd_kind_null,
rn_cmd_kind_clear,
rn_cmd_kind_quad,
rn_cmd_kind_set_clip,
} rn_cmd_kind_t;
typedef struct rn_cmd_t rn_cmd_t;
struct rn_cmd_t {
rn_cmd_t *next;
rn_cmd_kind_t kind;
union {
v4f32_t color;
r2f32_t rect;
struct { rn_vertex_t *vertex; u32 len; };
};
};
typedef struct rn_state_t rn_state_t;
struct rn_state_t {
rn_font_t main_font;
rn_shader_t shader2d;
v2f32_t window_size;
rn_cmd_t *first_cmd;
rn_cmd_t *last_cmd;
rn_vertex_t *vertices;
u32 len;
u32 cap;
u32 vao;
u32 vbo;
};
rn_state_t rn_state;
rn_shader_t rn_create_shader(char *glsl_vshader, char *glsl_fshader) {
rn_shader_t result = {};
result.vshader = glCreateShaderProgramv(GL_VERTEX_SHADER, 1, &glsl_vshader);
result.fshader = glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, &glsl_fshader);
GLint linked;
glGetProgramiv(result.vshader, GL_LINK_STATUS, &linked);
if (!linked) {
char message[1024];
glGetProgramInfoLog(result.vshader, sizeof(message), NULL, message);
fatalf("failed to create vertex shader: %s", message);
}
glGetProgramiv(result.fshader, GL_LINK_STATUS, &linked);
if (!linked) {
char message[1024];
glGetProgramInfoLog(result.fshader, sizeof(message), NULL, message);
fatalf("failed to create fragment shader: %s", message);
}
glGenProgramPipelines(1, &result.pipeline);
glUseProgramStages(result.pipeline, GL_VERTEX_SHADER_BIT, result.vshader);
glUseProgramStages(result.pipeline, GL_FRAGMENT_SHADER_BIT, result.fshader);
return result;
}
void gl_debug_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar *message, const void *user) {
if (severity == GL_DEBUG_SEVERITY_HIGH || severity == GL_DEBUG_SEVERITY_MEDIUM) {
fatalf("opengl message: %s", message);
} else {
debugf("opengl message: %s", message);
}
}
void rn_begin(v2f32_t window_size) {
rn_state.window_size = window_size;
}
void rn_init(ma_arena_t *perm) {
rn_state.cap = 1024*64;
rn_state.vertices = ma_push_array(perm, rn_vertex_t, rn_state.cap);
{
ma_temp_t scratch = ma_begin_scratch1(perm);
s8_t font_data = rn_get_default_font(tcx.temp);
rn_atlas_t *atlas = rn_create_atlas(scratch.arena, (v2i32_t){2048, 2048});
u32 font_size = 100;
rn_state.main_font = rn_create_font(perm, font_data, atlas, font_size);
GLint filter = GL_NEAREST;
// if (StyleFontFilter == 1) filter = GL_LINEAR;
glCreateTextures(GL_TEXTURE_2D, 1, &atlas->texture_id);
glTextureParameteri(atlas->texture_id, GL_TEXTURE_MIN_FILTER, filter);
glTextureParameteri(atlas->texture_id, GL_TEXTURE_MAG_FILTER, filter);
glTextureParameteri(atlas->texture_id, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTextureParameteri(atlas->texture_id, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTextureStorage2D(atlas->texture_id, 1, GL_R8, (GLsizei)atlas->size.x, (GLsizei)atlas->size.y);
glTextureSubImage2D(atlas->texture_id, 0, 0, 0, (GLsizei)atlas->size.x, (GLsizei)atlas->size.y, GL_RED, GL_UNSIGNED_BYTE, atlas->bitmap);
rn_state.main_font.texture_id = atlas->texture_id;
ma_end_scratch(scratch);
}
glDebugMessageCallback(&gl_debug_callback, NULL);
glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS);
glCreateBuffers(1, &rn_state.vbo);
glNamedBufferStorage(rn_state.vbo, rn_state.cap * sizeof(rn_vertex_t), 0, GL_DYNAMIC_STORAGE_BIT);
glCreateVertexArrays(1, &rn_state.vao);
GLint vbuf_index = 0;
glVertexArrayVertexBuffer(rn_state.vao, vbuf_index, rn_state.vbo, 0, sizeof(struct rn_vertex_t));
GLint a_pos = 0;
glVertexArrayAttribFormat(rn_state.vao, a_pos, 2, GL_FLOAT, GL_FALSE, offsetof(struct rn_vertex_t, pos));
glVertexArrayAttribBinding(rn_state.vao, a_pos, vbuf_index);
glEnableVertexArrayAttrib(rn_state.vao, a_pos);
GLint a_tex = 1;
glVertexArrayAttribFormat(rn_state.vao, a_tex, 2, GL_FLOAT, GL_FALSE, offsetof(struct rn_vertex_t, tex));
glVertexArrayAttribBinding(rn_state.vao, a_tex, vbuf_index);
glEnableVertexArrayAttrib(rn_state.vao, a_tex);
GLint a_color = 2;
glVertexArrayAttribFormat(rn_state.vao, a_color, 4, GL_FLOAT, GL_FALSE, offsetof(struct rn_vertex_t, color));
glVertexArrayAttribBinding(rn_state.vao, a_color, vbuf_index);
glEnableVertexArrayAttrib(rn_state.vao, a_color);
char *glsl_vshader =
"#version 450 core\n"
"layout(location=0) uniform vec2 U_InvHalfScreenSize;\n"
"layout(location=0) in vec2 VPos;\n"
"layout(location=1) in vec2 VTex;\n"
"layout(location=2) in vec4 VColor;\n"
"\n"
"out gl_PerVertex { vec4 gl_Position; }; // required because of ARB_separate_shader_objects\n"
"out vec2 FUV;\n"
"out vec4 FColor;\n"
"\n"
"void main() {\n"
" vec2 pos = VPos * U_InvHalfScreenSize;\n"
" pos.y = 2 - pos.y; // invert y axis\n"
" pos -= vec2(1, 1); // convert to 0,1 range\n"
"\n"
" gl_Position = vec4(pos, 0, 1);\n"
" FUV = VTex;\n"
"\n"
" FColor = VColor;\n"
"}\n";
char *glsl_fshader =
"#version 450 core\n"
"\n"
"in vec2 FUV;\n"
"in vec4 FColor;\n"
"\n"
"layout (binding=0) uniform sampler2D S_Texture;\n"
"layout (location=0) out vec4 OutColor;\n"
"\n"
"void main() {\n"
" vec4 c = FColor;\n"
" c.a *= texture(S_Texture, FUV).r;\n"
" OutColor = c;\n"
"}\n";
rn_state.shader2d = rn_create_shader(glsl_vshader, glsl_fshader);
}
rn_cmd_t *rn_get_cmd(rn_cmd_kind_t kind) {
b32 alloc_new = false;
if (rn_state.last_cmd == NULL) {
alloc_new = true;
} else if (rn_state.last_cmd->kind != kind) {
alloc_new = true;
}
rn_cmd_t *result = rn_state.last_cmd;
if (alloc_new) {
result = ma_push_type(tcx.temp, rn_cmd_t);
result->kind = kind;
SLLQ_APPEND(rn_state.first_cmd, rn_state.last_cmd, result);
if (rn_cmd_kind_quad) {
result->vertex = rn_state.vertices + rn_state.len;
}
}
return result;
}
rn_vertex_t *rn_push_vertex(rn_cmd_t *cmd, u32 count) {
rn_vertex_t *result = cmd->vertex + cmd->len;
cmd->len += count;
return result;
}
void rn_push_quad(r2f32_t rect, r2f32_t tex, v4f32_t color) {
rn_cmd_t *cmd = rn_get_cmd(rn_cmd_kind_quad);
rn_vertex_t *v = rn_push_vertex(cmd, 6);
v[0] = (rn_vertex_t){
{rect.min.x, rect.max.y},
{ tex.min.x, tex.max.y},
color
};
v[1] = (rn_vertex_t){
{rect.max.x, rect.max.y},
{ tex.max.x, tex.max.y},
color
};
v[2] = (rn_vertex_t){
{rect.min.x, rect.min.y},
{ tex.min.x, tex.min.y},
color
};
v[3] = (rn_vertex_t){
{rect.min.x, rect.min.y},
{ tex.min.x, tex.min.y},
color
};
v[4] = (rn_vertex_t){
{rect.max.x, rect.max.y},
{ tex.max.x, tex.max.y},
color
};
v[5] = (rn_vertex_t){
{rect.max.x, rect.min.y},
{ tex.max.x, tex.min.y},
color
};
}
void rn_draw_rect(r2f32_t rect, v4f32_t color) {
rn_push_quad(rect, rn_state.main_font.white_texture_bounding_box, color);
}
i64 rn_get_char_spacing(rn_font_t *font, u32 codepoint) {
rn_glyph_t *g = rn_get_glyph(font, codepoint);
if (g->xadvance) return (i64)g->xadvance;
return (i64)g->size.x;
}
i64 rn_get_line_spacing(rn_font_t *font) {
i64 result = (i64)(font->ascent - font->descent + font->line_gap);
return result;
}
v2f32_t rn_base_draw_string(rn_font_t *font, s8_t string, v2f32_t pos, v4f32_t color, b32 draw) {
pos.y += rn_get_line_spacing(font) + font->descent;
v2f32_t original_pos = pos;
for (utf8_iter_t iter = utf8_iterate_ex(string.str, (int)string.len); iter.item; utf8_advance(&iter)) {
u32 codepoint = iter.item;
rn_glyph_t *g = rn_get_glyph(font, codepoint);
r2f32_t rect = r2f32_mindim(v2f32_add(pos, g->offset), g->size);
if (draw && codepoint != '\n' && codepoint != ' ' && codepoint != '\t') {
rn_push_quad(rect, g->atlas_bounding_box, color);
}
pos.x += g->xadvance;
}
v2f32_t result = {pos.x - original_pos.x, font->size};
return result;
}
void rn_end(v2f32_t window_size, v4f32_t color) {
f32 wx = window_size.x;
f32 wy = window_size.y;
glEnable(GL_BLEND);
glEnable(GL_SCISSOR_TEST);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_DEPTH_TEST);
glDisable(GL_CULL_FACE);
glViewport(0, 0, (GLsizei)wx, (GLsizei)wy);
glScissor(0, 0, (GLsizei)wx, (GLsizei)wy);
glClearColor(color.r, color.g, color.b, color.a);
glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
// Default draw using the font texture
glBindProgramPipeline(rn_state.shader2d.pipeline);
float xinverse = 1.f / (wx / 2.f);
float yinverse = 1.f / (wy / 2.f);
glProgramUniform2f(rn_state.shader2d.vshader, 0, xinverse, yinverse);
for (rn_cmd_t *it = rn_state.first_cmd; it; it = it->next) {
if (it->kind == rn_cmd_kind_quad) {
glNamedBufferSubData(rn_state.vbo, 0, it->len * sizeof(rn_vertex_t), it->vertex);
glBindVertexArray(rn_state.vao);
GLint s_texture = 0; // texture unit that sampler2D will use in GLSL code
glBindTextureUnit(s_texture, rn_state.main_font.texture_id);
glDrawArrays(GL_TRIANGLES, 0, it->len);
} else_is_invalid;
}
}

5079
src/render/stb_truetype.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -3,7 +3,13 @@
#include "core/core_inc.c" #include "core/core_inc.c"
#include "app/app.c" #include "app/app.c"
#include "gfx2d/gfx2d.c"
#define STB_TRUETYPE_IMPLEMENTATION
#include "render/stb_truetype.h"
#include "render/backup_font.c"
#include "render/font.c"
#include "render/render_opengl.c"
// #include "gfx2d/gfx2d.c"
#include "ui.c" #include "ui.c"
@@ -19,7 +25,7 @@
typedef struct globals_t globals_t; typedef struct globals_t globals_t;
struct globals_t { struct globals_t {
gfx_t *gfx; // gfx_t *gfx;
app_event_t event; app_event_t event;
}; };
gb globals_t *globals; gb globals_t *globals;
@@ -30,28 +36,11 @@ fn void app_init(void) {
ma_arena_t *perm = &tcx._perm; ma_arena_t *perm = &tcx._perm;
globals = ma_push_type(perm, globals_t); globals = ma_push_type(perm, globals_t);
globals->gfx = ma_push_type(perm, gfx_t); // globals->gfx = ma_push_type(perm, gfx_t);
rn_init(perm);
ui_init(perm); ui_init(perm);
} }
#if 0
ui_begin_expander("app_event_t");
{
ui_edit_enum("kind: app_event_kind_t = %S");
ui_begin_expander("mouse_wheel_delta: v3f64_t");
{
ui_edit_f64(&x, "x: %f", x);
ui_edit_f64(&y, "y: %f", y);
ui_edit_f64(&z, "z: %f", z);
}
ui_end_expander();
}
ui_end_expander();
#endif
fn b32 app_update(app_event_list_t events) { fn b32 app_update(app_event_list_t events) {
ui_begin_frame(); ui_begin_frame();
defer_block(ui_begin_build(), ui_end_build()) { defer_block(ui_begin_build(), ui_end_build()) {
@@ -104,6 +93,15 @@ fn b32 app_update(app_event_list_t events) {
} }
{
app_event_t *ev = events.last;
rn_begin(v2f64_to_v2f32(ev->window_size));
rn_base_draw_string(&rn_state.main_font, s8_lit("hello world!"), v2f32(0,0), black_color_global, true);
rn_end(v2f64_to_v2f32(ev->window_size), white_color_global);
}
#if 0
// These steps should be totally optional!! // These steps should be totally optional!!
{ {
app_event_t *ev = events.last; app_event_t *ev = events.last;
@@ -137,6 +135,7 @@ fn b32 app_update(app_event_list_t events) {
ui_draw(globals->gfx); ui_draw(globals->gfx);
gfx_end(globals->gfx); gfx_end(globals->gfx);
} }
#endif
ui_end_frame(); ui_end_frame();
return false; return false;

View File

@@ -72,7 +72,7 @@ ui_box_t *ui_alloc_box(ui_id_t id) {
void ui_init(ma_arena_t *arena) { void ui_init(ma_arena_t *arena) {
assert(ui->box_arena != tcx.temp); assert(arena != tcx.temp);
ui = ma_push_type(arena, ui_t); ui = ma_push_type(arena, ui_t);
ui->box_arena = arena; ui->box_arena = arena;
@@ -277,6 +277,7 @@ void ui_end_frame(void) {
} }
} }
#if 0
void ui_draw(gfx_t *gfx) { void ui_draw(gfx_t *gfx) {
// compute standalone sizes: (pixels, text_content) // compute standalone sizes: (pixels, text_content)
for (ui_preorder_iter_t it = ui_iterate_preorder(ui->root); ui_preorder_iter_is_valid(it); ui_iter_advance_preorder(&it)) { for (ui_preorder_iter_t it = ui_iterate_preorder(ui->root); ui_preorder_iter_is_valid(it); ui_iter_advance_preorder(&it)) {
@@ -310,4 +311,5 @@ void ui_draw(gfx_t *gfx) {
ui_box_t *box = it.box; ui_box_t *box = it.box;
} }
} }
#endif

View File

@@ -1,10 +1,10 @@
[ ] app [ ] app
[ ] sleep
[ ] ui [ ] ui
[ ] event playback [ ] event playback
[ ] maybe different events structure: { frame1: { event1, event2 }, frame2: {event1} } [ ] maybe different events structure: { frame1: { event1, event2 }, frame2: {event1} }
[ ] how to fix variable scroll? or do we not care? (probably need delta time) [ ] how to fix variable scroll? or do we not care? (probably need delta time)
[ ] win32 [ ] win32
[ ] sleep
[ ] hot reload / plugins [ ] hot reload / plugins
[ ] tests using yield [ ] tests using yield
[ ] linux [ ] linux