win32 drawing text
This commit is contained in:
@@ -1,2 +1,7 @@
|
||||
#include "app.gen.c"
|
||||
#include "app_wasm.c"
|
||||
|
||||
#if PLATFORM_WINDOWS
|
||||
#include "app_win32.c"
|
||||
#elif PLATFORM_WASM
|
||||
#include "app_wasm.c"
|
||||
#endif
|
||||
@@ -186,6 +186,12 @@ void meta_app(ma_arena_t *arena) {
|
||||
app_event_t *last;
|
||||
i32 len;
|
||||
};
|
||||
|
||||
// struct app_frame_t {
|
||||
// f64 delta;
|
||||
// f64 time;
|
||||
// app_event_list_t event_list;
|
||||
// };
|
||||
));
|
||||
|
||||
sb8_serial_ast_to_code(h, decls);
|
||||
|
||||
@@ -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 "glad/glad.h"
|
||||
#include "glad/glad.c"
|
||||
|
||||
#pragma comment(linker, "/subsystem:windows")
|
||||
#pragma comment(lib, "gdi32.lib")
|
||||
#pragma comment(lib, "user32.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 WNDCLASSW w32_wc;
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
zero_struct(&w32_event_list);
|
||||
*waited = false;
|
||||
|
||||
MSG msg;
|
||||
if (wait) {
|
||||
if (PeekMessageW(&msg, NULL, 0, 0, PM_NOREMOVE) == 0) {
|
||||
*waited = true;
|
||||
}
|
||||
|
||||
GetMessageW(&msg, NULL, 0, 0);
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessageW(&msg);
|
||||
@@ -331,7 +336,13 @@ int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int n
|
||||
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;
|
||||
DEVMODEW devmodew = {0};
|
||||
@@ -339,16 +350,23 @@ int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int n
|
||||
refresh_rate = (f64)devmodew.dmDisplayFrequency;
|
||||
}
|
||||
|
||||
app_init();
|
||||
|
||||
f64 time_frame_start = w32_seconds_now();
|
||||
f64 time_delta = 1.0 / refresh_rate;
|
||||
f64 time_total = 0.0;
|
||||
f64 time_update = 0.0;
|
||||
b32 wait_for_events = false;
|
||||
|
||||
u64 consecutive_missed_frames = 0;
|
||||
u64 missed_frames = 0;
|
||||
u64 frame = 0;
|
||||
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) {
|
||||
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);
|
||||
// debugf("time_update: %f", time_update);
|
||||
|
||||
#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;
|
||||
}
|
||||
if (event_list.len == 0) {
|
||||
w32_push_event((app_event_t){.kind = app_event_kind_update});
|
||||
event_list = w32_event_list;
|
||||
}
|
||||
#endif
|
||||
|
||||
// w32_present_canvas(&canvas);
|
||||
b32 animating = app_update(event_list);
|
||||
wait_for_events = !animating;
|
||||
|
||||
SwapBuffers(w32_dc);
|
||||
|
||||
ma_set0(tcx.temp);
|
||||
///////////////////////////////
|
||||
@@ -378,7 +393,7 @@ int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int n
|
||||
if (time_update < time_delta) {
|
||||
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??
|
||||
if (w32_good_scheduling) {
|
||||
f64 time_to_sleep = time_delta - time_update;
|
||||
|
||||
1819
src/app/glad/glad.c
Normal file
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
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
311
src/app/glad/khrplatform.h
Normal 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_ */
|
||||
@@ -183,6 +183,7 @@ fn void utf8_advance(utf8_iter_t *iter) {
|
||||
iter->item = r.out_str;
|
||||
}
|
||||
|
||||
// @todo: s8_t, change name
|
||||
fn utf8_iter_t utf8_iterate_ex(char *str, int len) {
|
||||
utf8_iter_t result = {str, len};
|
||||
if (len) utf8_advance(&result);
|
||||
|
||||
146
src/draw/gfx2d.c
Normal file
146
src/draw/gfx2d.c
Normal 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,
|
||||
});
|
||||
}
|
||||
@@ -30,4 +30,5 @@ fn f64 os_get_milliseconds(void) {
|
||||
f64 secs = os_seconds_now();
|
||||
f64 result = secs * 1000;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
247
src/render/backup_font.c
Normal file
247
src/render/backup_font.c
Normal 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+`�j@'DbG&#^$PG.Ll+DNa<XCMKEV*N)LN/N"
|
||||
"*b=%Q6pia-Xg8I$<MR&,VdJe$<(7G;Ckl'&hF;;$<_=X(b.RS%%)###MPBuuE1V:v&cXm#(&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	v68C5g?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'8uTGT5g)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	r+$%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;BoFDoS97h5g)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^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
156
src/render/font.c
Normal 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
306
src/render/render_opengl.c
Normal 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
5079
src/render/stb_truetype.h
Normal file
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,13 @@
|
||||
|
||||
#include "core/core_inc.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"
|
||||
|
||||
@@ -19,7 +25,7 @@
|
||||
|
||||
typedef struct globals_t globals_t;
|
||||
struct globals_t {
|
||||
gfx_t *gfx;
|
||||
// gfx_t *gfx;
|
||||
app_event_t event;
|
||||
};
|
||||
gb globals_t *globals;
|
||||
@@ -30,28 +36,11 @@ fn void app_init(void) {
|
||||
|
||||
ma_arena_t *perm = &tcx._perm;
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
#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) {
|
||||
ui_begin_frame();
|
||||
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!!
|
||||
{
|
||||
app_event_t *ev = events.last;
|
||||
@@ -137,6 +135,7 @@ fn b32 app_update(app_event_list_t events) {
|
||||
ui_draw(globals->gfx);
|
||||
gfx_end(globals->gfx);
|
||||
}
|
||||
#endif
|
||||
|
||||
ui_end_frame();
|
||||
return false;
|
||||
|
||||
@@ -72,7 +72,7 @@ ui_box_t *ui_alloc_box(ui_id_t id) {
|
||||
|
||||
|
||||
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->box_arena = arena;
|
||||
@@ -277,6 +277,7 @@ void ui_end_frame(void) {
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
void ui_draw(gfx_t *gfx) {
|
||||
// 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)) {
|
||||
@@ -310,4 +311,5 @@ void ui_draw(gfx_t *gfx) {
|
||||
ui_box_t *box = it.box;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user