refactor: Split sigil-studio into sigil-app, sigil-graphics, sigil-audio
Split the monolithic sigil-studio package into three focused packages:
- sigil-app: Window management, input handling, application lifecycle (sokolapp, sokoltime, sokollog) - sigil-graphics: 2D rendering, images, fonts (sokolgfx, sokolgp, sokolglue, stbimage, stbtruetype) - sigil-audio: Audio playback and streaming (sokolaudio, stbvorbis)
Module names raised from (sigil studio X) to (sigil X) namespace.
sigil-studio is now a thin entrypoint package that bundles sigil-cli with the multimedia libraries.
Build system changes: - Add link-flags field to library struct for platform-specific libs - Add collect-native-link-flags to gather link flags from dependencies - Export host-os/host-arch from (sigil build) for package conditionals - Rename sokol.c files to sokol-{app,graphics,audio}.c to avoid object file collisions
Platform link flags: - Linux: -lX11 -lXi -lXcursor -lGL -lasound - macOS: Cocoa, QuartzCore, OpenGL, Metal, AudioToolbox frameworks
examples/hello-window.sgl | 8 +-
examples/solitaire.sgl | 12 +-
package.sgl | 181 +-
src/c/app.c | 526 ----
src/c/audio.c | 641 ----
src/c/font.c | 513 ----
src/c/graphics.c | 909 ------
src/c/image.c | 236 --
src/c/sokol.c | 18 -
src/c/stb_impl.c | 12 -
src/c/studio-internal.h | 58 -
src/sigil/studio/app.sgl | 91 -
src/sigil/studio/audio.sgl | 33 -
src/sigil/studio/font.sgl | 22 -
src/sigil/studio/graphics.sgl | 59 -
src/sigil/studio/image.sgl | 29 -
test/test-audio.sgl | 8 +-
test/test-draw-texture.sgl | 4 +-
test/test-font.sgl | 6 +-
test/test-image.sgl | 4 +-
test/test-transforms.sgl | 6 +-
vendor/sokol/sokol_app.h | 14047 ------------------------------------------------------------------------------------
vendor/sokol/sokol_audio.h | 2663 ----------------
vendor/sokol/sokol_gfx.h | 26612 ----------------------------------------------------------------------------------------------------------------------------------------------------------------
vendor/sokol/sokol_glue.h | 206 --
vendor/sokol/sokol_gp.h | 3112 -------------------
vendor/sokol/sokol_log.h | 334 --
vendor/sokol/sokol_time.h | 319 --
vendor/stb/stb_image.h | 7988 ------------------------------------------------
vendor/stb/stb_truetype.h | 5079 -------------------------------
vendor/stb/stb_vorbis.c | 5584 ----------------------------------
31 files changed, 49 insertions(+), 69271 deletions(-)examples/hello-window.sglmodified
;;; Opens a window and clears it to a cycling color.;;; Demonstrates the coroutine-based game loop where Scheme owns the main loop.(import (sigil studio app) (sigil studio graphics))(import (sigil app) (sigil graphics) (sigil math));; Simple color cycling using sine waves(define (get-color t) ;; Update time (let ((new-time (+ time dt))) ;; Get cycling color and render (begin-frame) (let ((color (get-color new-time))) (clear (car color) (cadr color) (caddr color))) (clear-screen (car color) (cadr color) (caddr color))) (end-frame) ;; Continue unless quit requestedexamples/solitaire.sglmodified
;;; n - New game(import (sigil core) (sigil studio app) (sigil studio graphics) (sigil studio font)) (sigil math) (sigil app) (sigil graphics) (sigil font));; ============================================================;; CONSTANTS;; ============================================================(define (on-init) (gfx-setup) (set-viewport SCREEN-WIDTH SCREEN-HEIGHT) (set-letterbox-color 0.02 0.02 0.05) ;; Load font (use test font for now) (set! *font* (load-font "test/Saucer.ttf" 24)) (set! *font-small* (load-font "test/Saucer.ttf" 16)) (set! *font* (load-font "packages/sigil-studio/test/Saucer.ttf" 24)) (set! *font-small* (load-font "packages/sigil-studio/test/Saucer.ttf" 16)) (init-game))package.sglmodified
;;; package.sgl - Sigil Studio Package Definition;;; sigil-studio - Multimedia development environment for Sigil;;;;;; Multimedia libraries for games and creative applications.;;; Built on Sokol for cross-platform graphics, audio, and windowing.;;; A "thick" CLI variant that includes windowing, graphics, and audio;;; support for building games and creative applications.;;;;;; Build with:;;; cd packages/sigil-studio;;; ../../run-sigil build # Build native lib + Scheme modules;;; ../../run-sigil run build:test # Build and run test binary;;; This is the same as the standard `sigil` CLI but with multimedia;;; modules available:;;; - (sigil app): Windowing, input handling, application lifecycle;;; - (sigil graphics): 2D rendering, images, textures;;; - (sigil image): CPU-side image loading;;; - (sigil font): Font loading and text rendering;;; - (sigil audio): Sound effects and music streaming(package name: "sigil-studio" version: "0.4.0" description: "Multimedia libraries for Sigil" description: "Multimedia development environment for Sigil" url: "https://codeberg.org/sigil/sigil" license: "BSD-3-Clause" authors: (list "David Wilson <[email protected]>") configs: (list (config name: 'dev output-dir: "build/dev" debug?: #t optimize: 0 c-flags: '("-Wall" "-Wextra" "-g" "-O0") features: '(debug dev)) ;; Reuse the CLI as entry point - same commands, just with more modules entry: '(sigil cli) bundle-name: "sigil-studio" (config name: 'release output-dir: "build/release" debug?: #f optimize: 2 c-flags: '("-Wall" "-O2") features: '(release))) dependencies: (list ;; Core CLI (brings in sigil-run, sigil-stdlib, and all CLI deps) (from-workspace name: "sigil-cli") default-config: 'dev libraries: (list (library name: 'sigil-studio c-sources: '("src/c/sokol.c" "src/c/stb_impl.c" "src/c/app.c" "src/c/image.c" "src/c/graphics.c" "src/c/font.c" "src/c/audio.c") sigil-sources: '("src/sigil/studio/app.sgl" "src/sigil/studio/image.sgl" "src/sigil/studio/graphics.sgl" "src/sigil/studio/font.sgl" "src/sigil/studio/audio.sgl"))) tasks: (list ;;; -------------------------------------------------------- ;;; Native Library Build ;;; -------------------------------------------------------- (task name: 'build:native description: "Build sigil-studio native library" steps: (list (ensure-dirs dirs: '("obj" "lib")) ;; Compile Sokol, STB, and wrapper sources ;; Note: Platform-specific flags are handled by the build system (compile-c-sources sources: '("src/c/sokol.c" "src/c/stb_impl.c" "src/c/app.c" "src/c/image.c" "src/c/graphics.c" "src/c/font.c" "src/c/audio.c") output-dir: (config-output-subdir "obj") flags: '("-Ivendor/sokol" "-Ivendor/stb" "-I../../components/libsigil/include" "-I../../components/libsigil/src" "-DSOKOL_NO_ENTRY" "-DSOKOL_GLCORE" "-D_GNU_SOURCE")) (create-static-library name: "sigil-studio" output-dir: (config-output-subdir "lib")))) ;;; -------------------------------------------------------- ;;; Scheme Module Build ;;; -------------------------------------------------------- (task name: 'build:scheme description: "Compile Scheme modules" steps: (list (ensure-dirs dirs: '("lib/sigil/studio")) (compile-sigil-module source: "src/sigil/studio/app.sgl" output: (config-output-subdir "lib/sigil/studio/app.sgb")) (compile-sigil-module source: "src/sigil/studio/image.sgl" output: (config-output-subdir "lib/sigil/studio/image.sgb")) (compile-sigil-module source: "src/sigil/studio/graphics.sgl" output: (config-output-subdir "lib/sigil/studio/graphics.sgb")) (compile-sigil-module source: "src/sigil/studio/font.sgl" output: (config-output-subdir "lib/sigil/studio/font.sgb")) (compile-sigil-module source: "src/sigil/studio/audio.sgl" output: (config-output-subdir "lib/sigil/studio/audio.sgb")))) ;;; -------------------------------------------------------- ;;; Combined Build ;;; -------------------------------------------------------- (task name: 'build description: "Build everything" depends: '(build:native build:scheme) steps: '()) ;;; -------------------------------------------------------- ;;; Test Binary ;;; -------------------------------------------------------- (task name: 'build:test description: "Build test binary" depends: '(build) steps: (list (ensure-dirs dirs: '("bin")) ;; Compile test harness (compile-c-sources sources: '("test/main.c") output-dir: (config-output-subdir "obj/test") flags: '("-I../../components/libsigil/include")) ;; Link test binary ;; NOTE: Custom linking is required because we need to link against ;; the parent project's bootstrap build (../../build/boot/). ;; Once proper package dependencies are implemented, this can use ;; the standard link-executable action. (lambda (ctx) (let* ((cfg (context-config ctx)) (out-dir (config-output-dir cfg)) (test-obj (path-join out-dir "obj/test/main.o")) (studio-lib (path-join out-dir "lib/libsigil-studio.a")) (bin-path (path-join out-dir "bin/sigil-studio-test"))) ;; Collect libsigil object files from bootstrap build (let ((sigil-objs (glob "../../build/boot/obj/libsigil/*.o")) (miniz-objs (glob "../../build/boot/obj/miniz/*.o"))) ;; Link everything together (display " LINK ") (display bin-path) (newline) (apply run-process ctx "gcc" "-o" bin-path test-obj studio-lib (append sigil-objs miniz-objs '("-lGL" "-lX11" "-lXi" "-lXcursor" "-lasound" "-lm" "-lpthread" "-ldl")))) ctx)))))) ;; Multimedia packages (from-workspace name: "sigil-app") (from-workspace name: "sigil-graphics") (from-workspace name: "sigil-audio")))src/c/app.cdeleted
/* * app.c - Sigil Studio Application Module * * Wraps sokol_app.h to provide windowing, input, and application lifecycle. */#include "studio-internal.h"#include "sigil-internal.h"/* Sokol headers (implementation is in sokol.c) */#include "sokol_app.h"#include "sokol_gfx.h"#include "sokol_glue.h"#include "sokol_gp.h"#include "sokol_time.h"#include "sokol_log.h"#include <stdio.h>#include <stdlib.h>#include <string.h>/* Global studio state */StudioState *g_studio = NULL;/* * Initialize studio state */void studio_state_init(SigilVM *vm){ if (g_studio) return; g_studio = calloc(1, sizeof(StudioState)); g_studio->vm = vm; g_studio->init_callback = SIGIL_FALSE; g_studio->frame_callback = SIGIL_FALSE; g_studio->cleanup_callback = SIGIL_FALSE;}void studio_state_shutdown(void){ if (g_studio) { free(g_studio); g_studio = NULL; }}/* * Clear per-frame input state */void studio_clear_frame_input(void){ if (!g_studio) return; memset(g_studio->keys_pressed, 0, sizeof(g_studio->keys_pressed)); memset(g_studio->keys_released, 0, sizeof(g_studio->keys_released)); memset(g_studio->mouse_pressed, 0, sizeof(g_studio->mouse_pressed)); memset(g_studio->mouse_released, 0, sizeof(g_studio->mouse_released));}/* * Convert Scheme key symbol to Sokol keycode */int studio_key_code_from_symbol(SigilVM *vm, Value sym){ (void)vm; if (!sigil_is_symbol(sym)) return SAPP_KEYCODE_INVALID; SigilSymbol *s = (SigilSymbol *)sigil_as_ptr(sym); const char *name = s->name; /* Letters */ if (s->length == 1 && name[0] >= 'a' && name[0] <= 'z') { return SAPP_KEYCODE_A + (name[0] - 'a'); } /* Numbers */ if (s->length == 1 && name[0] >= '0' && name[0] <= '9') { return SAPP_KEYCODE_0 + (name[0] - '0'); } /* Special keys */ if (strcmp(name, "space") == 0) return SAPP_KEYCODE_SPACE; if (strcmp(name, "return") == 0 || strcmp(name, "enter") == 0) return SAPP_KEYCODE_ENTER; if (strcmp(name, "escape") == 0 || strcmp(name, "esc") == 0) return SAPP_KEYCODE_ESCAPE; if (strcmp(name, "tab") == 0) return SAPP_KEYCODE_TAB; if (strcmp(name, "backspace") == 0) return SAPP_KEYCODE_BACKSPACE; /* Arrow keys */ if (strcmp(name, "left") == 0) return SAPP_KEYCODE_LEFT; if (strcmp(name, "right") == 0) return SAPP_KEYCODE_RIGHT; if (strcmp(name, "up") == 0) return SAPP_KEYCODE_UP; if (strcmp(name, "down") == 0) return SAPP_KEYCODE_DOWN; /* Modifiers */ if (strcmp(name, "shift") == 0) return SAPP_KEYCODE_LEFT_SHIFT; if (strcmp(name, "ctrl") == 0 || strcmp(name, "control") == 0) return SAPP_KEYCODE_LEFT_CONTROL; if (strcmp(name, "alt") == 0) return SAPP_KEYCODE_LEFT_ALT; return SAPP_KEYCODE_INVALID;}/* * Convert mouse button symbol to index */static int mouse_button_from_symbol(SigilVM *vm, Value sym){ (void)vm; if (!sigil_is_symbol(sym)) return -1; SigilSymbol *s = (SigilSymbol *)sigil_as_ptr(sym); const char *name = s->name; if (strcmp(name, "left") == 0) return 0; if (strcmp(name, "right") == 0) return 1; if (strcmp(name, "middle") == 0) return 2; return -1;}/* ============================================================ * SOKOL CALLBACKS * ============================================================ */static uint64_t last_time = 0;/* Check for VM errors and print them */static void check_vm_error(const char *context){ if (!g_studio) return; const char *err = sigil_error_message(g_studio->vm); if (err) { fprintf(stderr, "Scheme error in %s: %s\n", context, err); sigil_error_clear(g_studio->vm); sapp_request_quit(); }}static void app_init(void){ stm_setup(); last_time = stm_now(); /* Initialize graphics subsystem */ if (g_studio && !g_studio->gfx_initialized) { sg_desc desc = { .environment = sglue_environment(), }; sg_setup(&desc); /* Initialize sokol_gp for 2D rendering */ sgp_desc sgpdesc = {0}; sgp_setup(&sgpdesc); if (!sgp_is_valid()) { fprintf(stderr, "Failed to initialize sokol_gp\n"); } g_studio->gfx_initialized = true; } if (g_studio && !sigil_is_false(g_studio->init_callback)) { sigil_apply0(g_studio->vm, g_studio->init_callback); check_vm_error("init"); }}static void app_frame(void){ /* Calculate frame time */ uint64_t now = stm_now(); g_studio->frame_time = stm_sec(stm_diff(now, last_time)); g_studio->time_elapsed += g_studio->frame_time; last_time = now; /* Call Scheme frame callback with delta time */ if (g_studio && !sigil_is_false(g_studio->frame_callback)) { Value dt = sigil_flonum(g_studio->frame_time); sigil_apply1(g_studio->vm, g_studio->frame_callback, dt); check_vm_error("frame"); } /* Clear per-frame input state for next frame */ studio_clear_frame_input();}static void app_cleanup(void){ if (g_studio && !sigil_is_false(g_studio->cleanup_callback)) { sigil_apply0(g_studio->vm, g_studio->cleanup_callback); check_vm_error("cleanup"); } /* Shutdown graphics subsystem */ if (g_studio && g_studio->gfx_initialized) { sgp_shutdown(); sg_shutdown(); g_studio->gfx_initialized = false; }}static void app_event(const sapp_event *ev){ if (!g_studio) return; switch (ev->type) { case SAPP_EVENTTYPE_KEY_DOWN: if (ev->key_code < 512) { if (!g_studio->keys_down[ev->key_code]) { g_studio->keys_pressed[ev->key_code] = true; } g_studio->keys_down[ev->key_code] = true; } break; case SAPP_EVENTTYPE_KEY_UP: if (ev->key_code < 512) { g_studio->keys_down[ev->key_code] = false; g_studio->keys_released[ev->key_code] = true; } break; case SAPP_EVENTTYPE_MOUSE_DOWN: if (ev->mouse_button < 3) { if (!g_studio->mouse_buttons[ev->mouse_button]) { g_studio->mouse_pressed[ev->mouse_button] = true; } g_studio->mouse_buttons[ev->mouse_button] = true; } break; case SAPP_EVENTTYPE_MOUSE_UP: if (ev->mouse_button < 3) { g_studio->mouse_buttons[ev->mouse_button] = false; g_studio->mouse_released[ev->mouse_button] = true; } break; case SAPP_EVENTTYPE_MOUSE_MOVE: g_studio->mouse_x = ev->mouse_x; g_studio->mouse_y = ev->mouse_y; break; case SAPP_EVENTTYPE_QUIT_REQUESTED: g_studio->quit_requested = true; break; default: break; }}/* ============================================================ * NATIVE FUNCTIONS * ============================================================ *//* * (app-run init-proc frame-proc cleanup-proc [title] [width] [height]) * * Run the application main loop. * init-proc: called once at startup * frame-proc: called each frame with delta-time argument * cleanup-proc: called before shutdown */static Value native_app_run(SigilVM *vm, int argc, Value *args){ if (argc < 3) { sigil__vm_error(vm, SIGIL_ERR_ARITY, "app-run: requires init, frame, and cleanup procedures"); return SIGIL_UNDEFINED; } /* Initialize studio state */ studio_state_init(vm); /* Store callbacks */ g_studio->init_callback = args[0]; g_studio->frame_callback = args[1]; g_studio->cleanup_callback = args[2]; /* Parse optional arguments */ const char *title = "Sigil App"; int width = 800; int height = 600; if (argc > 3 && sigil_is_string(args[3])) { SigilString *s = (SigilString *)sigil_as_ptr(args[3]); title = s->data; } if (argc > 4 && sigil_is_fixnum(args[4])) { width = (int)sigil_as_fixnum(args[4]); } if (argc > 5 && sigil_is_fixnum(args[5])) { height = (int)sigil_as_fixnum(args[5]); } /* Configure and run Sokol app */ sapp_desc desc = { .init_cb = app_init, .frame_cb = app_frame, .cleanup_cb = app_cleanup, .event_cb = app_event, .width = width, .height = height, .window_title = title, .icon.sokol_default = true, .logger.func = slog_func, }; sapp_run(&desc); /* Cleanup */ studio_state_shutdown(); return SIGIL_NIL;}/* * (frame-width) -> integer */static Value native_frame_width(SigilVM *vm, int argc, Value *args){ (void)vm; (void)argc; (void)args; return sigil_fixnum(sapp_width());}/* * (frame-height) -> integer */static Value native_frame_height(SigilVM *vm, int argc, Value *args){ (void)vm; (void)argc; (void)args; return sigil_fixnum(sapp_height());}/* * (frame-time) -> float (seconds since last frame) */static Value native_frame_time(SigilVM *vm, int argc, Value *args){ (void)vm; (void)argc; (void)args; if (!g_studio) return sigil_flonum(0.0); return sigil_flonum(g_studio->frame_time);}/* * (time-elapsed) -> float (seconds since app start) */static Value native_time_elapsed(SigilVM *vm, int argc, Value *args){ (void)vm; (void)argc; (void)args; if (!g_studio) return sigil_flonum(0.0); return sigil_flonum(g_studio->time_elapsed);}/* * (mouse-x) -> float */static Value native_mouse_x(SigilVM *vm, int argc, Value *args){ (void)vm; (void)argc; (void)args; if (!g_studio) return sigil_flonum(0.0); return sigil_flonum(g_studio->mouse_x);}/* * (mouse-y) -> float */static Value native_mouse_y(SigilVM *vm, int argc, Value *args){ (void)vm; (void)argc; (void)args; if (!g_studio) return sigil_flonum(0.0); return sigil_flonum(g_studio->mouse_y);}/* * (mouse-down? button) -> boolean */static Value native_mouse_down(SigilVM *vm, int argc, Value *args){ if (argc < 1) return SIGIL_FALSE; int btn = mouse_button_from_symbol(vm, args[0]); if (btn < 0 || !g_studio) return SIGIL_FALSE; return g_studio->mouse_buttons[btn] ? SIGIL_TRUE : SIGIL_FALSE;}/* * (mouse-pressed? button) -> boolean */static Value native_mouse_pressed(SigilVM *vm, int argc, Value *args){ if (argc < 1) return SIGIL_FALSE; int btn = mouse_button_from_symbol(vm, args[0]); if (btn < 0 || !g_studio) return SIGIL_FALSE; return g_studio->mouse_pressed[btn] ? SIGIL_TRUE : SIGIL_FALSE;}/* * (mouse-released? button) -> boolean */static Value native_mouse_released(SigilVM *vm, int argc, Value *args){ if (argc < 1) return SIGIL_FALSE; int btn = mouse_button_from_symbol(vm, args[0]); if (btn < 0 || !g_studio) return SIGIL_FALSE; return g_studio->mouse_released[btn] ? SIGIL_TRUE : SIGIL_FALSE;}/* * (key-down? key) -> boolean */static Value native_key_down(SigilVM *vm, int argc, Value *args){ if (argc < 1) return SIGIL_FALSE; int key = studio_key_code_from_symbol(vm, args[0]); if (key == SAPP_KEYCODE_INVALID || !g_studio) return SIGIL_FALSE; return g_studio->keys_down[key] ? SIGIL_TRUE : SIGIL_FALSE;}/* * (key-pressed? key) -> boolean */static Value native_key_pressed(SigilVM *vm, int argc, Value *args){ if (argc < 1) return SIGIL_FALSE; int key = studio_key_code_from_symbol(vm, args[0]); if (key == SAPP_KEYCODE_INVALID || !g_studio) return SIGIL_FALSE; return g_studio->keys_pressed[key] ? SIGIL_TRUE : SIGIL_FALSE;}/* * (key-released? key) -> boolean */static Value native_key_released(SigilVM *vm, int argc, Value *args){ if (argc < 1) return SIGIL_FALSE; int key = studio_key_code_from_symbol(vm, args[0]); if (key == SAPP_KEYCODE_INVALID || !g_studio) return SIGIL_FALSE; return g_studio->keys_released[key] ? SIGIL_TRUE : SIGIL_FALSE;}/* * (quit-requested?) -> boolean */static Value native_quit_requested(SigilVM *vm, int argc, Value *args){ (void)vm; (void)argc; (void)args; if (!g_studio) return SIGIL_FALSE; return g_studio->quit_requested ? SIGIL_TRUE : SIGIL_FALSE;}/* * (request-quit) */static Value native_request_quit(SigilVM *vm, int argc, Value *args){ (void)vm; (void)argc; (void)args; sapp_request_quit(); return SIGIL_NIL;}/* ============================================================ * MODULE INITIALIZATION * ============================================================ */void sigil__init_sigil_studio_app_module(SigilVM *vm){ SigilModule *module = sigil_begin_module(vm, "(sigil studio app)"); if (!module) return; /* Application lifecycle */ sigil_module_register_native(vm, "app-run", native_app_run, SIGIL_ARITY_RANGE(3, 6), "Run application with init/frame/cleanup callbacks"); /* Frame info */ sigil_module_register_native(vm, "frame-width", native_frame_width, SIGIL_ARITY_EXACT(0), "Get frame buffer width"); sigil_module_register_native(vm, "frame-height", native_frame_height, SIGIL_ARITY_EXACT(0), "Get frame buffer height"); sigil_module_register_native(vm, "frame-time", native_frame_time, SIGIL_ARITY_EXACT(0), "Seconds since last frame"); sigil_module_register_native(vm, "time-elapsed", native_time_elapsed, SIGIL_ARITY_EXACT(0), "Seconds since app start"); /* Mouse input */ sigil_module_register_native(vm, "mouse-x", native_mouse_x, SIGIL_ARITY_EXACT(0), "Mouse X position"); sigil_module_register_native(vm, "mouse-y", native_mouse_y, SIGIL_ARITY_EXACT(0), "Mouse Y position"); sigil_module_register_native(vm, "mouse-down?", native_mouse_down, SIGIL_ARITY_EXACT(1), "Is mouse button held?"); sigil_module_register_native(vm, "mouse-pressed?", native_mouse_pressed, SIGIL_ARITY_EXACT(1), "Was mouse button just pressed?"); sigil_module_register_native(vm, "mouse-released?", native_mouse_released, SIGIL_ARITY_EXACT(1), "Was mouse button just released?"); /* Keyboard input */ sigil_module_register_native(vm, "key-down?", native_key_down, SIGIL_ARITY_EXACT(1), "Is key held?"); sigil_module_register_native(vm, "key-pressed?", native_key_pressed, SIGIL_ARITY_EXACT(1), "Was key just pressed?"); sigil_module_register_native(vm, "key-released?", native_key_released,Showing the first 500 of 527 diff lines for this file. This diff is INCOMPLETE; read the file or clone the repository for the rest.
src/c/audio.cdeleted
/* * audio.c - Sigil Studio Audio Module * * Wraps sokol_audio.h to provide audio playback capabilities. * Uses stb_vorbis for OGG decoding. * * Sound effects are loaded entirely into memory. * Music is streamed from disk via stb_vorbis. */#include "studio-internal.h"#include "sigil-internal.h"#include <stdio.h>#include <stdlib.h>#include <string.h>#include <math.h>/* Sokol headers (implementation is in sokol.c) */#include "sokol_audio.h"/* stb_vorbis for OGG decoding (implementation in stb_impl.c) */#include "stb_vorbis.c"/* ============================================================ * CONSTANTS * ============================================================ */#define MAX_SOUNDS 64#define MAX_PLAYING_SOUNDS 16#define STREAM_BUFFER_SAMPLES 4096/* ============================================================ * DATA STRUCTURES * ============================================================ *//* Sound effect - fully loaded into memory */typedef struct { float *samples; /* Interleaved stereo samples */ int num_samples; /* Total samples (frames * channels) */ int sample_rate; int channels;} StudioSound;/* Playing sound instance */typedef struct { StudioSound *sound; int position; /* Current playback position */ float volume; float pan; /* -1.0 left, 0.0 center, 1.0 right */ bool playing; bool loop;} PlayingSound;/* Music stream - decoded on the fly */typedef struct { stb_vorbis *vorbis; char *filepath; /* For reopening if looping */ float volume; bool playing; bool loop; bool paused;} MusicStream;/* ============================================================ * GLOBAL STATE * ============================================================ */static PlayingSound g_playing_sounds[MAX_PLAYING_SOUNDS];static MusicStream g_music = {0};static float g_master_volume = 1.0f;static bool g_muted = false;/* Type tags for foreign objects */static Value sound_type_tag = SIGIL_UNDEFINED;/* ============================================================ * AUDIO CALLBACK * ============================================================ */static void audio_callback(float *buffer, int num_frames, int num_channels){ /* Clear buffer */ memset(buffer, 0, num_frames * num_channels * sizeof(float)); if (g_muted) return; float master = g_master_volume; /* Mix playing sounds */ for (int i = 0; i < MAX_PLAYING_SOUNDS; i++) { PlayingSound *ps = &g_playing_sounds[i]; if (!ps->playing || !ps->sound) continue; StudioSound *snd = ps->sound; float vol = ps->volume * master; /* Calculate pan gains */ float pan = ps->pan; float left_gain = vol * (pan <= 0 ? 1.0f : 1.0f - pan); float right_gain = vol * (pan >= 0 ? 1.0f : 1.0f + pan); for (int f = 0; f < num_frames; f++) { if (ps->position >= snd->num_samples / snd->channels) { if (ps->loop) { ps->position = 0; } else { ps->playing = false; break; } } float left, right; if (snd->channels == 1) { /* Mono */ left = right = snd->samples[ps->position]; } else { /* Stereo */ left = snd->samples[ps->position * 2]; right = snd->samples[ps->position * 2 + 1]; } if (num_channels >= 2) { buffer[f * num_channels] += left * left_gain; buffer[f * num_channels + 1] += right * right_gain; } else { buffer[f] += (left + right) * 0.5f * vol; } ps->position++; } } /* Mix music stream */ if (g_music.playing && !g_music.paused && g_music.vorbis) { float vol = g_music.volume * master; float temp[STREAM_BUFFER_SAMPLES * 2]; int samples_needed = num_frames; int offset = 0; while (samples_needed > 0) { int to_decode = samples_needed < STREAM_BUFFER_SAMPLES ? samples_needed : STREAM_BUFFER_SAMPLES; int decoded = stb_vorbis_get_samples_float_interleaved( g_music.vorbis, 2, temp, to_decode * 2); if (decoded == 0) { /* End of file */ if (g_music.loop && g_music.filepath) { /* Reopen and continue */ stb_vorbis_close(g_music.vorbis); int error; g_music.vorbis = stb_vorbis_open_filename( g_music.filepath, &error, NULL); if (!g_music.vorbis) { g_music.playing = false; break; } continue; } else { g_music.playing = false; break; } } /* Mix decoded samples */ for (int f = 0; f < decoded; f++) { int buf_idx = (offset + f) * num_channels; if (num_channels >= 2) { buffer[buf_idx] += temp[f * 2] * vol; buffer[buf_idx + 1] += temp[f * 2 + 1] * vol; } else { buffer[buf_idx] += (temp[f * 2] + temp[f * 2 + 1]) * 0.5f * vol; } } samples_needed -= decoded; offset += decoded; } } /* Clamp output */ for (int i = 0; i < num_frames * num_channels; i++) { if (buffer[i] > 1.0f) buffer[i] = 1.0f; if (buffer[i] < -1.0f) buffer[i] = -1.0f; }}/* ============================================================ * HELPER FUNCTIONS * ============================================================ */static void ensure_sound_type(SigilVM *vm){ if (sigil_is_undefined(sound_type_tag)) { sound_type_tag = sigil_intern_symbol(vm, "sigil-studio-sound", 18); }}static StudioSound *get_sound(SigilVM *vm, Value v){ if (!sigil_is_foreign(v)) return NULL; ensure_sound_type(vm); if (sigil_foreign_type(v) != sound_type_tag) return NULL; return (StudioSound *)sigil_foreign_data(v);}static void sound_destructor(void *data){ StudioSound *snd = (StudioSound *)data; if (snd) { free(snd->samples); free(snd); }}static PlayingSound *find_free_slot(void){ for (int i = 0; i < MAX_PLAYING_SOUNDS; i++) { if (!g_playing_sounds[i].playing) { return &g_playing_sounds[i]; } } return NULL;}/* ============================================================ * NATIVE FUNCTIONS - SETUP * ============================================================ *//* * (audio-setup) - Initialize audio subsystem */static Value native_audio_setup(SigilVM *vm, int argc, Value *args){ (void)vm; (void)argc; (void)args; if (g_studio && g_studio->audio_initialized) { return SIGIL_NIL; } saudio_desc desc = { .stream_cb = audio_callback, .num_channels = 2, .sample_rate = 44100, .buffer_frames = 2048 }; saudio_setup(&desc); /* Clear playing sounds */ memset(g_playing_sounds, 0, sizeof(g_playing_sounds)); /* Clear music */ memset(&g_music, 0, sizeof(g_music)); g_music.volume = 1.0f; g_master_volume = 1.0f; g_muted = false; if (g_studio) { g_studio->audio_initialized = true; } return SIGIL_NIL;}/* * (audio-shutdown) - Shutdown audio subsystem */static Value native_audio_shutdown(SigilVM *vm, int argc, Value *args){ (void)vm; (void)argc; (void)args; if (g_studio && g_studio->audio_initialized) { /* Stop music */ if (g_music.vorbis) { stb_vorbis_close(g_music.vorbis); g_music.vorbis = NULL; } free(g_music.filepath); g_music.filepath = NULL; saudio_shutdown(); g_studio->audio_initialized = false; } return SIGIL_NIL;}/* * (audio-initialized?) -> boolean */static Value native_audio_initialized(SigilVM *vm, int argc, Value *args){ (void)vm; (void)argc; (void)args; if (!g_studio) return SIGIL_FALSE; return g_studio->audio_initialized ? SIGIL_TRUE : SIGIL_FALSE;}/* ============================================================ * NATIVE FUNCTIONS - SOUNDS * ============================================================ *//* * (load-sound path) -> <sound> or #f * * Load an OGG file entirely into memory. */static Value native_load_sound(SigilVM *vm, int argc, Value *args){ if (argc < 1 || !sigil_is_string(args[0])) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "load-sound: expected string path"); return SIGIL_FALSE; } SigilString *path_str = (SigilString *)sigil_as_ptr(args[0]); const char *path = path_str->data; int channels, sample_rate; short *raw_samples; int num_samples = stb_vorbis_decode_filename(path, &channels, &sample_rate, &raw_samples); if (num_samples < 0) { return SIGIL_FALSE; } /* Convert to float */ int total_samples = num_samples * channels; float *samples = malloc(total_samples * sizeof(float)); if (!samples) { free(raw_samples); return SIGIL_FALSE; } for (int i = 0; i < total_samples; i++) { samples[i] = raw_samples[i] / 32768.0f; } free(raw_samples); StudioSound *snd = malloc(sizeof(StudioSound)); if (!snd) { free(samples); return SIGIL_FALSE; } snd->samples = samples; snd->num_samples = total_samples; snd->sample_rate = sample_rate; snd->channels = channels; ensure_sound_type(vm); return sigil_make_foreign(vm, sound_type_tag, snd, sound_destructor, sizeof(StudioSound) + total_samples * sizeof(float));}/* * (sound? obj) -> boolean */static Value native_sound_p(SigilVM *vm, int argc, Value *args){ (void)argc; return get_sound(vm, args[0]) ? SIGIL_TRUE : SIGIL_FALSE;}/* * (play-sound sound [volume] [pan] [loop?]) -> boolean * * Play a sound effect. Returns #t if started, #f if no slots available. */static Value native_play_sound(SigilVM *vm, int argc, Value *args){ if (argc < 1) { sigil__vm_set_error(vm, SIGIL_ERR_ARITY, "play-sound: requires sound argument"); return SIGIL_FALSE; } StudioSound *snd = get_sound(vm, args[0]); if (!snd) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "play-sound: expected sound"); return SIGIL_FALSE; } PlayingSound *ps = find_free_slot(); if (!ps) { return SIGIL_FALSE; /* No slots available */ } ps->sound = snd; ps->position = 0; ps->volume = argc > 1 ? (float)sigil_as_flonum(args[1]) : 1.0f; ps->pan = argc > 2 ? (float)sigil_as_flonum(args[2]) : 0.0f; ps->loop = argc > 3 ? sigil_is_true(args[3]) : false; ps->playing = true; return SIGIL_TRUE;}/* * (stop-all-sounds) - Stop all playing sound effects */static Value native_stop_all_sounds(SigilVM *vm, int argc, Value *args){ (void)vm; (void)argc; (void)args; for (int i = 0; i < MAX_PLAYING_SOUNDS; i++) { g_playing_sounds[i].playing = false; } return SIGIL_NIL;}/* ============================================================ * NATIVE FUNCTIONS - MUSIC * ============================================================ *//* * (play-music path [loop?]) -> boolean * * Start streaming music from an OGG file. */static Value native_play_music(SigilVM *vm, int argc, Value *args){ if (argc < 1 || !sigil_is_string(args[0])) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "play-music: expected string path"); return SIGIL_FALSE; } /* Stop any existing music */ if (g_music.vorbis) { stb_vorbis_close(g_music.vorbis); g_music.vorbis = NULL; } free(g_music.filepath); g_music.filepath = NULL; SigilString *path_str = (SigilString *)sigil_as_ptr(args[0]); const char *path = path_str->data; int error; g_music.vorbis = stb_vorbis_open_filename(path, &error, NULL); if (!g_music.vorbis) { return SIGIL_FALSE; } g_music.filepath = strdup(path); g_music.loop = argc > 1 ? sigil_is_true(args[1]) : true; g_music.playing = true; g_music.paused = false; return SIGIL_TRUE;}/* * (stop-music) - Stop music playback */static Value native_stop_music(SigilVM *vm, int argc, Value *args){ (void)vm; (void)argc; (void)args; if (g_music.vorbis) { stb_vorbis_close(g_music.vorbis); g_music.vorbis = NULL; } free(g_music.filepath); g_music.filepath = NULL; g_music.playing = false; return SIGIL_NIL;}/* * (pause-music) - Pause music playback */static Value native_pause_music(SigilVM *vm, int argc, Value *args){ (void)vm; (void)argc; (void)args; g_music.paused = true; return SIGIL_NIL;}/* * (resume-music) - Resume music playback */static Value native_resume_music(SigilVM *vm, int argc, Value *args){ (void)vm; (void)argc; (void)args; g_music.paused = false; return SIGIL_NIL;}/* * (music-playing?) -> boolean */static Value native_music_playing(SigilVM *vm, int argc, Value *args){ (void)vm; (void)argc; (void)args; return (g_music.playing && !g_music.paused) ? SIGIL_TRUE : SIGIL_FALSE;}Showing the first 500 of 642 diff lines for this file. This diff is INCOMPLETE; read the file or clone the repository for the rest.
src/c/font.cdeleted
/* * font.c - Font loading and text rendering for Sigil Studio * * Provides TrueType font loading via stb_truetype and text rendering. * Fonts are rasterized to an atlas texture at a specified size. */#include "studio-internal.h"#include "sigil-internal.h"#include "sokol_gfx.h"#include "sokol_gp.h"#include "stb_truetype.h"#include <stdio.h>#include <stdlib.h>#include <string.h>/* Font type tag */static Value font_type_tag = SIGIL_UNDEFINED;/* ASCII printable range */#define FIRST_CHAR 32 /* space */#define LAST_CHAR 126 /* tilde */#define NUM_CHARS (LAST_CHAR - FIRST_CHAR + 1)/* Font structure */typedef struct { sg_image atlas; sg_view atlas_view; sg_sampler sampler; int atlas_width; int atlas_height; float font_size; float ascent; float descent; float line_gap; stbtt_bakedchar char_data[NUM_CHARS];} StudioFont;/* Initialize font type tag */static void ensure_font_type(SigilVM *vm){ if (sigil_is_undefined(font_type_tag)) { font_type_tag = sigil_intern_symbol(vm, "sigil-studio-font", 17); }}/* Get font from Value */static StudioFont *get_font(SigilVM *vm, Value v){ if (!sigil_is_foreign(v)) return NULL; ensure_font_type(vm); if (sigil_foreign_type(v) != font_type_tag) return NULL; return (StudioFont *)sigil_foreign_data(v);}/* Font destructor */static void font_destructor(void *data){ StudioFont *font = (StudioFont *)data; if (font) { if (font->atlas_view.id != SG_INVALID_ID) { sg_destroy_view(font->atlas_view); } if (font->atlas.id != SG_INVALID_ID) { sg_destroy_image(font->atlas); } if (font->sampler.id != SG_INVALID_ID) { sg_destroy_sampler(font->sampler); } free(font); }}/* Helper to read entire file */static unsigned char *read_file(const char *path, size_t *size_out){ FILE *f = fopen(path, "rb"); if (!f) return NULL; fseek(f, 0, SEEK_END); size_t size = (size_t)ftell(f); fseek(f, 0, SEEK_SET); unsigned char *data = malloc(size); if (!data) { fclose(f); return NULL; } if (fread(data, 1, size, f) != size) { free(data); fclose(f); return NULL; } fclose(f); if (size_out) *size_out = size; return data;}/* Helper to extract float from fixnum or flonum */static float value_to_float(Value v){ if (sigil_is_fixnum(v)) { return (float)sigil_as_fixnum(v); } else if (sigil_is_flonum(v)) { return (float)sigil_as_flonum(v); } return 0.0f;}/* * (load-font path size) -> <font> or #f * * Load a TrueType font and rasterize it at the given pixel size. */static Value native_load_font(SigilVM *vm, int argc, Value *args){ if (argc < 2) { sigil__vm_error(vm, SIGIL_ERR_ARITY, "load-font: requires path and size"); return SIGIL_UNDEFINED; } if (!sigil_is_string(args[0])) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "load-font: expected string path"); return SIGIL_FALSE; } SigilString *path_str = (SigilString *)sigil_as_ptr(args[0]); const char *path = path_str->data; float font_size = value_to_float(args[1]); if (font_size < 1.0f) font_size = 16.0f; /* Read font file */ size_t font_data_size; unsigned char *font_data = read_file(path, &font_data_size); if (!font_data) { return SIGIL_FALSE; } /* Calculate atlas size based on font size */ int atlas_width = 512; int atlas_height = 512; if (font_size > 32) { atlas_width = 1024; atlas_height = 1024; } /* Allocate atlas bitmap */ unsigned char *atlas_bitmap = calloc(1, (size_t)(atlas_width * atlas_height)); if (!atlas_bitmap) { free(font_data); sigil__vm_set_error(vm, SIGIL_ERR_MEMORY, "load-font: out of memory"); return SIGIL_FALSE; } /* Create font structure */ StudioFont *font = calloc(1, sizeof(StudioFont)); if (!font) { free(atlas_bitmap); free(font_data); sigil__vm_set_error(vm, SIGIL_ERR_MEMORY, "load-font: out of memory"); return SIGIL_FALSE; } /* Bake font to atlas */ int result = stbtt_BakeFontBitmap(font_data, 0, font_size, atlas_bitmap, atlas_width, atlas_height, FIRST_CHAR, NUM_CHARS, font->char_data); if (result <= 0) { free(font); free(atlas_bitmap); free(font_data); sigil__vm_set_error(vm, SIGIL_ERR_RUNTIME, "load-font: failed to bake font"); return SIGIL_FALSE; } /* Get font metrics */ stbtt_fontinfo info; if (stbtt_InitFont(&info, font_data, 0)) { float scale = stbtt_ScaleForPixelHeight(&info, font_size); int ascent, descent, line_gap; stbtt_GetFontVMetrics(&info, &ascent, &descent, &line_gap); font->ascent = (float)ascent * scale; font->descent = (float)descent * scale; font->line_gap = (float)line_gap * scale; } free(font_data); /* Convert 8-bit bitmap to RGBA for GPU */ unsigned char *rgba = malloc((size_t)(atlas_width * atlas_height * 4)); if (!rgba) { free(font); free(atlas_bitmap); sigil__vm_set_error(vm, SIGIL_ERR_MEMORY, "load-font: out of memory"); return SIGIL_FALSE; } for (int i = 0; i < atlas_width * atlas_height; i++) { rgba[i * 4 + 0] = 255; /* R */ rgba[i * 4 + 1] = 255; /* G */ rgba[i * 4 + 2] = 255; /* B */ rgba[i * 4 + 3] = atlas_bitmap[i]; /* A from grayscale */ } free(atlas_bitmap); /* Create GPU texture */ sg_image_desc img_desc = { .width = atlas_width, .height = atlas_height, .pixel_format = SG_PIXELFORMAT_RGBA8, .data.mip_levels[0] = { .ptr = rgba, .size = (size_t)(atlas_width * atlas_height * 4) } }; font->atlas = sg_make_image(&img_desc); free(rgba); if (font->atlas.id == SG_INVALID_ID) { free(font); sigil__vm_set_error(vm, SIGIL_ERR_RUNTIME, "load-font: failed to create atlas texture"); return SIGIL_FALSE; } /* Create view for sokol_gp */ font->atlas_view = sgp_make_texture_view_from_image(font->atlas, "font-atlas"); if (font->atlas_view.id == SG_INVALID_ID) { sg_destroy_image(font->atlas); free(font); sigil__vm_set_error(vm, SIGIL_ERR_RUNTIME, "load-font: failed to create texture view"); return SIGIL_FALSE; } /* Create sampler (linear filtering for smooth text) */ sg_sampler_desc smp_desc = { .min_filter = SG_FILTER_LINEAR, .mag_filter = SG_FILTER_LINEAR, .wrap_u = SG_WRAP_CLAMP_TO_EDGE, .wrap_v = SG_WRAP_CLAMP_TO_EDGE }; font->sampler = sg_make_sampler(&smp_desc); font->atlas_width = atlas_width; font->atlas_height = atlas_height; font->font_size = font_size; ensure_font_type(vm); return sigil_make_foreign(vm, font_type_tag, font, font_destructor, sizeof(StudioFont));}/* * (font? obj) -> boolean */static Value native_font_p(SigilVM *vm, int argc, Value *args){ (void)argc; return get_font(vm, args[0]) ? SIGIL_TRUE : SIGIL_FALSE;}/* * (font-size font) -> number */static Value native_font_size(SigilVM *vm, int argc, Value *args){ (void)argc; StudioFont *font = get_font(vm, args[0]); if (!font) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "font-size: expected font"); return SIGIL_UNDEFINED; } return sigil_flonum(font->font_size);}/* * (font-line-height font) -> number * * Returns the recommended line height (ascent - descent + line_gap). */static Value native_font_line_height(SigilVM *vm, int argc, Value *args){ (void)argc; StudioFont *font = get_font(vm, args[0]); if (!font) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "font-line-height: expected font"); return SIGIL_UNDEFINED; } float line_height = font->ascent - font->descent + font->line_gap; return sigil_flonum(line_height);}/* * (draw-text-char font char x y) -> number (advance width) * * Draw a single character at position. Returns the advance width for spacing. * This is useful for custom text effects like wavy text. */static Value native_draw_text_char(SigilVM *vm, int argc, Value *args){ if (argc < 4) { sigil__vm_error(vm, SIGIL_ERR_ARITY, "draw-text-char: requires font, char, x, y"); return SIGIL_UNDEFINED; } StudioFont *font = get_font(vm, args[0]); if (!font) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "draw-text-char: expected font"); return SIGIL_UNDEFINED; } /* Get character - accept either a char or single-char string */ int c; if (sigil_is_char(args[1])) { c = (unsigned char)sigil_as_char(args[1]); } else if (sigil_is_string(args[1])) { SigilString *s = (SigilString *)sigil_as_ptr(args[1]); if (s->byte_length == 0) return sigil_flonum(0.0); c = (unsigned char)s->data[0]; } else { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "draw-text-char: expected char or string"); return SIGIL_UNDEFINED; } float x = value_to_float(args[2]); float y = value_to_float(args[3]); /* Handle characters outside our range */ if (c < FIRST_CHAR || c > LAST_CHAR) { return sigil_flonum(0.0); } stbtt_bakedchar *bc = &font->char_data[c - FIRST_CHAR]; /* Bind font atlas */ sgp_set_view(0, font->atlas_view); sgp_set_sampler(0, font->sampler); sgp_set_blend_mode(SGP_BLENDMODE_BLEND); /* Destination rectangle */ float dx = x + bc->xoff; float dy = y + bc->yoff; float dw = bc->x1 - bc->x0; float dh = bc->y1 - bc->y0; /* Source rectangle in atlas */ float sx = (float)bc->x0; float sy = (float)bc->y0; float sw = (float)(bc->x1 - bc->x0); float sh = (float)(bc->y1 - bc->y0); sgp_rect dest = {dx, dy, dw, dh}; sgp_rect src = {sx, sy, sw, sh}; sgp_draw_textured_rect(0, dest, src); /* Reset state */ sgp_reset_view(0); sgp_reset_sampler(0); return sigil_flonum(bc->xadvance);}/* * (draw-text font text x y) -> void * * Draw text at the given position. x,y is the baseline start position. */static Value native_draw_text(SigilVM *vm, int argc, Value *args){ if (argc < 4) { sigil__vm_error(vm, SIGIL_ERR_ARITY, "draw-text: requires font, text, x, y"); return SIGIL_UNDEFINED; } StudioFont *font = get_font(vm, args[0]); if (!font) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "draw-text: expected font"); return SIGIL_UNDEFINED; } if (!sigil_is_string(args[1])) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "draw-text: expected string"); return SIGIL_UNDEFINED; } SigilString *text_str = (SigilString *)sigil_as_ptr(args[1]); const char *text = text_str->data; float x = value_to_float(args[2]); float y = value_to_float(args[3]); /* Bind font atlas */ sgp_set_view(0, font->atlas_view); sgp_set_sampler(0, font->sampler); /* Enable blending for text */ sgp_set_blend_mode(SGP_BLENDMODE_BLEND); /* Draw each character */ float cursor_x = x; for (const char *p = text; *p; p++) { int c = (unsigned char)*p; /* Skip characters outside our range */ if (c < FIRST_CHAR || c > LAST_CHAR) { if (c == '\n') { cursor_x = x; y += font->ascent - font->descent + font->line_gap; } continue; } stbtt_bakedchar *bc = &font->char_data[c - FIRST_CHAR]; /* Destination rectangle */ float dx = cursor_x + bc->xoff; float dy = y + bc->yoff; float dw = bc->x1 - bc->x0; float dh = bc->y1 - bc->y0; /* Source rectangle in atlas (in pixels) */ float sx = (float)bc->x0; float sy = (float)bc->y0; float sw = (float)(bc->x1 - bc->x0); float sh = (float)(bc->y1 - bc->y0); sgp_rect dest = {dx, dy, dw, dh}; sgp_rect src = {sx, sy, sw, sh}; sgp_draw_textured_rect(0, dest, src); cursor_x += bc->xadvance; } /* Reset state */ sgp_reset_view(0); sgp_reset_sampler(0); return SIGIL_NIL;}/* * (text-width font text) -> number * * Calculate the width of text in pixels. */static Value native_text_width(SigilVM *vm, int argc, Value *args){ if (argc < 2) { sigil__vm_error(vm, SIGIL_ERR_ARITY, "text-width: requires font and text"); return SIGIL_UNDEFINED; } StudioFont *font = get_font(vm, args[0]); if (!font) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "text-width: expected font"); return SIGIL_UNDEFINED; } if (!sigil_is_string(args[1])) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "text-width: expected string"); return SIGIL_UNDEFINED; } SigilString *text_str = (SigilString *)sigil_as_ptr(args[1]); const char *text = text_str->data; float width = 0.0f; for (const char *p = text; *p; p++) { int c = (unsigned char)*p; if (c >= FIRST_CHAR && c <= LAST_CHAR) { width += font->char_data[c - FIRST_CHAR].xadvance; } } return sigil_flonum(width);}/* * Module initialization */void sigil__init_sigil_studio_font_module(SigilVM *vm){ SigilModule *module = sigil_begin_module(vm, "(sigil studio font)"); if (!module) return; sigil_module_register_native(vm, "load-font", native_load_font, SIGIL_ARITY_EXACT(2), "Load TrueType font at size"); sigil_module_register_native(vm, "font?", native_font_p, SIGIL_ARITY_EXACT(1), "Check if object is a font"); sigil_module_register_native(vm, "font-size", native_font_size, SIGIL_ARITY_EXACT(1), "Get font pixel size"); sigil_module_register_native(vm, "font-line-height", native_font_line_height, SIGIL_ARITY_EXACT(1), "Get recommended line height"); sigil_module_register_native(vm, "draw-text", native_draw_text, SIGIL_ARITY_EXACT(4), "Draw text at position"); sigil_module_register_native(vm, "draw-text-char", native_draw_text_char,Showing the first 500 of 514 diff lines for this file. This diff is INCOMPLETE; read the file or clone the repository for the rest.
src/c/graphics.cdeleted
/* * graphics.c - Sigil Studio Graphics Module * * Wraps sokol_gfx.h to provide 2D/3D rendering capabilities. */#include "studio-internal.h"#include "sigil-internal.h"/* Sokol headers (implementation is in sokol.c) */#include "sokol_app.h"#include "sokol_gfx.h"#include "sokol_glue.h"#include "sokol_gp.h"#include <stdio.h>#include <stdlib.h>/* External: get pixel data from image (defined in image.c) */extern unsigned char *sigil_studio_image_pixels(SigilVM *vm, Value img_val, int *width, int *height);/* Texture type tag (initialized at module init) */static Value texture_type_tag = SIGIL_UNDEFINED;/* Texture structure */typedef struct { sg_image handle; sg_sampler sampler; sg_view view; int width; int height;} StudioTexture;/* Current draw color */static float draw_color[4] = {1.0f, 1.0f, 1.0f, 1.0f};/* Clear color (set by clear, used in end-frame) */static float clear_color[4] = {0.0f, 0.0f, 0.0f, 1.0f};/* SGP initialized flag */static bool sgp_initialized = false;/* Virtual viewport state */static bool virtual_viewport_enabled = false;static int virtual_width = 0;static int virtual_height = 0;/* Letterbox color (bars outside viewport) */static float letterbox_color[4] = {0.0f, 0.0f, 0.0f, 1.0f};/* Helper to extract float from fixnum or flonum */static float value_to_float(Value v){ if (sigil_is_fixnum(v)) { return (float)sigil_as_fixnum(v); } else if (sigil_is_flonum(v)) { return (float)sigil_as_flonum(v); } return 0.0f;}/* ============================================================ * NATIVE FUNCTIONS * ============================================================ *//* * (gfx-setup) - Initialize graphics subsystem * Called automatically by app-run, but can be called manually. */static Value native_gfx_setup(SigilVM *vm, int argc, Value *args){ (void)vm; (void)argc; (void)args; if (g_studio && g_studio->gfx_initialized) { return SIGIL_NIL; } /* Initialize sokol_gfx */ sg_desc desc = { .environment = sglue_environment(), }; sg_setup(&desc); /* Initialize sokol_gp for 2D rendering */ sgp_desc sgpdesc = {0}; sgp_setup(&sgpdesc); if (!sgp_is_valid()) { fprintf(stderr, "Failed to initialize sokol_gp\n"); sg_shutdown(); return SIGIL_FALSE; } sgp_initialized = true; if (g_studio) { g_studio->gfx_initialized = true; } return SIGIL_NIL;}/* * (gfx-shutdown) - Shutdown graphics subsystem */static Value native_gfx_shutdown(SigilVM *vm, int argc, Value *args){ (void)vm; (void)argc; (void)args; if (g_studio && g_studio->gfx_initialized) { if (sgp_initialized) { sgp_shutdown(); sgp_initialized = false; } sg_shutdown(); g_studio->gfx_initialized = false; } return SIGIL_NIL;}/* * (set-letterbox-color r g b [a]) - Set the color for letterbox bars * * Default is black. Only visible when using a virtual viewport. */static Value native_set_letterbox_color(SigilVM *vm, int argc, Value *args){ if (argc < 3) { sigil__vm_error(vm, SIGIL_ERR_ARITY, "set-letterbox-color: requires r, g, b arguments"); return SIGIL_UNDEFINED; } letterbox_color[0] = value_to_float(args[0]); letterbox_color[1] = value_to_float(args[1]); letterbox_color[2] = value_to_float(args[2]); letterbox_color[3] = argc > 3 ? value_to_float(args[3]) : 1.0f; return SIGIL_NIL;}/* * (set-viewport width height) - Set virtual viewport with letterboxing * * Creates a fixed coordinate space that maintains aspect ratio. * Black bars are added as needed to fill the window. * Call with #f to disable and use window coordinates. */static Value native_set_viewport(SigilVM *vm, int argc, Value *args){ (void)vm; if (argc == 1 && sigil_is_false(args[0])) { /* Disable virtual viewport */ virtual_viewport_enabled = false; return SIGIL_NIL; } if (argc < 2) { sigil__vm_error(vm, SIGIL_ERR_ARITY, "set-viewport: requires width, height"); return SIGIL_UNDEFINED; } virtual_viewport_enabled = true; virtual_width = (int)value_to_float(args[0]); virtual_height = (int)value_to_float(args[1]); return SIGIL_NIL;}/* * (begin-frame) - Begin a new frame */static Value native_begin_frame(SigilVM *vm, int argc, Value *args){ (void)vm; (void)argc; (void)args; int window_w = sapp_width(); int window_h = sapp_height(); /* Begin sokol_gp frame with full window size */ sgp_begin(window_w, window_h); if (virtual_viewport_enabled && virtual_width > 0 && virtual_height > 0) { /* Calculate letterbox viewport */ float scale_x = (float)window_w / (float)virtual_width; float scale_y = (float)window_h / (float)virtual_height; float scale = (scale_x < scale_y) ? scale_x : scale_y; int viewport_w = (int)(virtual_width * scale); int viewport_h = (int)(virtual_height * scale); int viewport_x = (window_w - viewport_w) / 2; int viewport_y = (window_h - viewport_h) / 2; sgp_viewport(viewport_x, viewport_y, viewport_w, viewport_h); sgp_project(0, (float)virtual_width, 0, (float)virtual_height); } else { /* Default: use window coordinates */ sgp_viewport(0, 0, window_w, window_h); sgp_project(0, (float)window_w, 0, (float)window_h); } /* Reset to white draw color */ sgp_set_color(1.0f, 1.0f, 1.0f, 1.0f); return SIGIL_NIL;}/* * (end-frame) - End the current frame */static Value native_end_frame(SigilVM *vm, int argc, Value *args){ (void)vm; (void)argc; (void)args; /* Begin render pass - clear to letterbox color */ sg_pass_action pass_action = { .colors[0] = { .load_action = SG_LOADACTION_CLEAR, .clear_value = {letterbox_color[0], letterbox_color[1], letterbox_color[2], letterbox_color[3]} } }; sg_pass pass = { .action = pass_action, .swapchain = sglue_swapchain() }; sg_begin_pass(&pass); /* Flush sokol_gp commands to GPU */ sgp_flush(); sgp_end(); sg_end_pass(); sg_commit(); return SIGIL_NIL;}/* * (clear-screen r g b [a]) - Clear the viewport with a color * * When using a virtual viewport, this fills the viewport area. * The letterbox bars remain the pass clear color (black). */static Value native_clear_screen(SigilVM *vm, int argc, Value *args){ if (argc < 3) { sigil__vm_error(vm, SIGIL_ERR_ARITY, "clear-screen: requires r, g, b arguments"); return SIGIL_UNDEFINED; } float r = value_to_float(args[0]); float g = value_to_float(args[1]); float b = value_to_float(args[2]); float a = argc > 3 ? value_to_float(args[3]) : 1.0f; /* Draw a filled rectangle covering the entire viewport/projection area */ sgp_set_color(r, g, b, a); if (virtual_viewport_enabled && virtual_width > 0 && virtual_height > 0) { sgp_draw_filled_rect(0, 0, (float)virtual_width, (float)virtual_height); } else { sgp_draw_filled_rect(0, 0, (float)sapp_width(), (float)sapp_height()); } /* Reset to white for subsequent drawing */ sgp_set_color(1.0f, 1.0f, 1.0f, 1.0f); return SIGIL_NIL;}/* * (set-color r g b [a]) - Set current draw color */static Value native_set_color(SigilVM *vm, int argc, Value *args){ if (argc < 3) { sigil__vm_error(vm, SIGIL_ERR_ARITY, "set-color: requires r, g, b arguments"); return SIGIL_UNDEFINED; } draw_color[0] = value_to_float(args[0]); draw_color[1] = value_to_float(args[1]); draw_color[2] = value_to_float(args[2]); draw_color[3] = argc > 3 ? value_to_float(args[3]) : 1.0f; /* Set sokol_gp color */ sgp_set_color(draw_color[0], draw_color[1], draw_color[2], draw_color[3]); return SIGIL_NIL;}/* * (draw-filled-rect x y w h) - Draw a filled rectangle */static Value native_draw_filled_rect(SigilVM *vm, int argc, Value *args){ if (argc < 4) { sigil__vm_error(vm, SIGIL_ERR_ARITY, "draw-filled-rect: requires x, y, w, h arguments"); return SIGIL_UNDEFINED; } float x = value_to_float(args[0]); float y = value_to_float(args[1]); float w = value_to_float(args[2]); float h = value_to_float(args[3]); sgp_draw_filled_rect(x, y, w, h); return SIGIL_NIL;}/* * (draw-rect x y w h) - Draw a rectangle outline */static Value native_draw_rect(SigilVM *vm, int argc, Value *args){ if (argc < 4) { sigil__vm_error(vm, SIGIL_ERR_ARITY, "draw-rect: requires x, y, w, h arguments"); return SIGIL_UNDEFINED; } float x = value_to_float(args[0]); float y = value_to_float(args[1]); float w = value_to_float(args[2]); float h = value_to_float(args[3]); /* Draw rectangle outline using 4 lines */ sgp_line lines[4] = { {{x, y}, {x + w, y}}, /* top */ {{x + w, y}, {x + w, y + h}}, /* right */ {{x + w, y + h}, {x, y + h}}, /* bottom */ {{x, y + h}, {x, y}} /* left */ }; sgp_draw_lines(lines, 4); return SIGIL_NIL;}/* * (draw-line x1 y1 x2 y2) - Draw a line */static Value native_draw_line(SigilVM *vm, int argc, Value *args){ if (argc < 4) { sigil__vm_error(vm, SIGIL_ERR_ARITY, "draw-line: requires x1, y1, x2, y2 arguments"); return SIGIL_UNDEFINED; } float x1 = value_to_float(args[0]); float y1 = value_to_float(args[1]); float x2 = value_to_float(args[2]); float y2 = value_to_float(args[3]); sgp_line line = {{x1, y1}, {x2, y2}}; sgp_draw_lines(&line, 1); return SIGIL_NIL;}/* * (draw-point x y) - Draw a single point */static Value native_draw_point(SigilVM *vm, int argc, Value *args){ if (argc < 2) { sigil__vm_error(vm, SIGIL_ERR_ARITY, "draw-point: requires x, y arguments"); return SIGIL_UNDEFINED; } float x = value_to_float(args[0]); float y = value_to_float(args[1]); sgp_point pt = {x, y}; sgp_draw_points(&pt, 1); return SIGIL_NIL;}/* * (draw-triangle x1 y1 x2 y2 x3 y3) - Draw a triangle outline */static Value native_draw_triangle(SigilVM *vm, int argc, Value *args){ if (argc < 6) { sigil__vm_error(vm, SIGIL_ERR_ARITY, "draw-triangle: requires x1, y1, x2, y2, x3, y3"); return SIGIL_UNDEFINED; } float x1 = value_to_float(args[0]); float y1 = value_to_float(args[1]); float x2 = value_to_float(args[2]); float y2 = value_to_float(args[3]); float x3 = value_to_float(args[4]); float y3 = value_to_float(args[5]); sgp_line lines[3] = { {{x1, y1}, {x2, y2}}, {{x2, y2}, {x3, y3}}, {{x3, y3}, {x1, y1}} }; sgp_draw_lines(lines, 3); return SIGIL_NIL;}/* * (fill-triangle x1 y1 x2 y2 x3 y3) - Draw a filled triangle */static Value native_fill_triangle(SigilVM *vm, int argc, Value *args){ if (argc < 6) { sigil__vm_error(vm, SIGIL_ERR_ARITY, "fill-triangle: requires x1, y1, x2, y2, x3, y3"); return SIGIL_UNDEFINED; } float x1 = value_to_float(args[0]); float y1 = value_to_float(args[1]); float x2 = value_to_float(args[2]); float y2 = value_to_float(args[3]); float x3 = value_to_float(args[4]); float y3 = value_to_float(args[5]); sgp_triangle tri = {{x1, y1}, {x2, y2}, {x3, y3}}; sgp_draw_filled_triangles(&tri, 1); return SIGIL_NIL;}/* ============================================================ * TRANSFORM STACK * ============================================================ *//* * (push-transform) - Save current transform state */static Value native_push_transform(SigilVM *vm, int argc, Value *args){ (void)vm; (void)argc; (void)args; sgp_push_transform(); return SIGIL_NIL;}/* * (pop-transform) - Restore previous transform state */static Value native_pop_transform(SigilVM *vm, int argc, Value *args){ (void)vm; (void)argc; (void)args; sgp_pop_transform(); return SIGIL_NIL;}/* * (reset-transform) - Reset to identity transform */static Value native_reset_transform(SigilVM *vm, int argc, Value *args){ (void)vm; (void)argc; (void)args; sgp_reset_transform(); return SIGIL_NIL;}/* * (translate x y) - Translate by (x, y) */static Value native_translate(SigilVM *vm, int argc, Value *args){ if (argc < 2) { sigil__vm_error(vm, SIGIL_ERR_ARITY, "translate: requires x, y arguments"); return SIGIL_UNDEFINED; } float x = value_to_float(args[0]); float y = value_to_float(args[1]); sgp_translate(x, y); return SIGIL_NIL;}/* * (rotate angle) - Rotate by angle (in radians) */static Value native_rotate(SigilVM *vm, int argc, Value *args){ if (argc < 1) { sigil__vm_error(vm, SIGIL_ERR_ARITY, "rotate: requires angle argument"); return SIGIL_UNDEFINED; } float angle = value_to_float(args[0]); sgp_rotate(angle); return SIGIL_NIL;}/* * (rotate-at angle x y) - Rotate around point (x, y) */static Value native_rotate_at(SigilVM *vm, int argc, Value *args)Showing the first 500 of 910 diff lines for this file. This diff is INCOMPLETE; read the file or clone the repository for the rest.
src/c/image.cdeleted
/* * image.c - CPU-side image loading for Sigil Studio * * Provides image loading using stb_image. Images are loaded into CPU memory * and can be inspected or converted to GPU textures via (sigil studio graphics). */#include "sigil-internal.h"#include <stdio.h>#include <stdlib.h>#include <string.h>#include "stb_image.h"/* Image type tag (initialized at module init) */static Value image_type_tag = SIGIL_UNDEFINED;/* Image structure stored as foreign object */typedef struct { unsigned char *pixels; /* RGBA pixel data */ int width; int height; int channels; /* Always 4 (RGBA) after loading */} StudioImage;/* Initialize image type tag */static void ensure_image_type(SigilVM *vm){ if (sigil_is_undefined(image_type_tag)) { image_type_tag = sigil_intern_symbol(vm, "sigil-studio-image", 18); }}/* Get image from Value, returns NULL if not an image */static StudioImage *get_image(SigilVM *vm, Value v){ if (!sigil_is_foreign(v)) return NULL; ensure_image_type(vm); if (sigil_foreign_type(v) != image_type_tag) return NULL; return (StudioImage *)sigil_foreign_data(v);}/* Destructor for image foreign object */static void image_destructor(void *data){ StudioImage *img = (StudioImage *)data; if (img) { if (img->pixels) { stbi_image_free(img->pixels); } free(img); }}/* * (load-image path) -> <image> or #f * * Load an image file from disk. Returns an image object or #f on failure. * Supported formats: PNG, JPEG, BMP, TGA, GIF, HDR, PSD, PIC, PNM */static Value native_load_image(SigilVM *vm, int argc, Value *args){ (void)argc; if (!sigil_is_string(args[0])) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "load-image: expected string path"); return SIGIL_FALSE; } SigilString *path_str = (SigilString *)sigil_as_ptr(args[0]); const char *path = path_str->data; int width, height, channels; /* Always request 4 channels (RGBA) for consistency */ unsigned char *pixels = stbi_load(path, &width, &height, &channels, 4); if (!pixels) { /* Return #f on failure - don't set error, let caller handle it */ return SIGIL_FALSE; } /* Create image structure */ StudioImage *img = malloc(sizeof(StudioImage)); if (!img) { stbi_image_free(pixels); sigil__vm_set_error(vm, SIGIL_ERR_MEMORY, "load-image: out of memory"); return SIGIL_FALSE; } img->pixels = pixels; img->width = width; img->height = height; img->channels = 4; /* We always load as RGBA */ /* Wrap in foreign object */ ensure_image_type(vm); return sigil_make_foreign(vm, image_type_tag, img, image_destructor, sizeof(StudioImage) + (size_t)(width * height * 4));}/* * (image? obj) -> boolean * * Check if object is an image. */static Value native_image_p(SigilVM *vm, int argc, Value *args){ (void)argc; return get_image(vm, args[0]) ? SIGIL_TRUE : SIGIL_FALSE;}/* * (image-width img) -> integer * * Get image width in pixels. */static Value native_image_width(SigilVM *vm, int argc, Value *args){ (void)argc; StudioImage *img = get_image(vm, args[0]); if (!img) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "image-width: expected image"); return SIGIL_UNDEFINED; } return sigil_fixnum(img->width);}/* * (image-height img) -> integer * * Get image height in pixels. */static Value native_image_height(SigilVM *vm, int argc, Value *args){ (void)argc; StudioImage *img = get_image(vm, args[0]); if (!img) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "image-height: expected image"); return SIGIL_UNDEFINED; } return sigil_fixnum(img->height);}/* * (image-channels img) -> integer * * Get number of channels (always 4 for RGBA). */static Value native_image_channels(SigilVM *vm, int argc, Value *args){ (void)argc; StudioImage *img = get_image(vm, args[0]); if (!img) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "image-channels: expected image"); return SIGIL_UNDEFINED; } return sigil_fixnum(img->channels);}/* * (image-free! img) -> void * * Free the CPU-side pixel data. The image object becomes invalid. * This is optional - GC will clean up automatically, but this allows * explicit memory management for large images. */static Value native_image_free(SigilVM *vm, int argc, Value *args){ (void)argc; StudioImage *img = get_image(vm, args[0]); if (!img) { sigil__vm_set_error(vm, SIGIL_ERR_TYPE, "image-free!: expected image"); return SIGIL_UNDEFINED; } if (img->pixels) { stbi_image_free(img->pixels); img->pixels = NULL; img->width = 0; img->height = 0; } return SIGIL_NIL;}/* * Internal: Get pixel data pointer for use by graphics module. * Returns NULL if image is invalid or freed. */unsigned char *sigil_studio_image_pixels(SigilVM *vm, Value img_val, int *width, int *height){ StudioImage *img = get_image(vm, img_val); if (!img || !img->pixels) return NULL; if (width) *width = img->width; if (height) *height = img->height; return img->pixels;}/* * Module initialization */void sigil__init_sigil_studio_image_module(SigilVM *vm){ SigilModule *module = sigil_begin_module(vm, "(sigil studio image)"); if (!module) return; sigil_module_register_native(vm, "load-image", native_load_image, SIGIL_ARITY_EXACT(1), "Load image from file path"); sigil_module_register_native(vm, "image?", native_image_p, SIGIL_ARITY_EXACT(1), "Check if object is an image"); sigil_module_register_native(vm, "image-width", native_image_width, SIGIL_ARITY_EXACT(1), "Get image width"); sigil_module_register_native(vm, "image-height", native_image_height, SIGIL_ARITY_EXACT(1), "Get image height"); sigil_module_register_native(vm, "image-channels", native_image_channels, SIGIL_ARITY_EXACT(1), "Get number of channels"); sigil_module_register_native(vm, "image-free!", native_image_free, SIGIL_ARITY_EXACT(1), "Free image pixel data"); sigil_module_export(vm, "load-image"); sigil_module_export(vm, "image?"); sigil_module_export(vm, "image-width"); sigil_module_export(vm, "image-height"); sigil_module_export(vm, "image-channels"); sigil_module_export(vm, "image-free!"); sigil_end_module(vm);}src/c/sokol.cdeleted
/* * sokol.c - Sokol Implementation File * * All Sokol implementations must be in a single translation unit. * This file includes all Sokol headers with SOKOL_IMPL defined. */#define SOKOL_IMPL/* SOKOL_GLCORE is defined via compiler flags *//* Order matters: gfx before glue */#include "sokol_app.h"#include "sokol_gfx.h"#include "sokol_glue.h"#include "sokol_gp.h"#include "sokol_audio.h"#include "sokol_time.h"#include "sokol_log.h"src/c/stb_impl.cdeleted
/* * stb_impl.c - STB library implementations * * This file contains the implementations of STB single-header libraries. * Compile this once and link with other modules. */#define STB_IMAGE_IMPLEMENTATION#include "stb_image.h"#define STB_TRUETYPE_IMPLEMENTATION#include "stb_truetype.h"src/c/studio-internal.hdeleted
/* * studio-internal.h - Internal header for Sigil Studio native code * * Shared state and utilities for app, graphics, and audio modules. */#ifndef SIGIL_STUDIO_INTERNAL_H#define SIGIL_STUDIO_INTERNAL_H#include <sigil/sigil.h>#include <stdbool.h>/* * Application state - shared between modules */typedef struct { SigilVM *vm; /* Callbacks from Scheme */ Value init_callback; Value frame_callback; Value cleanup_callback; /* Frame timing */ double frame_time; /* Seconds since last frame */ double time_elapsed; /* Seconds since app start */ /* Input state - current frame */ bool keys_down[512]; bool keys_pressed[512]; bool keys_released[512]; bool mouse_buttons[3]; bool mouse_pressed[3]; bool mouse_released[3]; float mouse_x; float mouse_y; /* Quit handling */ bool quit_requested; /* Initialization flags */ bool gfx_initialized; bool audio_initialized;} StudioState;/* Global state - initialized by app module */extern StudioState *g_studio;/* Initialize/shutdown studio state */void studio_state_init(SigilVM *vm);void studio_state_shutdown(void);/* Input handling helpers */void studio_clear_frame_input(void);int studio_key_code_from_symbol(SigilVM *vm, Value sym);#endif /* SIGIL_STUDIO_INTERNAL_H */src/sigil/studio/app.sgldeleted
;;; (sigil studio app) - Application Framework;;;;;; Provides windowing, input handling, and application lifecycle management.;;; Native functions are registered by app.c, this module provides re-exports;;; and higher-level utilities.;;;;;; The frame-loop macro provides a coroutine-based game loop where Scheme;;; appears to own the main loop while C (Sokol) actually controls execution.(define-library (sigil studio app) (import (sigil core) (sigil coroutines)) (export ;; Native functions (re-exported) app-run frame-width frame-height frame-time time-elapsed mouse-x mouse-y mouse-down? mouse-pressed? mouse-released? key-down? key-pressed? key-released? quit-requested? request-quit ;; Cooperative frame scheduling run-game wait-frame) (begin ;; Native functions are automatically available after native module init. ;; ========== Cooperative Frame Scheduler ========== ;;; ;;; The frame-loop pattern inverts control so Scheme code reads naturally: ;;; ;;; (run-game "My Game" 800 600 ;;; (lambda () ;;; ;; init code here ;;; (let loop () ;;; (let ((dt (wait-frame))) ;;; ;; frame code here using dt ;;; (unless (quit-requested?) ;;; (loop)))))) ;;; ;;; Behind the scenes, (wait-frame) yields to C, which resumes the ;;; coroutine on the next frame with the delta time. ;; Global coroutine for the current game loop (define *game-coroutine* #f) ;; Yield point - returns delta time when resumed (define (wait-frame) (send 'frame-complete)) ;; Internal frame callback - resumes the game coroutine (define (frame-tick dt) (when *game-coroutine* (if (coroutine-done? *game-coroutine*) (request-quit) (coroutine-send *game-coroutine* dt)))) ;; Stored game thunk - will be converted to coroutine in init callback (define *game-thunk* #f) ;; Internal init callback - creates and starts the game coroutine (define (game-init) (when *game-thunk* ;; Create the game coroutine now that Sokol is initialized (set! *game-coroutine* (coroutine ;; Call the user's game thunk (*game-thunk*) ;; When thunk returns, game is done 'game-finished)) ;; Run init code up to first (wait-frame) (when (not (coroutine-done? *game-coroutine*)) (coroutine-send *game-coroutine* 0.0)))) ;; Run a game with the coroutine-based loop ;; game-thunk is called inside a coroutine and can use (wait-frame) (define (run-game title width height game-thunk) ;; Store the thunk for the init callback (set! *game-thunk* game-thunk) ;; Run the app - init callback will start the coroutine (app-run game-init ; init - creates and starts coroutine frame-tick ; frame - resumes coroutine (lambda () ; cleanup (set! *game-coroutine* #f) (set! *game-thunk* #f)) title width height))))src/sigil/studio/audio.sgldeleted
;;; (sigil studio audio) - Audio Module;;;;;; Provides audio playback capabilities via sokol_audio.;;; Uses stb_vorbis for OGG audio decoding.;;;;;; Sound effects are loaded entirely into memory for low-latency playback.;;; Music is streamed from disk for memory efficiency.;;;;;; Native functions are registered by audio.c.(define-library (sigil studio audio) (import (sigil core)) (export ;; Setup/shutdown audio-setup audio-shutdown audio-initialized? ;; Sound effects (loaded into memory) load-sound sound? play-sound stop-all-sounds ;; Music streaming play-music stop-music pause-music resume-music music-playing? set-music-volume ;; Global control set-master-volume mute-audio unmute-audio audio-muted?) (begin ;; Native functions are automatically available after native module init. ))src/sigil/studio/font.sgldeleted
;;; (sigil studio font) - Font Loading and Text Rendering;;;;;; Provides TrueType font loading and text rendering via stb_truetype.;;; Native functions are registered by font.c.(define-library (sigil studio font) (import (sigil core)) (export ;; Font loading load-font font? font-size font-line-height ;; Text rendering draw-text draw-text-char text-width draw-text-centered) (begin ;; Draw text centered on position x, y (define (draw-text-centered font text x y) (let ((w (text-width font text))) (draw-text font text (- x (/ w 2)) y)))))src/sigil/studio/graphics.sgldeleted
;;; (sigil studio graphics) - Graphics Module;;;;;; Provides 2D/3D rendering capabilities via sokol_gfx.;;; Native functions are registered by graphics.c.;;;;;; For image loading and textures:;;; - Use (sigil studio image) for CPU-side image loading;;; - Use load-texture to convert images to GPU textures(define-library (sigil studio graphics) (import (sigil core) (sigil studio image)) (export ;; Setup/shutdown gfx-setup gfx-shutdown gfx-initialized? ;; Viewport set-viewport set-letterbox-color ;; Frame management begin-frame end-frame ;; Drawing clear-screen set-color draw-filled-rect draw-rect draw-line draw-point draw-triangle fill-triangle ;; Transform stack push-transform pop-transform reset-transform translate rotate rotate-at scale scale-at ;; Textures load-texture texture? texture-width texture-height draw-texture draw-texture-region ;; Re-export image functions for convenience load-image image? image-width image-height image-channels image-free! ;; High-level frame helper with-frame) (begin ;; Native functions are automatically available after native module init. ;; Convenience macro for frame management (define-syntax with-frame (syntax-rules () ((_ body ...) (begin (begin-frame) body ... (end-frame)))))))src/sigil/studio/image.sgldeleted
;;; (sigil studio image) - CPU-side Image Loading;;;;;; Provides image loading from files using stb_image.;;; Images are loaded into CPU memory and can be:;;; - Inspected (width, height, channels);;; - Converted to GPU textures via (sigil studio graphics);;;;;; Supported formats: PNG, JPEG, BMP, TGA, GIF, HDR, PSD, PIC, PNM(define-library (sigil studio image) (import (sigil core)) (export ;; Image loading load-image ;; Image predicates and properties image? image-width image-height image-channels ;; Memory management image-free!) (begin ;; Native functions are registered by image.c ;; This module provides re-exports and documentation. ))test/test-audio.sglmodified
;;; test-audio.sgl - Test audio playback(import (sigil core) (sigil studio app) (sigil studio graphics) (sigil studio audio) (sigil studio font)) (sigil app) (sigil graphics) (sigil audio) (sigil font))(define *font* #f)(define *music-playing* #f)test/test-draw-texture.sglmodified
;;; Opens a window and draws a test image to the screen.(import (sigil core) (sigil studio app) (sigil studio graphics)) (sigil app) (sigil graphics))(display "Loading test image...\n")test/test-font.sglmodified
;;; test-font.sgl - Test font loading and text rendering(import (sigil core) (sigil studio app) (sigil studio graphics) (sigil studio font)) (sigil app) (sigil graphics) (sigil font))(display "Loading font...\n")test/test-image.sglmodified
;;; test-image.sgl - Test image loading;;;;;; This test verifies that (sigil studio image) can load images.;;; This test verifies that (sigil image) can load images.(import (sigil core) (sigil studio image)) (sigil image))(display "Testing image loading...\n")test/test-transforms.sglmodified
;;; test-transforms.sgl - Demo of transform stack(import (sigil core) (sigil studio app) (sigil studio graphics) (sigil studio font)) (sigil app) (sigil graphics) (sigil font))(define (label text x y) (set-color 0.7 0.7 0.7)vendor/sokol/sokol_app.hdeleted
#if defined(SOKOL_IMPL) && !defined(SOKOL_APP_IMPL)#define SOKOL_APP_IMPL#endif#ifndef SOKOL_APP_INCLUDED/* sokol_app.h -- cross-platform application wrapper Project URL: https://github.com/floooh/sokol Do this: #define SOKOL_IMPL or #define SOKOL_APP_IMPL before you include this file in *one* C or C++ file to create the implementation. In the same place define one of the following to select the 3D-API which should be initialized by sokol_app.h (this must also match the backend selected for sokol_gfx.h if both are used in the same project): #define SOKOL_GLCORE #define SOKOL_GLES3 #define SOKOL_D3D11 #define SOKOL_METAL #define SOKOL_WGPU #define SOKOL_NOAPI Optionally provide the following defines with your own implementations: SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) SOKOL_WIN32_FORCE_MAIN - define this on Win32 to add a main() entry point SOKOL_WIN32_FORCE_WINMAIN - define this on Win32 to add a WinMain() entry point (enabled by default unless SOKOL_WIN32_FORCE_MAIN or SOKOL_NO_ENTRY is defined) SOKOL_NO_ENTRY - define this if sokol_app.h shouldn't "hijack" the main() function SOKOL_APP_API_DECL - public function declaration prefix (default: extern) SOKOL_API_DECL - same as SOKOL_APP_API_DECL SOKOL_API_IMPL - public function implementation prefix (default: -) Optionally define the following to force debug checks and validations even in release mode: SOKOL_DEBUG - by default this is defined if NDEBUG is not defined If sokol_app.h is compiled as a DLL, define the following before including the declaration or implementation: SOKOL_DLL On Windows, SOKOL_DLL will define SOKOL_APP_API_DECL as __declspec(dllexport) or __declspec(dllimport) as needed. if SOKOL_WIN32_FORCE_MAIN and SOKOL_WIN32_FORCE_WINMAIN are both defined, it is up to the developer to define the desired subsystem. On Linux, SOKOL_GLCORE can use either GLX or EGL. GLX is default, set SOKOL_FORCE_EGL to override. For example code, see https://github.com/floooh/sokol-samples/tree/master/sapp Portions of the Windows and Linux GL initialization, event-, icon- etc... code have been taken from GLFW (http://www.glfw.org/). iOS onscreen keyboard support 'inspired' by libgdx. Link with the following system libraries: - on macOS: - all backends: Foundation, Cocoa, QuartzCore - with SOKOL_METAL: Metal, MetalKit - with SOKOL_GLCORE: OpenGL - with SOKOL_WGPU: a WebGPU implementation library (tested with webgpu_dawn) - on iOS: - all backends: Foundation, UIKit - with SOKOL_METAL: Metal, MetalKit - with SOKOL_GLES3: OpenGLES, GLKit - on Linux: - all backends: X11, Xi, Xcursor, dl, pthread, m - with SOKOL_GLCORE: GL - with SOKOL_GLES3: GLESv2 - with SOKOL_WGPU: a WebGPU implementation library (tested with webgpu_dawn) - with EGL: EGL - on Android: GLESv3, EGL, log, android - on Windows: - with MSVC or Clang: library dependencies are defined via `#pragma comment` - with SOKOL_WGPU: a WebGPU implementation library (tested with webgpu_dawn) - with MINGW/MSYS2 gcc: - compile with '-mwin32' so that _WIN32 is defined - link with the following libs: -lkernel32 -luser32 -lshell32 - additionally with the GL backend: -lgdi32 - additionally with the D3D11 backend: -ld3d11 -ldxgi On Linux, you also need to use the -pthread compiler and linker option, otherwise weird things will happen, see here for details: https://github.com/floooh/sokol/issues/376 On macOS and iOS, the implementation must be compiled as Objective-C. On Emscripten: - for WebGL2: add the linker option `-s USE_WEBGL2=1` - for WebGPU: compile and link with `--use-port=emdawnwebgpu` (for more exotic situations read: https://dawn.googlesource.com/dawn/+/refs/heads/main/src/emdawnwebgpu/pkg/README.md) FEATURE OVERVIEW ================ sokol_app.h provides a minimalistic cross-platform API which implements the 'application-wrapper' parts of a 3D application: - a common application entry function - creates a window and 3D-API context/device with a swapchain surface, depth-stencil-buffer surface and optionally MSAA surface - makes the rendered frame visible - provides keyboard-, mouse- and low-level touch-events - platforms: MacOS, iOS, HTML5, Win32, Linux/RaspberryPi, Android - 3D-APIs: Metal, D3D11, GL4.1, GL4.3, GLES3, WebGL2, WebGPU, NOAPI FEATURE/PLATFORM MATRIX ======================= | Windows | macOS | Linux | iOS | Android | HTML5 --------------------+---------+-------+-------+-------+---------+-------- gl 4.x | YES | YES | YES | --- | --- | --- gles3/webgl2 | --- | --- | YES(2)| YES | YES | YES metal | --- | YES | --- | YES | --- | --- d3d11 | YES | --- | --- | --- | --- | --- webgpu | YES(4) | YES(4)| YES(4)| NO | NO | YES noapi | YES | TODO | TODO | --- | TODO | --- KEY_DOWN | YES | YES | YES | SOME | TODO | YES KEY_UP | YES | YES | YES | SOME | TODO | YES CHAR | YES | YES | YES | YES | TODO | YES MOUSE_DOWN | YES | YES | YES | --- | --- | YES MOUSE_UP | YES | YES | YES | --- | --- | YES MOUSE_SCROLL | YES | YES | YES | --- | --- | YES MOUSE_MOVE | YES | YES | YES | --- | --- | YES MOUSE_ENTER | YES | YES | YES | --- | --- | YES MOUSE_LEAVE | YES | YES | YES | --- | --- | YES TOUCHES_BEGAN | --- | --- | --- | YES | YES | YES TOUCHES_MOVED | --- | --- | --- | YES | YES | YES TOUCHES_ENDED | --- | --- | --- | YES | YES | YES TOUCHES_CANCELLED | --- | --- | --- | YES | YES | YES RESIZED | YES | YES | YES | YES | YES | YES ICONIFIED | YES | YES | YES | --- | --- | --- RESTORED | YES | YES | YES | --- | --- | --- FOCUSED | YES | YES | YES | --- | --- | YES UNFOCUSED | YES | YES | YES | --- | --- | YES SUSPENDED | --- | --- | --- | YES | YES | TODO RESUMED | --- | --- | --- | YES | YES | TODO QUIT_REQUESTED | YES | YES | YES | --- | --- | YES IME | TODO | TODO? | TODO | ??? | TODO | ??? key repeat flag | YES | YES | YES | --- | --- | YES windowed | YES | YES | YES | --- | --- | YES fullscreen | YES | YES | YES | YES | YES | YES(3) mouse hide | YES | YES | YES | --- | --- | YES mouse lock | YES | YES | YES | --- | --- | YES set cursor type | YES | YES | YES | --- | --- | YES screen keyboard | --- | --- | --- | YES | TODO | YES swap interval | YES | YES | YES | YES | TODO | YES high-dpi | YES | YES | TODO | YES | YES | YES clipboard | YES | YES | YES | --- | --- | YES MSAA | YES | YES | YES | YES | YES | YES drag'n'drop | YES | YES | YES | --- | --- | YES window icon | YES | YES(1)| YES | --- | --- | YES (1) macOS has no regular window icons, instead the dock icon is changed (2) supported with EGL only (not GLX) (3) fullscreen in the browser not supported on iphones (4) WebGPU on native desktop platforms should be considered experimental and mainly useful for debugging and benchmarking STEP BY STEP ============ --- Add a sokol_main() function to your code which returns a sapp_desc structure with initialization parameters and callback function pointers. This function is called very early, usually at the start of the platform's entry function (e.g. main or WinMain). You should do as little as possible here, since the rest of your code might be called from another thread (this depends on the platform): sapp_desc sokol_main(int argc, char* argv[]) { return (sapp_desc) { .width = 640, .height = 480, .init_cb = my_init_func, .frame_cb = my_frame_func, .cleanup_cb = my_cleanup_func, .event_cb = my_event_func, ... }; } To get any logging output in case of errors you need to provide a log callback. The easiest way is via sokol_log.h: #include "sokol_log.h" sapp_desc sokol_main(int argc, char* argv[]) { return (sapp_desc) { ... .logger.func = slog_func, }; } There are many more setup parameters, but these are the most important. For a complete list search for the sapp_desc structure declaration below. DO NOT call any sokol-app function from inside sokol_main(), since sokol-app will not be initialized at this point. The .width and .height parameters are the preferred size of the 3D rendering canvas. The actual size may differ from this depending on platform and other circumstances. Also the canvas size may change at any time (for instance when the user resizes the application window, or rotates the mobile device). You can just keep .width and .height zero-initialized to open a default-sized window (what "default-size" exactly means is platform-specific, but usually it's a size that covers most of, but not all, of the display). All provided function callbacks will be called from the same thread, but this may be different from the thread where sokol_main() was called. .init_cb (void (*)(void)) This function is called once after the application window, 3D rendering context and swap chain have been created. The function takes no arguments and has no return value. .frame_cb (void (*)(void)) This is the per-frame callback, which is usually called 60 times per second. This is where your application would update most of its state and perform all rendering. .cleanup_cb (void (*)(void)) The cleanup callback is called once right before the application quits. .event_cb (void (*)(const sapp_event* event)) The event callback is mainly for input handling, but is also used to communicate other types of events to the application. Keep the event_cb struct member zero-initialized if your application doesn't require event handling. As you can see, those 'standard callbacks' don't have a user_data argument, so any data that needs to be preserved between callbacks must live in global variables. If keeping state in global variables is not an option, there's an alternative set of callbacks with an additional user_data pointer argument: .user_data (void*) The user-data argument for the callbacks below .init_userdata_cb (void (*)(void* user_data)) .frame_userdata_cb (void (*)(void* user_data)) .cleanup_userdata_cb (void (*)(void* user_data)) .event_userdata_cb (void(*)(const sapp_event* event, void* user_data)) The function sapp_userdata() can be used to query the user_data pointer provided in the sapp_desc struct. You can also call sapp_query_desc() to get a copy of the original sapp_desc structure. NOTE that there's also an alternative compile mode where sokol_app.h doesn't "hijack" the main() function. Search below for SOKOL_NO_ENTRY. --- Implement the initialization callback function (init_cb), this is called once after the rendering surface, 3D API and swap chain have been initialized by sokol_app. All sokol-app functions can be called from inside the initialization callback, the most useful functions at this point are: int sapp_width(void) int sapp_height(void) Returns the current width and height of the default framebuffer in pixels, this may change from one frame to the next, and it may be different from the initial size provided in the sapp_desc struct. float sapp_widthf(void) float sapp_heightf(void) These are alternatives to sapp_width() and sapp_height() which return the default framebuffer size as float values instead of integer. This may help to prevent casting back and forth between int and float in more strongly typed languages than C and C++. double sapp_frame_duration(void) Returns the frame duration in seconds averaged over a number of frames to smooth out any jittering spikes. int sapp_color_format(void) int sapp_depth_format(void) The color and depth-stencil pixelformats of the default framebuffer, as integer values which are compatible with sokol-gfx's sg_pixel_format enum (so that they can be plugged directly in places where sg_pixel_format is expected). Possible values are: 23 == SG_PIXELFORMAT_RGBA8 28 == SG_PIXELFORMAT_BGRA8 42 == SG_PIXELFORMAT_DEPTH 43 == SG_PIXELFORMAT_DEPTH_STENCIL int sapp_sample_count(void) Return the MSAA sample count of the default framebuffer. const void* sapp_metal_get_device(void) const void* sapp_metal_get_current_drawable(void) const void* sapp_metal_get_depth_stencil_texture(void) const void* sapp_metal_get_msaa_color_texture(void) If the Metal backend has been selected, these functions return pointers to various Metal API objects required for rendering, otherwise they return a null pointer. These void pointers are actually Objective-C ids converted with a (ARC) __bridge cast so that the ids can be tunneled through C code. Also note that the returned pointers may change from one frame to the next, only the Metal device object is guaranteed to stay the same. const void* sapp_macos_get_window(void) On macOS, get the NSWindow object pointer, otherwise a null pointer. Before being used as Objective-C object, the void* must be converted back with a (ARC) __bridge cast. const void* sapp_ios_get_window(void) On iOS, get the UIWindow object pointer, otherwise a null pointer. Before being used as Objective-C object, the void* must be converted back with a (ARC) __bridge cast. const void* sapp_d3d11_get_device(void) const void* sapp_d3d11_get_device_context(void) const void* sapp_d3d11_get_render_view(void) const void* sapp_d3d11_get_resolve_view(void); const void* sapp_d3d11_get_depth_stencil_view(void) Similar to the sapp_metal_* functions, the sapp_d3d11_* functions return pointers to D3D11 API objects required for rendering, only if the D3D11 backend has been selected. Otherwise they return a null pointer. Note that the returned pointers to the render-target-view and depth-stencil-view may change from one frame to the next! const void* sapp_win32_get_hwnd(void) On Windows, get the window's HWND, otherwise a null pointer. The HWND has been cast to a void pointer in order to be tunneled through code which doesn't include Windows.h. const void* sapp_x11_get_window(void) On Linux, get the X11 Window, otherwise a null pointer. The Window has been cast to a void pointer in order to be tunneled through code which doesn't include X11/Xlib.h. const void* sapp_x11_get_display(void) On Linux, get the X11 Display, otherwise a null pointer. The Display has been cast to a void pointer in order to be tunneled through code which doesn't include X11/Xlib.h. const void* sapp_wgpu_get_device(void) const void* sapp_wgpu_get_render_view(void) const void* sapp_wgpu_get_resolve_view(void) const void* sapp_wgpu_get_depth_stencil_view(void) These are the WebGPU-specific functions to get the WebGPU objects and values required for rendering. If sokol_app.h is not compiled with SOKOL_WGPU, these functions return null. uint32_t sapp_gl_get_framebuffer(void) This returns the 'default framebuffer' of the GL context. Typically this will be zero. int sapp_gl_get_major_version(void) int sapp_gl_get_minor_version(void) bool sapp_gl_is_gles(void) Returns the major and minor version of the GL context and whether the GL context is a GLES context const void* sapp_android_get_native_activity(void); On Android, get the native activity ANativeActivity pointer, otherwise a null pointer. --- Implement the frame-callback function, this function will be called on the same thread as the init callback, but might be on a different thread than the sokol_main() function. Note that the size of the rendering framebuffer might have changed since the frame callback was called last. Call the functions sapp_width() and sapp_height() each frame to get the current size. --- Optionally implement the event-callback to handle input events. sokol-app provides the following type of input events: - a 'virtual key' was pressed down or released - a single text character was entered (provided as UTF-32 encoded UNICODE code point) - a mouse button was pressed down or released (left, right, middle) - mouse-wheel or 2D scrolling events - the mouse was moved - the mouse has entered or left the application window boundaries - low-level, portable multi-touch events (began, moved, ended, cancelled) - the application window was resized, iconified or restored - the application was suspended or restored (on mobile platforms) - the user or application code has asked to quit the application - a string was pasted to the system clipboard - one or more files have been dropped onto the application window To explicitly 'consume' an event and prevent that the event is forwarded for further handling to the operating system, call sapp_consume_event() from inside the event handler (NOTE that this behaviour is currently only implemented for some HTML5 events, support for other platforms and event types will be added as needed, please open a GitHub ticket and/or provide a PR if needed). NOTE: Do *not* call any 3D API rendering functions in the event callback function, since the 3D API context may not be active when the event callback is called (it may work on some platforms and 3D APIs, but not others, and the exact behaviour may change between sokol-app versions). --- Implement the cleanup-callback function, this is called once after the user quits the application (see the section "APPLICATION QUIT" for detailed information on quitting behaviour, and how to intercept a pending quit - for instance to show a "Really Quit?" dialog box). Note that the cleanup-callback isn't guaranteed to be called on the web and mobile platforms. MOUSE CURSOR TYPE AND VISIBILITY ================================ You can show and hide the mouse cursor with void sapp_show_mouse(bool show) And to get the current shown status: bool sapp_mouse_shown(void) NOTE that hiding the mouse cursor is different and independent from the MOUSE/POINTER LOCK feature which will also hide the mouse pointer when active (MOUSE LOCK is described below). To change the mouse cursor to one of several predefined types, call the function: void sapp_set_mouse_cursor(sapp_mouse_cursor cursor) Setting the default mouse cursor SAPP_MOUSECURSOR_DEFAULT will restore the standard look. To get the currently active mouse cursor type, call: sapp_mouse_cursor sapp_get_mouse_cursor(void) MOUSE LOCK (AKA POINTER LOCK, AKA MOUSE CAPTURE) ================================================ In normal mouse mode, no mouse movement events are reported when the mouse leaves the windows client area or hits the screen border (whether it's one or the other depends on the platform), and the mouse move events (SAPP_EVENTTYPE_MOUSE_MOVE) contain absolute mouse positions in framebuffer pixels in the sapp_event items mouse_x and mouse_y, and relative movement in framebuffer pixels in the sapp_event items mouse_dx and mouse_dy. To get continuous mouse movement (also when the mouse leaves the window client area or hits the screen border), activate mouse-lock mode by calling: sapp_lock_mouse(true) When mouse lock is activated, the mouse pointer is hidden, the reported absolute mouse position (sapp_event.mouse_x/y) appears frozen, and the relative mouse movement in sapp_event.mouse_dx/dy no longer has a direct relation to framebuffer pixels but instead uses "raw mouse input" (what "raw mouse input" exactly means also differs by platform). To deactivate mouse lock and return to normal mouse mode, call sapp_lock_mouse(false) And finally, to check if mouse lock is currently active, call if (sapp_mouse_locked()) { ... } Note that mouse-lock state may not change immediately after sapp_lock_mouse(true/false) is called, instead on some platforms the actual state switch may be delayed to the end of the current frame or even to a later frame. The mouse may also be unlocked automatically without calling sapp_lock_mouse(false), most notably when the application window becomes inactive. On the web platform there are further restrictions to be aware of, caused by the limitations of the HTML5 Pointer Lock API: - sapp_lock_mouse(true) can be called at any time, but it will only take effect in a 'short-lived input event handler of a specific type', meaning when one of the following events happens: - SAPP_EVENTTYPE_MOUSE_DOWN - SAPP_EVENTTYPE_MOUSE_UP - SAPP_EVENTTYPE_MOUSE_SCROLL - SAPP_EVENTTYPE_KEY_UP - SAPP_EVENTTYPE_KEY_DOWN - The mouse lock/unlock action on the web platform is asynchronous, this means that sapp_mouse_locked() won't immediately return the new status after calling sapp_lock_mouse(), instead the reported status will only change when the pointer lock has actually been activated or deactivated in the browser. - On the web, mouse lock can be deactivated by the user at any time by pressing the Esc key. When this happens, sokol_app.h behaves the same as if sapp_lock_mouse(false) is called. For things like camera manipulation it's most straightforward to lock and unlock the mouse right from the sokol_app.h event handler, for instance the following code enters and leaves mouse lock when the left mouse button is pressed and released, and then uses the relativeShowing the first 500 of 14048 diff lines for this file. This diff is INCOMPLETE; read the file or clone the repository for the rest.
vendor/sokol/sokol_audio.hdeleted
#if defined(SOKOL_IMPL) && !defined(SOKOL_AUDIO_IMPL)#define SOKOL_AUDIO_IMPL#endif#ifndef SOKOL_AUDIO_INCLUDED/* sokol_audio.h -- cross-platform audio-streaming API Project URL: https://github.com/floooh/sokol Do this: #define SOKOL_IMPL or #define SOKOL_AUDIO_IMPL before you include this file in *one* C or C++ file to create the implementation. Optionally provide the following defines with your own implementations: SOKOL_DUMMY_BACKEND - use a dummy backend SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) SOKOL_AUDIO_API_DECL- public function declaration prefix (default: extern) SOKOL_API_DECL - same as SOKOL_AUDIO_API_DECL SOKOL_API_IMPL - public function implementation prefix (default: -) SAUDIO_RING_MAX_SLOTS - max number of slots in the push-audio ring buffer (default 1024) SAUDIO_OSX_USE_SYSTEM_HEADERS - define this to force inclusion of system headers on macOS instead of using embedded CoreAudio declarations If sokol_audio.h is compiled as a DLL, define the following before including the declaration or implementation: SOKOL_DLL On Windows, SOKOL_DLL will define SOKOL_AUDIO_API_DECL as __declspec(dllexport) or __declspec(dllimport) as needed. Link with the following libraries: - on macOS: AudioToolbox - on iOS: AudioToolbox, AVFoundation - on FreeBSD: asound - on Linux: asound - on Android: aaudio - on Windows with MSVC or Clang toolchain: no action needed, libs are defined in-source via pragma-comment-lib - on Windows with MINGW/MSYS2 gcc: compile with '-mwin32' and link with -lole32 - on Vita: SceAudio - on 3DS: NDSP (libctru) FEATURE OVERVIEW ================ You provide a mono- or stereo-stream of 32-bit float samples, which Sokol Audio feeds into platform-specific audio backends: - Windows: WASAPI - Linux: ALSA - FreeBSD: ALSA - macOS: CoreAudio - iOS: CoreAudio+AVAudioSession - emscripten: WebAudio with ScriptProcessorNode - Android: AAudio - Vita: SceAudio - 3DS: NDSP (libctru) Sokol Audio will not do any buffer mixing or volume control, if you have multiple independent input streams of sample data you need to perform the mixing yourself before forwarding the data to Sokol Audio. There are two mutually exclusive ways to provide the sample data: 1. Callback model: You provide a callback function, which will be called when Sokol Audio needs new samples. On all platforms except emscripten, this function is called from a separate thread. 2. Push model: Your code pushes small blocks of sample data from your main loop or a thread you created. The pushed data is stored in a ring buffer where it is pulled by the backend code when needed. The callback model is preferred because it is the most direct way to feed sample data into the audio backends and also has less moving parts (there is no ring buffer between your code and the audio backend). Sometimes it is not possible to generate the audio stream directly in a callback function running in a separate thread, for such cases Sokol Audio provides the push-model as a convenience. SOKOL AUDIO, SOLOUD AND MINIAUDIO ================================= The WASAPI, ALSA and CoreAudio backend code has been taken from the SoLoud library (with some modifications, so any bugs in there are most likely my fault). If you need a more fully-featured audio solution, check out SoLoud, it's excellent: https://github.com/jarikomppa/soloud Another alternative which feature-wise is somewhere inbetween SoLoud and sokol-audio might be MiniAudio: https://github.com/mackron/miniaudio GLOSSARY ======== - stream buffer: The internal audio data buffer, usually provided by the backend API. The size of the stream buffer defines the base latency, smaller buffers have lower latency but may cause audio glitches. Bigger buffers reduce or eliminate glitches, but have a higher base latency. - stream callback: Optional callback function which is called by Sokol Audio when it needs new samples. On Windows, macOS/iOS and Linux, this is called in a separate thread, on WebAudio, this is called per-frame in the browser thread. - channel: A discrete track of audio data, currently 1-channel (mono) and 2-channel (stereo) is supported and tested. - sample: The magnitude of an audio signal on one channel at a given time. In Sokol Audio, samples are 32-bit float numbers in the range -1.0 to +1.0. - frame: The tightly packed set of samples for all channels at a given time. For mono 1 frame is 1 sample. For stereo, 1 frame is 2 samples. - packet: In Sokol Audio, a small chunk of audio data that is moved from the main thread to the audio streaming thread in order to decouple the rate at which the main thread provides new audio data, and the streaming thread consuming audio data. WORKING WITH SOKOL AUDIO ======================== First call saudio_setup() with your preferred audio playback options. In most cases you can stick with the default values, these provide a good balance between low-latency and glitch-free playback on all audio backends. You should always provide a logging callback to be aware of any warnings and errors. The easiest way is to use sokol_log.h for this: #include "sokol_log.h" // ... saudio_setup(&(saudio_desc){ .logger = { .func = slog_func, } }); If you want to use the callback-model, you need to provide a stream callback function either in saudio_desc.stream_cb or saudio_desc.stream_userdata_cb, otherwise keep both function pointers zero-initialized. Use push model and default playback parameters: saudio_setup(&(saudio_desc){ .logger.func = slog_func }); Use stream callback model and default playback parameters: saudio_setup(&(saudio_desc){ .stream_cb = my_stream_callback .logger.func = slog_func, }); The standard stream callback doesn't have a user data argument, if you want that, use the alternative stream_userdata_cb and also set the user_data pointer: saudio_setup(&(saudio_desc){ .stream_userdata_cb = my_stream_callback, .user_data = &my_data .logger.func = slog_func, }); The following playback parameters can be provided through the saudio_desc struct: General parameters (both for stream-callback and push-model): int sample_rate -- the sample rate in Hz, default: 44100 int num_channels -- number of channels, default: 1 (mono) int buffer_frames -- number of frames in streaming buffer, default: 2048 The stream callback prototype (either with or without userdata): void (*stream_cb)(float* buffer, int num_frames, int num_channels) void (*stream_userdata_cb)(float* buffer, int num_frames, int num_channels, void* user_data) Function pointer to the user-provide stream callback. Push-model parameters: int packet_frames -- number of frames in a packet, default: 128 int num_packets -- number of packets in ring buffer, default: 64 The sample_rate and num_channels parameters are only hints for the audio backend, it isn't guaranteed that those are the values used for actual playback. To get the actual parameters, call the following functions after saudio_setup(): int saudio_sample_rate(void) int saudio_channels(void); It's unlikely that the number of channels will be different than requested, but a different sample rate isn't uncommon. (NOTE: there's an yet unsolved issue when an audio backend might switch to a different sample rate when switching output devices, for instance plugging in a bluetooth headset, this case is currently not handled in Sokol Audio). You can check if audio initialization was successful with saudio_isvalid(). If backend initialization failed for some reason (for instance when there's no audio device in the machine), this will return false. Not checking for success won't do any harm, all Sokol Audio function will silently fail when called after initialization has failed, so apart from missing audio output, nothing bad will happen. Before your application exits, you should call saudio_shutdown(); This stops the audio thread (on Linux, Windows and macOS/iOS) and properly shuts down the audio backend. THE STREAM CALLBACK MODEL ========================= To use Sokol Audio in stream-callback-mode, provide a callback function like this in the saudio_desc struct when calling saudio_setup(): void stream_cb(float* buffer, int num_frames, int num_channels) { ... } Or the alternative version with a user-data argument: void stream_userdata_cb(float* buffer, int num_frames, int num_channels, void* user_data) { my_data_t* my_data = (my_data_t*) user_data; ... } The job of the callback function is to fill the *buffer* with 32-bit float sample values. To output silence, fill the buffer with zeros: void stream_cb(float* buffer, int num_frames, int num_channels) { const int num_samples = num_frames * num_channels; for (int i = 0; i < num_samples; i++) { buffer[i] = 0.0f; } } For stereo output (num_channels == 2), the samples for the left and right channel are interleaved: void stream_cb(float* buffer, int num_frames, int num_channels) { assert(2 == num_channels); for (int i = 0; i < num_frames; i++) { buffer[2*i + 0] = ...; // left channel buffer[2*i + 1] = ...; // right channel } } Please keep in mind that the stream callback function is running in a separate thread, if you need to share data with the main thread you need to take care yourself to make the access to the shared data thread-safe! THE PUSH MODEL ============== To use the push-model for providing audio data, simply don't set (keep zero-initialized) the stream_cb field in the saudio_desc struct when calling saudio_setup(). To provide sample data with the push model, call the saudio_push() function at regular intervals (for instance once per frame). You can call the saudio_expect() function to ask Sokol Audio how much room is in the ring buffer, but if you provide a continuous stream of data at the right sample rate, saudio_expect() isn't required (it's a simple way to sync/throttle your sample generation code with the playback rate though). With saudio_push() you may need to maintain your own intermediate sample buffer, since pushing individual sample values isn't very efficient. The following example is from the MOD player sample in sokol-samples (https://github.com/floooh/sokol-samples): const int num_frames = saudio_expect(); if (num_frames > 0) { const int num_samples = num_frames * saudio_channels(); read_samples(flt_buf, num_samples); saudio_push(flt_buf, num_frames); } Another option is to ignore saudio_expect(), and just push samples as they are generated in small batches. In this case you *need* to generate the samples at the right sample rate: The following example is taken from the Tiny Emulators project (https://github.com/floooh/chips-test), this is for mono playback, so (num_samples == num_frames): // tick the sound generator if (ay38910_tick(&sys->psg)) { // new sample is ready sys->sample_buffer[sys->sample_pos++] = sys->psg.sample; if (sys->sample_pos == sys->num_samples) { // new sample packet is ready saudio_push(sys->sample_buffer, sys->num_samples); sys->sample_pos = 0; } } THE WEBAUDIO BACKEND ==================== The WebAudio backend is currently using a ScriptProcessorNode callback to feed the sample data into WebAudio. ScriptProcessorNode has been deprecated for a while because it is running from the main thread, with the default initialization parameters it works 'pretty well' though. Ultimately Sokol Audio will use Audio Worklets, but this requires a few more things to fall into place (Audio Worklets implemented everywhere, SharedArrayBuffers enabled again, and I need to figure out a 'low-cost' solution in terms of implementation effort, since Audio Worklets are a lot more complex than ScriptProcessorNode if the audio data needs to come from the main thread). The WebAudio backend is automatically selected when compiling for emscripten (__EMSCRIPTEN__ define exists). https://developers.google.com/web/updates/2017/12/audio-worklet https://developers.google.com/web/updates/2018/06/audio-worklet-design-pattern "Blob URLs": https://www.html5rocks.com/en/tutorials/workers/basics/ Also see: https://blog.paul.cx/post/a-wait-free-spsc-ringbuffer-for-the-web/ THE COREAUDIO BACKEND ===================== The CoreAudio backend is selected on macOS and iOS (__APPLE__ is defined). Since the CoreAudio API is implemented in C (not Objective-C) on macOS the implementation part of Sokol Audio can be included into a C source file. However on iOS, Sokol Audio must be compiled as Objective-C due to it's reliance on the AVAudioSession object. The iOS code path support both being compiled with or without ARC (Automatic Reference Counting). For thread synchronisation, the CoreAudio backend will use the pthread_mutex_* functions. The incoming floating point samples will be directly forwarded to CoreAudio without further conversion. macOS and iOS applications that use Sokol Audio need to link with the AudioToolbox framework. THE WASAPI BACKEND ================== The WASAPI backend is automatically selected when compiling on Windows (_WIN32 is defined). For thread synchronisation a Win32 critical section is used. WASAPI may use a different size for its own streaming buffer then requested, so the base latency may be slightly bigger. The current backend implementation converts the incoming floating point sample values to signed 16-bit integers. The required Windows system DLLs are linked with #pragma comment(lib, ...), so you shouldn't need to add additional linker libs in the build process (otherwise this is a bug which should be fixed in sokol_audio.h). THE ALSA BACKEND ================ The ALSA backend is automatically selected when compiling on Linux ('linux' is defined). For thread synchronisation, the pthread_mutex_* functions are used. Samples are directly forwarded to ALSA in 32-bit float format, no further conversion is taking place. You need to link with the 'asound' library, and the <alsa/asoundlib.h> header must be present (usually both are installed with some sort of ALSA development package). THE VITA BACKEND ================ The VITA backend is automatically selected when compiling with vitasdk ('PSP2_SDK_VERSION' is defined). For thread synchronisation, the pthread_mutex_* functions are used. Samples are converted from float to short (uint16_t) to maintain all the same interface/api as other platforms. You may use any supported sample rate you wish, but all audio MUST match the same sample rate you choose. This uses the "BGM" port to allow selecting the sample rate ("Main" port is restricted to 48000 only). You need to link with the 'SceAudio' library, and the <psp2/audioout.h> header must be present (usually both are installed with the vitasdk). THE 3DS BACKEND ================ The 3DS backend is automatically selected when compiling with libctru ('__3DS__' is defined). Running a separate thread on the older 3ds is not a good idea and I was not able to get it working without slowing down the main thread too much (it has a single core available with cooperative threads). The NDSP seems to work better by using its ndspSetCallback method. You may use any supported sample rate you wish, but all audio MUST match the same sample rate you choose or it will sound slowed down or sped up. The queue size and other NDSP specific parameters can be chosen by the provided 'saudio_n3ds_desc' type. Defaults will be used if nothing is provided. There is a known issue of a noticeable delay when starting a new sound on emulators. I was not able to improve this to my liking and ~300ms can be expected. This can be improved by using a lower buffer size than the 2048 default but I would not suggest under 1536. It may crash under 1408, and they must be in multiples of 128. Note: I was NOT able to reproduce this issue on a real device and the audio worked perfectly. MEMORY ALLOCATION OVERRIDE ========================== You can override the memory allocation functions at initialization time like this: void* my_alloc(size_t size, void* user_data) { return malloc(size); } void my_free(void* ptr, void* user_data) { free(ptr); } ... saudio_setup(&(saudio_desc){ // ... .allocator = { .alloc_fn = my_alloc, .free_fn = my_free, .user_data = ..., } }); ... If no overrides are provided, malloc and free will be used. This only affects memory allocation calls done by sokol_audio.h itself though, not any allocations in OS libraries. Memory allocation will only happen on the same thread where saudio_setup() was called, so you don't need to worry about thread-safety. ERROR REPORTING AND LOGGING =========================== To get any logging information at all you need to provide a logging callback in the setup call the easiest way is to use sokol_log.h: #include "sokol_log.h" saudio_setup(&(saudio_desc){ .logger.func = slog_func }); To override logging with your own callback, first write a logging function like this: void my_log(const char* tag, // e.g. 'saudio' uint32_t log_level, // 0=panic, 1=error, 2=warn, 3=info uint32_t log_item_id, // SAUDIO_LOGITEM_* const char* message_or_null, // a message string, may be nullptr in release mode uint32_t line_nr, // line number in sokol_audio.h const char* filename_or_null, // source filename, may be nullptr in release mode void* user_data) { ... } ...and then setup sokol-audio like this: saudio_setup(&(saudio_desc){ .logger = { .func = my_log, .user_data = my_user_data, } }); The provided logging function must be reentrant (e.g. be callable from different threads).Showing the first 500 of 2664 diff lines for this file. This diff is INCOMPLETE; read the file or clone the repository for the rest.
vendor/sokol/sokol_gfx.hdeleted
#if defined(SOKOL_IMPL) && !defined(SOKOL_GFX_IMPL)#define SOKOL_GFX_IMPL#endif#ifndef SOKOL_GFX_INCLUDED/* sokol_gfx.h -- simple 3D API wrapper Project URL: https://github.com/floooh/sokol Example code: https://github.com/floooh/sokol-samples Do this: #define SOKOL_IMPL or #define SOKOL_GFX_IMPL before you include this file in *one* C or C++ file to create the implementation. In the same place define one of the following to select the rendering backend: #define SOKOL_GLCORE #define SOKOL_GLES3 #define SOKOL_D3D11 #define SOKOL_METAL #define SOKOL_WGPU #define SOKOL_VULKAN #define SOKOL_DUMMY_BACKEND I.e. for the desktop GL it should look like this: #include ... #include ... #define SOKOL_IMPL #define SOKOL_GLCORE #include "sokol_gfx.h" The dummy backend replaces the platform-specific backend code with empty stub functions. This is useful for writing tests that need to run on the command line. Optionally provide the following defines with your own implementations: SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) SOKOL_GFX_API_DECL - public function declaration prefix (default: extern) SOKOL_API_DECL - same as SOKOL_GFX_API_DECL SOKOL_API_IMPL - public function implementation prefix (default: -) SOKOL_TRACE_HOOKS - enable trace hook callbacks (search below for TRACE HOOKS) SOKOL_EXTERNAL_GL_LOADER - indicates that you're using your own GL loader, in this case sokol_gfx.h will not include any platform GL headers and disable the integrated Win32 GL loader If sokol_gfx.h is compiled as a DLL, define the following before including the declaration or implementation: SOKOL_DLL On Windows, SOKOL_DLL will define SOKOL_GFX_API_DECL as __declspec(dllexport) or __declspec(dllimport) as needed. Optionally define the following to force debug checks and validations even in release mode: SOKOL_DEBUG - by default this is defined if NDEBUG is not defined Link with the following system libraries (note that sokol_app.h has additional linker requirements): - on macOS/iOS with Metal: Metal - on macOS with GL: OpenGL - on iOS with GL: OpenGLES - on Linux with EGL: GL or GLESv2 - on Linux with GLX: GL - on Android: GLESv3, log, android - on Windows with the MSVC or Clang toolchains: no action needed, libs are defined in-source via pragma-comment-lib - on Windows with MINGW/MSYS2 gcc: compile with '-mwin32' so that _WIN32 is defined - with the D3D11 backend: -ld3d11 On macOS and iOS, the implementation must be compiled as Objective-C. On Emscripten: - for WebGL2: add the linker option `-s USE_WEBGL2=1` - for WebGPU: compile and link with `--use-port=emdawnwebgpu` (for more exotic situations, read: https://dawn.googlesource.com/dawn/+/refs/heads/main/src/emdawnwebgpu/pkg/README.md) sokol_gfx DOES NOT: =================== - create a window, swapchain or the 3D-API context/device, you must do this before sokol_gfx is initialized, and pass any required information (like 3D device pointers) to the sokol_gfx initialization call - present the rendered frame, how this is done exactly usually depends on how the window and 3D-API context/device was created - provide a unified shader language, instead 3D-API-specific shader source-code or shader-bytecode must be provided (for the "official" offline shader cross-compiler / code-generator, see here: https://github.com/floooh/sokol-tools/blob/master/docs/sokol-shdc.md) STEP BY STEP ============ --- to initialize sokol_gfx, after creating a window and a 3D-API context/device, call: sg_setup(const sg_desc*) Depending on the selected 3D backend, sokol-gfx requires some information about its runtime environment, like a GPU device pointer, default swapchain pixel formats and so on. If you are using sokol_app.h for the window system glue, you can use a helper function provided in the sokol_glue.h header: #include "sokol_gfx.h" #include "sokol_app.h" #include "sokol_glue.h" //... sg_setup(&(sg_desc){ .environment = sglue_environment(), }); To get any logging output for errors and from the validation layer, you need to provide a logging callback. Easiest way is through sokol_log.h: #include "sokol_log.h" //... sg_setup(&(sg_desc){ //... .logger.func = slog_func, }); --- create resource objects (buffers, images, views, samplers, shaders and pipeline objects) sg_buffer sg_make_buffer(const sg_buffer_desc*) sg_image sg_make_image(const sg_image_desc*) sg_view sg_make_view(const sg_view_desc*) sg_sampler sg_make_sampler(const sg_sampler_desc*) sg_shader sg_make_shader(const sg_shader_desc*) sg_pipeline sg_make_pipeline(const sg_pipeline_desc*) --- start a render- or compute-pass: sg_begin_pass(const sg_pass* pass); Typically, render passes render into an externally provided swapchain which presents the rendering result on the display. Such a 'swapchain pass' is started like this: sg_begin_pass(&(sg_pass){ .action = { ... }, .swapchain = sglue_swapchain() }) ...where .action is an sg_pass_action struct containing actions to be performed at the start and end of a render pass (such as clearing the render surfaces to a specific color), and .swapchain is an sg_swapchain struct with all the required information to render into the swapchain's surfaces. To start an 'offscreen render pass' into sokol-gfx image objects, populate the sg_pass.attachments nested struct with attachment view objects (1..4 color-attachment-views for to render into, a depth-stencil-attachment-view to provide the depth-stencil-buffer, and optionally 1..4 resolve-attachment-views for an MSAA-resolve operation: sg_begin_pass(&(sg_pass){ .action = { ... }, .attachments = { .colors[0] = color_attachment_view, .resolves[0] = optional_resolve_attachment_view, .depth_stencil = depth_stencil_attachment_view, }, }); To start a compute-pass, just set the .compute item to true: sg_begin_pass(&(sg_pass){ .compute = true }); --- set the pipeline state for the next draw call with: sg_apply_pipeline(sg_pipeline pip) --- fill an sg_bindings struct with the resource bindings for the next draw- or dispatch-call (0..N vertex buffers, 0 or 1 index buffer, 0..N views, 0..N samplers), and call sg_apply_bindings(const sg_bindings* bindings) ...to update the resource bindings. Note that in a compute pass, no vertex- or index-buffer bindings can be used, and in render passes, no storage-image bindings are allowed. Those restrictions will be checked by the sokol-gfx validation layer. --- optionally update shader uniform data with: sg_apply_uniforms(int ub_slot, const sg_range* data) Read the section 'UNIFORM DATA LAYOUT' to learn about the expected memory layout of the uniform data passed into sg_apply_uniforms(). --- kick off a draw call with: sg_draw(int base_element, int num_elements, int num_instances) The sg_draw() function unifies all the different ways to render primitives in a single call (indexed vs non-indexed rendering, and instanced vs non-instanced rendering). In case of indexed rendering, base_element and num_element specify indices in the currently bound index buffer. In case of non-indexed rendering base_element and num_elements specify vertices in the currently bound vertex-buffer(s). To perform instanced rendering, the rendering pipeline must be setup for instancing (see sg_pipeline_desc below), a separate vertex buffer containing per-instance data must be bound, and the num_instances parameter must be > 1. Alternatively, call: sg_draw_ex(...) to provide a base-vertex and/or base-instance which allows to render from different sections of a vertex buffer without rebinding the vertex buffer with a different offset. Note that the `sg_draw_ex()` only has limited portability on OpenGL, check the sg_limits struct members .draw_base_vertex and .draw_base_instance for runtime support, those are generally true on non-GL-backends, and on GL the feature flags are set according to the GL version: - on GL base_instance != 0 is only supported since GL 4.2 - on GLES3.x, base_instance != 0 is not supported - on GLES3.x, base_vertex is only supported since GLES3.2 (e.g. not supported on WebGL2) --- ...or kick of a dispatch call to invoke a compute shader workload: sg_dispatch(int num_groups_x, int num_groups_y, int num_groups_z) The dispatch args define the number of 'compute workgroups' processed by the currently applied compute shader. --- finish the current pass with: sg_end_pass() --- when done with the current frame, call sg_commit() --- at the end of your program, shutdown sokol_gfx with: sg_shutdown() --- if you need to destroy resources before sg_shutdown(), call: sg_destroy_buffer(sg_buffer buf) sg_destroy_image(sg_image img) sg_destroy_sampler(sg_sampler smp) sg_destroy_shader(sg_shader shd) sg_destroy_pipeline(sg_pipeline pip) sg_destroy_view(sg_view view) --- to set a new viewport rectangle, call: sg_apply_viewport(int x, int y, int width, int height, bool origin_top_left) ...or if you want to specify the viewport rectangle with float values: sg_apply_viewportf(float x, float y, float width, float height, bool origin_top_left) --- to set a new scissor rect, call: sg_apply_scissor_rect(int x, int y, int width, int height, bool origin_top_left) ...or with float values: sg_apply_scissor_rectf(float x, float y, float width, float height, bool origin_top_left) Both sg_apply_viewport() and sg_apply_scissor_rect() must be called inside a rendering pass (e.g. not in a compute pass, or outside a pass) Note that sg_begin_pass() will reset both the viewport and scissor rectangles to cover the entire framebuffer. --- to update (overwrite) the content of buffer and image resources, call: sg_update_buffer(sg_buffer buf, const sg_range* data) sg_update_image(sg_image img, const sg_image_data* data) Buffers and images to be updated must have been created with sg_buffer_desc.usage.dynamic_update or .stream_update. Only one update per frame is allowed for buffer and image resources when using the sg_update_*() functions. The rationale is to have a simple protection from the CPU scribbling over data the GPU is currently using, or the CPU having to wait for the GPU Buffer and image updates can be partial, as long as a rendering operation only references the valid (updated) data in the buffer or image. --- to append a chunk of data to a buffer resource, call: int sg_append_buffer(sg_buffer buf, const sg_range* data) The difference to sg_update_buffer() is that sg_append_buffer() can be called multiple times per frame to append new data to the buffer piece by piece, optionally interleaved with draw calls referencing the previously written data. sg_append_buffer() returns a byte offset to the start of the written data, this offset can be assigned to sg_bindings.vertex_buffer_offsets[n] or sg_bindings.index_buffer_offset Code example: for (...) { const void* data = ...; const int num_bytes = ...; int offset = sg_append_buffer(buf, &(sg_range) { .ptr=data, .size=num_bytes }); bindings.vertex_buffer_offsets[0] = offset; sg_apply_pipeline(pip); sg_apply_bindings(&bindings); sg_apply_uniforms(...); sg_draw(...); } A buffer to be used with sg_append_buffer() must have been created with sg_buffer_desc.usage.dynamic_update or .stream_update. If the application appends more data to the buffer then fits into the buffer, the buffer will go into the "overflow" state for the rest of the frame. Any draw calls attempting to render an overflown buffer will be silently dropped (in debug mode this will also result in a validation error). You can also check manually if a buffer is in overflow-state by calling bool sg_query_buffer_overflow(sg_buffer buf) You can manually check to see if an overflow would occur before adding any data to a buffer by calling bool sg_query_buffer_will_overflow(sg_buffer buf, size_t size) NOTE: Due to restrictions in underlying 3D-APIs, appended chunks of data will be 4-byte aligned in the destination buffer. This means that there will be gaps in index buffers containing 16-bit indices when the number of indices in a call to sg_append_buffer() is odd. This isn't a problem when each call to sg_append_buffer() is associated with one draw call, but will be problematic when a single indexed draw call spans several appended chunks of indices. --- to check at runtime for optional features, limits and pixelformat support, call: sg_features sg_query_features() sg_limits sg_query_limits() sg_pixelformat_info sg_query_pixelformat(sg_pixel_format fmt) --- if you need to call into the underlying 3D-API directly, you must call: sg_reset_state_cache() ...before calling sokol_gfx functions again --- you can inspect the original sg_desc structure handed to sg_setup() by calling sg_query_desc(). This will return an sg_desc struct with the default values patched in instead of any zero-initialized values --- you can get a desc struct matching the creation attributes of a specific resource object via: sg_buffer_desc sg_query_buffer_desc(sg_buffer buf) sg_image_desc sg_query_image_desc(sg_image img) sg_sampler_desc sg_query_sampler_desc(sg_sampler smp) sg_shader_desc sq_query_shader_desc(sg_shader shd) sg_pipeline_desc sg_query_pipeline_desc(sg_pipeline pip) sg_view_desc sg_query_view_desc(sg_view view) ...but NOTE that the returned desc structs may be incomplete, only creation attributes that are kept around internally after resource creation will be filled in, and in some cases (like shaders) that's very little. Any missing attributes will be set to zero. The returned desc structs might still be useful as partial blueprint for creating similar resources if filled up with the missing attributes. Calling the query-desc functions on an invalid resource will return completely zeroed structs (it makes sense to check the resource state with sg_query_*_state() first) --- you can query the default resource creation parameters through the functions sg_buffer_desc sg_query_buffer_defaults(const sg_buffer_desc* desc) sg_image_desc sg_query_image_defaults(const sg_image_desc* desc) sg_sampler_desc sg_query_sampler_defaults(const sg_sampler_desc* desc) sg_shader_desc sg_query_shader_defaults(const sg_shader_desc* desc) sg_pipeline_desc sg_query_pipeline_defaults(const sg_pipeline_desc* desc) sg_view_desc sg_query_view_defaults(const sg_view_desc* desc) These functions take a pointer to a desc structure which may contain zero-initialized items for default values. These zero-init values will be replaced with their concrete values in the returned desc struct. --- you can inspect various internal resource runtime values via: sg_buffer_info sg_query_buffer_info(sg_buffer buf) sg_image_info sg_query_image_info(sg_image img) sg_sampler_info sg_query_sampler_info(sg_sampler smp) sg_shader_info sg_query_shader_info(sg_shader shd) sg_pipeline_info sg_query_pipeline_info(sg_pipeline pip) sg_view_info sg_query_view_info(sg_view view) ...please note that the returned info-structs are tied quite closely to sokol_gfx.h internals, and may change more often than other public API functions and structs. -- you can query the type/flavour and parent resource of a view: sg_view_type sg_query_view_type(sg_view view) sg_image sg_query_view_image(sg_view view) sg_buffer sg_query_view_buffer(sg_view view) --- you can query stats and control stats collection via: sg_query_stats() sg_enable_stats() sg_disable_stats() sg_stats_enabled() --- you can ask at runtime what backend sokol_gfx.h has been compiled for: sg_backend sg_query_backend(void) --- call the following helper functions to compute the number of bytes in a texture row or surface for a specific pixel format. These functions might be helpful when preparing image data for consumption by sg_make_image() or sg_update_image(): int sg_query_row_pitch(sg_pixel_format fmt, int width, int int row_align_bytes); int sg_query_surface_pitch(sg_pixel_format fmt, int width, int height, int row_align_bytes); Width and height are generally in number pixels, but note that 'row' has different meaning for uncompressed vs compressed pixel formats: for uncompressed formats, a row is identical with a single line if pixels, while in compressed formats, one row is a line of *compression blocks*. This is why calling sg_query_surface_pitch() for a compressed pixel format and height N, N+1, N+2, ... may return the same result. The row_align_bytes parameter is for added flexibility. For image data that goes into the sg_make_image() or sg_update_image() this should generally be 1, because these functions take tightly packed image data as input no matter what alignment restrictions exist in the backend 3D APIs. ON INITIALIZATION: ================== When calling sg_setup(), a pointer to an sg_desc struct must be provided which contains initialization options. These options provide two types of information to sokol-gfx: (1) upper bounds and limits needed to allocate various internal data structures: - the max number of resources of each type that can be alive at the same time, this is used for allocating internal pools - the max overall size of uniform data that can be updated per frame, including a worst-case alignment per uniform update (this worst-case alignment is 256 bytes) - the max size of all dynamic resource updates (sg_update_buffer, sg_append_buffer and sg_update_image) per frame - the max number of compute-dispatch calls in a compute pass Not all of those limit values are used by all backends, but it is good practice to provide them none-the-less. (2) 3D backend "environment information" in a nested sg_environment struct: - pointers to backend-specific context- or device-objects (for instance the D3D11, WebGPU or Metal device objects) - defaults for external swapchain pixel formats and sample counts, these will be used as default values in image and pipeline objects, and the sg_swapchain struct passed into sg_begin_pass() Usually you provide a complete sg_environment struct through a helper function, as an example look at the sglue_environment() function in the sokol_glue.h header. See the documentation block of the sg_desc struct below for more information. ON RENDER PASSES ================ Relevant samples: - https://floooh.github.io/sokol-html5/offscreen-sapp.html - https://floooh.github.io/sokol-html5/offscreen-msaa-sapp.html - https://floooh.github.io/sokol-html5/mrt-sapp.html - https://floooh.github.io/sokol-html5/mrt-pixelformats-sapp.html A render pass groups rendering commands into a set of render target images (called 'render pass attachments'). Render target images can be used in subsequent passes as textures (it is invalid to use the same image both as render target and as texture in the same pass). The following sokol-gfx functions must only be called inside a render-pass: sg_apply_viewport[f]Showing the first 500 of 26613 diff lines for this file. This diff is INCOMPLETE; read the file or clone the repository for the rest.
vendor/sokol/sokol_glue.hdeleted
#if defined(SOKOL_IMPL) && !defined(SOKOL_GLUE_IMPL)#define SOKOL_GLUE_IMPL#endif#ifndef SOKOL_GLUE_INCLUDED/* sokol_glue.h -- glue helper functions for sokol headers Project URL: https://github.com/floooh/sokol Do this: #define SOKOL_IMPL or #define SOKOL_GLUE_IMPL before you include this file in *one* C or C++ file to create the implementation. ...optionally provide the following macros to override defaults: SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) SOKOL_GLUE_API_DECL - public function declaration prefix (default: extern) SOKOL_API_DECL - same as SOKOL_GLUE_API_DECL SOKOL_API_IMPL - public function implementation prefix (default: -) If sokol_glue.h is compiled as a DLL, define the following before including the declaration or implementation: SOKOL_DLL On Windows, SOKOL_DLL will define SOKOL_GLUE_API_DECL as __declspec(dllexport) or __declspec(dllimport) as needed. OVERVIEW ======== sokol_glue.h provides glue helper functions between sokol_gfx.h and sokol_app.h, so that sokol_gfx.h doesn't need to depend on sokol_app.h but can be used with different window system glue libraries. PROVIDED FUNCTIONS ================== sg_environment sglue_environment(void) Returns an sg_environment struct initialized by calling sokol_app.h functions. Use this in the sg_setup() call like this: sg_setup(&(sg_desc){ .environment = sglue_environment(), ... }); sg_swapchain sglue_swapchain(void) Returns an sg_swapchain struct initialized by calling sokol_app.h functions. Use this in sg_begin_pass() for a 'swapchain pass' like this: sg_begin_pass(&(sg_pass){ .swapchain = sglue_swapchain(), ... }); LICENSE ======= zlib/libpng license Copyright (c) 2018 Andre Weissflog This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution.*/#define SOKOL_GLUE_INCLUDED#if defined(SOKOL_API_DECL) && !defined(SOKOL_GLUE_API_DECL)#define SOKOL_GLUE_API_DECL SOKOL_API_DECL#endif#ifndef SOKOL_GLUE_API_DECL#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_GLUE_IMPL)#define SOKOL_GLUE_API_DECL __declspec(dllexport)#elif defined(_WIN32) && defined(SOKOL_DLL)#define SOKOL_GLUE_API_DECL __declspec(dllimport)#else#define SOKOL_GLUE_API_DECL extern#endif#endif#ifndef SOKOL_GFX_INCLUDED#error "Please include sokol_gfx.h before sokol_glue.h"#endif#ifdef __cplusplusextern "C" {#endifSOKOL_GLUE_API_DECL sg_environment sglue_environment(void);SOKOL_GLUE_API_DECL sg_swapchain sglue_swapchain(void);#ifdef __cplusplus} /* extern "C" */#endif#endif /* SOKOL_GLUE_INCLUDED *//*-- IMPLEMENTATION ----------------------------------------------------------*/#ifdef SOKOL_GLUE_IMPL#define SOKOL_GLUE_IMPL_INCLUDED (1)#include <string.h> /* memset */#ifndef SOKOL_APP_INCLUDED#error "Please include sokol_app.h before the sokol_glue.h implementation"#endif#ifndef SOKOL_API_IMPL#define SOKOL_API_IMPL#endif#ifndef _SOKOL_PRIVATE #if defined(__GNUC__) || defined(__clang__) #define _SOKOL_PRIVATE __attribute__((unused)) static #else #define _SOKOL_PRIVATE static #endif#endif#ifndef SOKOL_ASSERT #include <assert.h> #define SOKOL_ASSERT(c) assert(c)#endif#ifndef SOKOL_UNREACHABLE #define SOKOL_UNREACHABLE SOKOL_ASSERT(false)#endif_SOKOL_PRIVATE sg_pixel_format _sglue_to_sgpixelformat(sapp_pixel_format fmt) { switch (fmt) { case SAPP_PIXELFORMAT_NONE: return SG_PIXELFORMAT_NONE; case SAPP_PIXELFORMAT_RGBA8: return SG_PIXELFORMAT_RGBA8; case SAPP_PIXELFORMAT_SRGB8A8: return SG_PIXELFORMAT_SRGB8A8; case SAPP_PIXELFORMAT_BGRA8: return SG_PIXELFORMAT_BGRA8; case SAPP_PIXELFORMAT_DEPTH_STENCIL: return SG_PIXELFORMAT_DEPTH_STENCIL; case SAPP_PIXELFORMAT_DEPTH: return SG_PIXELFORMAT_DEPTH; case SAPP_PIXELFORMAT_SBGRA8: // FIXME! default: SOKOL_UNREACHABLE; return SG_PIXELFORMAT_NONE; }}SOKOL_API_IMPL sg_environment sglue_environment(void) { sg_environment res; memset(&res, 0, sizeof(res)); const sapp_environment env = sapp_get_environment(); res.defaults.color_format = _sglue_to_sgpixelformat(env.defaults.color_format); res.defaults.depth_format = _sglue_to_sgpixelformat(env.defaults.depth_format); res.defaults.sample_count = env.defaults.sample_count; res.metal.device = env.metal.device; res.d3d11.device = env.d3d11.device; res.d3d11.device_context = env.d3d11.device_context; res.wgpu.device = env.wgpu.device; res.vulkan.physical_device = env.vulkan.physical_device; res.vulkan.device = env.vulkan.device; res.vulkan.queue = env.vulkan.queue; res.vulkan.queue_family_index = env.vulkan.queue_family_index; return res;}SOKOL_API_IMPL sg_swapchain sglue_swapchain(void) { sg_swapchain res; memset(&res, 0, sizeof(res)); const sapp_swapchain sc = sapp_get_swapchain(); res.width = sc.width; res.height = sc.height; res.sample_count = sc.sample_count; res.color_format = _sglue_to_sgpixelformat(sc.color_format); res.depth_format = _sglue_to_sgpixelformat(sc.depth_format); res.metal.current_drawable = sc.metal.current_drawable; res.metal.depth_stencil_texture = sc.metal.depth_stencil_texture; res.metal.msaa_color_texture = sc.metal.msaa_color_texture; res.d3d11.render_view = sc.d3d11.render_view; res.d3d11.resolve_view = sc.d3d11.resolve_view; res.d3d11.depth_stencil_view = sc.d3d11.depth_stencil_view; res.wgpu.render_view = sc.wgpu.render_view; res.wgpu.resolve_view = sc.wgpu.resolve_view; res.wgpu.depth_stencil_view = sc.wgpu.depth_stencil_view; res.vulkan.render_image = sc.vulkan.render_image; res.vulkan.render_view = sc.vulkan.render_view; res.vulkan.resolve_image = sc.vulkan.resolve_image; res.vulkan.resolve_view = sc.vulkan.resolve_view; res.vulkan.depth_stencil_image = sc.vulkan.depth_stencil_image; res.vulkan.depth_stencil_view = sc.vulkan.depth_stencil_view; res.vulkan.render_finished_semaphore = sc.vulkan.render_finished_semaphore; res.vulkan.present_complete_semaphore = sc.vulkan.present_complete_semaphore; res.gl.framebuffer = sc.gl.framebuffer; return res;}#endif /* SOKOL_GLUE_IMPL */vendor/sokol/sokol_gp.hdeleted
/*Minimal efficient cross platform 2D graphics painter for Sokol GFX.sokol_gp - v0.7.0 - 06/Dec/2024Eduardo Bart - [email protected]https://github.com/edubart/sokol_gp# Sokol GPMinimal efficient cross platform 2D graphics painter in pure Cusing modern graphics API through the excellent [Sokol GFX](https://github.com/floooh/sokol) library.Sokol GP, or in short SGP, stands for Sokol Graphics Painter.## Features* Made and optimized only for **2D rendering only**, no 3D support.* Minimal, in a pure single C header.* Use modern unfixed pipeline graphics APIs for more efficiency.* Cross platform (backed by Sokol GFX).* D3D11/OpenGL 3.3/Metal/WebGPU graphics backends (through Sokol GFX).* **Automatic batching** (merge recent draw calls into batches automatically).* **Batch optimizer** (rearranges the ordering of draw calls to batch more).* Uses preallocated memory (no allocations at runtime).* Supports drawing basic 2D primitives (rectangles, triangles, lines and points).* Supports the classic 2D color blending modes (color blend, add, modulate, multiply).* Supports 2D space transformations and changing 2D space coordinate systems.* Supports drawing the basic primitives (rectangles, triangles, lines and points).* Supports multiple texture bindings.* Supports custom fragment shaders with 2D primitives.* Can be mixed with projects that are already using Sokol GFX.## Why?Sokol GFX is an excellent library for rendering using unfixed pipelinesof modern graphics cards, but it is too complex to use for simple 2D drawing,and it's API is too generic and specialized for 3D rendering. To draw 2D stuff, the programmerusually needs to setup custom shaders when using Sokol GFX, or use its Sokol GLextra library, but Sokol GL also has an API with 3D design in mind, whichincurs some costs and limitations.This library was created to draw 2D primitives through Sokol GFX with ease,and by not considering 3D usage it is optimized for 2D rendering only,furthermore it features an **automatic batch optimizer**, more details of it will be described below.## Automatic batch optimizerWhen drawing the library creates a draw command queue of all primitives yet to be drawn,every time a new draw command is added the batch optimizer looks back up to the last8 recent draw commands (this is adjustable), and try to rearrange and merge drawing commandsif it finds a previous draw command that meets the following criteria:* The new draw command and previous command uses the *same primitive pipeline** The new draw command and previous command uses the *same shader uniforms** The new draw command and previous command uses the *same texture bindings** The new draw command and previous command does not have another intermediarydraw command *that overlaps* in-between them.By doing this the batch optimizer is able for example to merge textured draw calls,even if they were drawn with other intermediary different textures draws between them.The effect is more efficiency when drawing, because less draw calls will be dispatchedto the GPU,This library can avoid a lot of work of making an efficient 2D drawing batching system,by automatically merging draw calls behind the scenes at runtime,thus the programmer does not need to manage batched draw calls manually,nor he needs to sort batched texture draw calls,the library will do this seamlessly behind the scenes.The batching algorithm is fast, but it has `O(n)` CPU complexity for every new draw command added,where `n` is the `SGP_BATCH_OPTIMIZER_DEPTH` configuration.In experiments using `8` as the default is a good default,but you may want to try out different values depending on your case.Using values that are too high is not recommended, because the algorithm may take too longscanning previous draw commands, and that may consume more CPU resources.The batch optimizer can be disabled by setting `SGP_BATCH_OPTIMIZER_DEPTH` to 0,you can use that to measure its impact.In the samples directory of this repository there is abenchmark example that tests drawing with the bath optimizer enabled/disabled.On my machine that benchmark was able to increase performance in a 2.2x factor when it is enabled.In some private game projects the gains of the batch optimizer proved to increase FPS performanceabove 1.5x by just replacing the graphics backend with this library, with no internalchanges to the game itself.## Design choicesThe library has some design choices with performance in mind that will be discussed briefly here.Like Sokol GFX, Sokol GP will never do any allocation in the draw loop,so when initializing you must configure beforehand the maximum size of thedraw command queue buffer and the vertices buffer.All the 2D space transformation (functions like `sgp_rotate`) are done by the CPU and not by the GPU,this is intentionally to avoid adding extra overhead in the GPU, because typically the numberof vertices of 2D applications are not that large, and it is more efficient to performall the transformation with the CPU right away rather than pushing extra buffers to the GPUthat ends up using more bandwidth of the CPU<->GPU bus.In contrast 3D applications usually dispatches vertex transformations to the GPU using a vertex shader,they do this because the amount of vertices of 3D objects can be very largeand it is usually the best choice, but this is not true for 2D rendering.Many APIs to transform the 2D space before drawing a primitive are available, such astranslate, rotate and scale. They can be used as similarly as the ones available in 3D graphics APIs,but they are crafted for 2D only, for example when using 2D we don't need to use a 4x4 or 3x3 matrixto perform vertex transformation, instead the code is specialized for 2D and can use a 2x3 matrix,saving extra CPU float computations.All pipelines always use a texture associated with it, even when drawing non textured primitives,because this minimizes graphics pipeline changes when mixing textured calls and non textured calls,improving efficiency.The library is coded in the style of Sokol GFX headers, reusing many macros from there,you can change some of its semantics such as custom allocator, custom log function, and someother details, read `sokol_gfx.h` documentation for more on that.## UsageCopy `sokol_gp.h` along with other Sokol headers to the same folder. Setup Sokol GFXas you usually would, then add call to `sgp_setup(desc)` just after `sg_setup(desc)`, andcall to `sgp_shutdown()` just before `sg_shutdown()`. Note that you should usually check ifSGP is valid after its creation with `sgp_is_valid()` and exit gracefully with an error if not.In your frame draw function add `sgp_begin(width, height)` before calling any SGPdraw function, then draw your primitives. At the end of the frame (or framebuffer) youshould **ALWAYS call** `sgp_flush()` between a Sokol GFX begin/end render pass,the `sgp_flush()` will dispatch all draw commands to Sokol GFX. Then call `sgp_end()` immediatelyto discard the draw command queue.An actual example of this setup will be shown below.## Quick usage exampleThe following is a quick example on how to this library with Sokol GFX and Sokol APP:```c// This is an example on how to set up and use Sokol GP to draw a filled rectangle.// Includes Sokol GFX, Sokol GP and Sokol APP, doing all implementations.#define SOKOL_IMPL#include "sokol_gfx.h"#include "sokol_gp.h"#include "sokol_app.h"#include "sokol_glue.h"#include "sokol_log.h"#include <stdio.h> // for fprintf()#include <stdlib.h> // for exit()#include <math.h> // for sinf() and cosf()// Called on every frame of the application.static void frame(void) { // Get current window size. int width = sapp_width(), height = sapp_height(); float ratio = width/(float)height; // Begin recording draw commands for a frame buffer of size (width, height). sgp_begin(width, height); // Set frame buffer drawing region to (0,0,width,height). sgp_viewport(0, 0, width, height); // Set drawing coordinate space to (left=-ratio, right=ratio, top=1, bottom=-1). sgp_project(-ratio, ratio, 1.0f, -1.0f); // Clear the frame buffer. sgp_set_color(0.1f, 0.1f, 0.1f, 1.0f); sgp_clear(); // Draw an animated rectangle that rotates and changes its colors. float time = sapp_frame_count() * sapp_frame_duration(); float r = sinf(time)*0.5+0.5, g = cosf(time)*0.5+0.5; sgp_set_color(r, g, 0.3f, 1.0f); sgp_rotate_at(time, 0.0f, 0.0f); sgp_draw_filled_rect(-0.5f, -0.5f, 1.0f, 1.0f); // Begin a render pass. sg_pass pass = {.swapchain = sglue_swapchain()}; sg_begin_pass(&pass); // Dispatch all draw commands to Sokol GFX. sgp_flush(); // Finish a draw command queue, clearing it. sgp_end(); // End render pass. sg_end_pass(); // Commit Sokol render. sg_commit();}// Called when the application is initializing.static void init(void) { // Initialize Sokol GFX. sg_desc sgdesc = { .environment = sglue_environment(), .logger.func = slog_func }; sg_setup(&sgdesc); if (!sg_isvalid()) { fprintf(stderr, "Failed to create Sokol GFX context!\n"); exit(-1); } // Initialize Sokol GP, adjust the size of command buffers for your own use. sgp_desc sgpdesc = {0}; sgp_setup(&sgpdesc); if (!sgp_is_valid()) { fprintf(stderr, "Failed to create Sokol GP context: %s\n", sgp_get_error_message(sgp_get_last_error())); exit(-1); }}// Called when the application is shutting down.static void cleanup(void) { // Cleanup Sokol GP and Sokol GFX resources. sgp_shutdown(); sg_shutdown();}// Implement application main through Sokol APP.sapp_desc sokol_main(int argc, char* argv[]) { (void)argc; (void)argv; return (sapp_desc){ .init_cb = init, .frame_cb = frame, .cleanup_cb = cleanup, .window_title = "Rectangle (Sokol GP)", .logger.func = slog_func, };}```To run this example, first copy the `sokol_gp.h` header alongside with other Sokol headersto the same folder, then compile with any C compiler using the proper linking flags (read `sokol_gfx.h`).## Complete ExamplesIn folder `samples` you can find the following complete examples covering all APIs of the library:* [sample-primitives.c](https://github.com/edubart/sokol_gp/blob/master/samples/sample-primitives.c): This is an example showing all drawing primitives and transformations APIs.* [sample-blend.c](https://github.com/edubart/sokol_gp/blob/master/samples/sample-blend.c): This is an example showing all blend modes between 3 rectangles.* [sample-framebuffer.c](https://github.com/edubart/sokol_gp/blob/master/samples/sample-framebuffer.c): This is an example showing how to use multiple `sgp_begin()` with frame buffers.* [sample-sdf.c](https://github.com/edubart/sokol_gp/blob/master/samples/sample-sdf.c): This is an example on how to create custom shaders.* [sample-effect.c](https://github.com/edubart/sokol_gp/blob/master/samples/sample-effect.c): This is an example on how to use custom shaders for 2D drawing.* [sample-bench.c](https://github.com/edubart/sokol_gp/blob/master/samples/sample-bench.c): This is a heavy example used for benchmarking purposes.These examples are used as the test suite for the library, you can build them by typing `make`.## Error handlingIt is possible that after many draw calls the command or vertex buffer may overflow,in that case the library will set an error error state and will continue to operate normally,but when flushing the drawing command queue with `sgp_flush()` no draw command will be dispatched.This can happen because the library uses pre allocated buffers, in suchcases the issue can be fixed by increasing the prefixed command queue buffer and the vertices bufferwhen calling `sgp_setup()`.Making invalid number of push/pops of `sgp_push_transform()` and `sgp_pop_transform()`,or nesting too many `sgp_begin()` and `sgp_end()` may also lead to errors, thatis a usage mistake.You can enable the `SOKOL_DEBUG` macro in such cases to debug, or handlethe error programmatically by reading `sgp_get_last_error()` after calling `sgp_end()`.It is also advised to leave `SOKOL_DEBUG` enabled when developing with Sokol, so you cancatch mistakes early.## Blend modesThe library supports the most usual blend modes used in 2D, which are the following:- `SGP_BLENDMODE_NONE` - No blending (`dstRGBA = srcRGBA`).- `SGP_BLENDMODE_BLEND` - Alpha blending (`dstRGB = (srcRGB * srcA) + (dstRGB * (1-srcA))` and `dstA = srcA + (dstA * (1-srcA))`)- `SGP_BLENDMODE_BLEND_PREMULTIPLIED` - Pre-multiplied alpha blending (`dstRGBA = srcRGBA + (dstRGBA * (1-srcA))`)- `SGP_BLENDMODE_ADD` - Additive blending (`dstRGB = (srcRGB * srcA) + dstRGB` and `dstA = dstA`)- `SGP_BLENDMODE_ADD_PREMULTIPLIED` - Pre-multiplied additive blending (`dstRGB = srcRGB + dstRGB` and `dstA = dstA`)- `SGP_BLENDMODE_MOD` - Color modulate (`dstRGB = srcRGB * dstRGB` and `dstA = dstA`)- `SGP_BLENDMODE_MUL` - Color multiply (`dstRGB = (srcRGB * dstRGB) + (dstRGB * (1-srcA))` and `dstA = (srcA * dstA) + (dstA * (1-srcA))`)## Changing 2D coordinate systemYou can change the screen area to draw by calling `sgp_viewport(x, y, width, height)`.You can change the coordinate system of the 2D space by calling `sgp_project(left, right, top, bottom)`,with it.## Transforming 2D spaceYou can translate, rotate or scale the 2D space before a draw call, by using the transformationfunctions the library provides, such as `sgp_translate(x, y)`, `sgp_rotate(theta)`, etc.Check the cheat sheet or the header for more.To save and restore the transformation state you should call `sgp_push_transform()` andlater `sgp_pop_transform()`.## Drawing primitivesThe library provides drawing functions for all the basic primitives, that is,for points, lines, triangles and rectangles, such as `sgp_draw_line()` and `sgp_draw_filled_rect()`.Check the cheat sheet or the header for more.All of them have batched variations.## Drawing textured primitivesTo draw textured rectangles you can use `sgp_set_image(0, img)` and then sgp_draw_filled_rect()`,this will draw an entire texture into a rectangle.You should later reset the image with `sgp_reset_image(0)` to restore the bound image to default white image,otherwise you will have glitches when drawing a solid color.In case you want to draw a specific source from the texture,you should use `sgp_draw_textured_rect()` instead.By default textures are drawn using a simple nearest filter sampler,you can change the sampler with `sgp_set_sampler(0, smp)` before drawing a texture,it's recommended to restore the default sampler using `sgp_reset_sampler(0)`.## Color modulationAll common pipelines have color modulation, and you can modulatea color before a draw by setting the current state color with `sgp_set_color(r,g,b,a)`,later you should reset the color to default (white) with `sgp_reset_color()`.## Custom shadersWhen using a custom shader, you must create a pipeline for it with `sgp_make_pipeline(desc)`,using shader, blend mode and a draw primitive associated with it. Then you shouldcall `sgp_set_pipeline()` before the shader draw call. You are responsible for usingthe same blend mode and drawing primitive as the created pipeline.Custom uniforms can be passed to the shader with `sgp_set_uniform(vs_data, vs_size, fs_data, fs_size)`,where you should always pass a pointer to a struct with exactly the same schema and sizeas the one defined in the vertex and fragment shaders.Although you can create custom shaders for each graphics backend manually,it is advised should use the Sokol shader compiler [SHDC](https://github.com/floooh/sokol-tools/blob/master/docs/sokol-shdc.md),because it can generate shaders for multiple backends from a single `.glsl` file,and this usually works well.By default the library uniform buffer per draw call has just 8 float uniforms(`SGP_UNIFORM_CONTENT_SLOTS` configuration), and that may be too low to use with custom shaders.This is the default because typically newcomers may not want to use custom 2D shaders,and increasing a larger value means more overhead.If you are using custom shaders please increase this value to be large enough to holdthe number of uniforms of your largest shader.## Library configurationThe following macros can be defined before including to change the library behavior:- `SGP_BATCH_OPTIMIZER_DEPTH` - Number of draw commands that the batch optimizer looks back at. Default is 8.- `SGP_UNIFORM_CONTENT_SLOTS` - Maximum number of floats that can be stored in each draw call uniform buffer. Default is 8.- `SGP_TEXTURE_SLOTS` - Maximum number of textures that can be bound per draw call. Default is 4.## LicenseMIT, see LICENSE file or the end of `sokol_gp.h` file.*/#if defined(SOKOL_IMPL) && !defined(SOKOL_GP_IMPL)#define SOKOL_GP_IMPL#endif#ifndef SOKOL_GP_INCLUDED#define SOKOL_GP_INCLUDED 1#ifndef SOKOL_GFX_INCLUDED#error "Please include sokol_gfx.h before sokol_gp.h"#endif/* Number of draw commands that the batch optimizer looks back at.8 is a fair default value, but could be tuned per application.1 makes the batch optimizer try to merge only the very last draw call.0 disables the batch optimizer*/#ifndef SGP_BATCH_OPTIMIZER_DEPTH#define SGP_BATCH_OPTIMIZER_DEPTH 8#endif/* Number of uniform floats (4-bytes) slots that can be set in a shader.Increase this value if you need to use shader with many uniforms.*/#ifndef SGP_UNIFORM_CONTENT_SLOTS#define SGP_UNIFORM_CONTENT_SLOTS 8#endif/* Number of texture slots that can be bound in a pipeline. */#ifndef SGP_TEXTURE_SLOTS#define SGP_TEXTURE_SLOTS 4#endif#if defined(SOKOL_API_DECL) && !defined(SOKOL_GP_API_DECL)#define SOKOL_GP_API_DECL SOKOL_API_DECL#endif#ifndef SOKOL_GP_API_DECL#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_GP_IMPL)#define SOKOL_GP_API_DECL __declspec(dllexport)#elif defined(_WIN32) && defined(SOKOL_DLL)#define SOKOL_GP_API_DECL __declspec(dllimport)#else#define SOKOL_GP_API_DECL extern#endif#endif#ifndef SOKOL_LOG #ifdef SOKOL_DEBUG #include <stdio.h> #define SOKOL_LOG(s) { SOKOL_ASSERT(s); puts(s); } #else #define SOKOL_LOG(s) #endif#endif#include <stdbool.h>#include <stdint.h>#ifdef __cplusplusextern "C" {#endif/* List of possible error codes. */typedef enum sgp_error { SGP_NO_ERROR = 0, SGP_ERROR_SOKOL_INVALID, SGP_ERROR_VERTICES_FULL, SGP_ERROR_UNIFORMS_FULL, SGP_ERROR_COMMANDS_FULL, SGP_ERROR_VERTICES_OVERFLOW, SGP_ERROR_TRANSFORM_STACK_OVERFLOW, SGP_ERROR_TRANSFORM_STACK_UNDERFLOW, SGP_ERROR_STATE_STACK_OVERFLOW, SGP_ERROR_STATE_STACK_UNDERFLOW, SGP_ERROR_ALLOC_FAILED, SGP_ERROR_MAKE_VERTEX_BUFFER_FAILED, SGP_ERROR_MAKE_WHITE_IMAGE_FAILED, SGP_ERROR_MAKE_WHITE_VIEW_FAILED, SGP_ERROR_MAKE_NEAREST_SAMPLER_FAILED, SGP_ERROR_MAKE_COMMON_SHADER_FAILED, SGP_ERROR_MAKE_COMMON_PIPELINE_FAILED,} sgp_error;/* Blend modes. */typedef enum sgp_blend_mode { SGP_BLENDMODE_NONE = 0, /* No blending dstRGBA = srcRGBA */ SGP_BLENDMODE_BLEND, /* Alpha blending. dstRGB = (srcRGB * srcA) + (dstRGB * (1-srcA)) dstA = srcA + (dstA * (1-srcA)) */ SGP_BLENDMODE_BLEND_PREMULTIPLIED, /* Pre-multiplied alpha blending. dstRGBA = srcRGBA + (dstRGBA * (1-srcA)) */ SGP_BLENDMODE_ADD, /* Additive blending. dstRGB = (srcRGB * srcA) + dstRGB dstA = dstA */ SGP_BLENDMODE_ADD_PREMULTIPLIED, /* Pre-multiplied additive blending. dstRGB = srcRGB + dstRGB dstA = dstA */ SGP_BLENDMODE_MOD, /* Color modulate. dstRGB = srcRGB * dstRGB dstA = dstA */ SGP_BLENDMODE_MUL, /* Color multiply. dstRGB = (srcRGB * dstRGB) + (dstRGB * (1-srcA)) dstA = (srcA * dstA) + (dstA * (1-srcA)) */ _SGP_BLENDMODE_NUM} sgp_blend_mode;typedef enum sgp_vs_attr_location { SGP_VS_ATTR_COORD = 0, SGP_VS_ATTR_COLOR = 1} sgp_vs_attr_location;typedef enum sgp_uniform_slot { SGP_UNIFORM_SLOT_VERTEX = 0, SGP_UNIFORM_SLOT_FRAGMENT = 1} sgp_uniform_slot;typedef struct sgp_isize { int w, h;} sgp_isize;typedef struct sgp_irect { int x, y, w, h;} sgp_irect;typedef struct sgp_rect { float x, y, w, h;} sgp_rect;typedef struct sgp_textured_rect { sgp_rect dst; sgp_rect src;} sgp_textured_rect;typedef struct sgp_vec2 { float x, y;} sgp_vec2;typedef sgp_vec2 sgp_point;typedef struct sgp_line { sgp_point a, b;} sgp_line;Showing the first 500 of 3114 diff lines for this file. This diff is INCOMPLETE; read the file or clone the repository for the rest.
vendor/sokol/sokol_log.hdeleted
#if defined(SOKOL_IMPL) && !defined(SOKOL_LOG_IMPL)#define SOKOL_LOG_IMPL#endif#ifndef SOKOL_LOG_INCLUDED/* sokol_log.h -- common logging callback for sokol headers Project URL: https://github.com/floooh/sokol Example code: https://github.com/floooh/sokol-samples Do this: #define SOKOL_IMPL or #define SOKOL_LOG_IMPL before you include this file in *one* C or C++ file to create the implementation. Optionally provide the following defines when building the implementation: SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) SOKOL_UNREACHABLE() - a guard macro for unreachable code (default: assert(false)) SOKOL_LOG_API_DECL - public function declaration prefix (default: extern) SOKOL_API_DECL - same as SOKOL_GFX_API_DECL SOKOL_API_IMPL - public function implementation prefix (default: -) Optionally define the following for verbose output: SOKOL_DEBUG - by default this is defined if NDEBUG is not defined OVERVIEW ======== sokol_log.h provides a default logging callback for other sokol headers. To use the default log callback, just include sokol_log.h and provide a function pointer to the 'slog_func' function when setting up the sokol library: For instance with sokol_audio.h: #include "sokol_log.h" ... saudio_setup(&(saudio_desc){ .logger.func = slog_func }); Logging output goes to stderr and/or a platform specific logging subsystem (which means that in some scenarios you might see logging messages duplicated): - Windows: stderr + OutputDebugStringA() - macOS/iOS/Linux: stderr + syslog() - Emscripten: console.info()/warn()/error() - Android: __android_log_write() On Windows with sokol_app.h also note the runtime config items to make stdout/stderr output visible on the console for WinMain() applications via sapp_desc.win32.console_attach or sapp_desc.win32.console_create, however when running in a debugger on Windows, the logging output should show up on the debug output UI panel. In debug mode, a log message might look like this: [sspine][error][id:12] /Users/floh/projects/sokol/util/sokol_spine.h:3472:0: SKELETON_DESC_NO_ATLAS: no atlas object provided in sspine_skeleton_desc.atlas The source path and line number is formatted like compiler errors, in some IDEs (like VSCode) such error messages are clickable. In release mode, logging is less verbose as to not bloat the executable with string data, but you still get enough information to identify the type and location of an error: [sspine][error][id:12][line:3472] RULES FOR WRITING YOUR OWN LOGGING FUNCTION =========================================== - must be re-entrant because it might be called from different threads - must treat **all** provided string pointers as optional (can be null) - don't store the string pointers, copy the string data instead - must not return for log level panic LICENSE ======= zlib/libpng license Copyright (c) 2023 Andre Weissflog This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution.*/#define SOKOL_LOG_INCLUDED (1)#include <stdint.h>#if defined(SOKOL_API_DECL) && !defined(SOKOL_LOG_API_DECL)#define SOKOL_LOG_API_DECL SOKOL_API_DECL#endif#ifndef SOKOL_LOG_API_DECL#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_LOG_IMPL)#define SOKOL_LOG_API_DECL __declspec(dllexport)#elif defined(_WIN32) && defined(SOKOL_DLL)#define SOKOL_LOG_API_DECL __declspec(dllimport)#else#define SOKOL_LOG_API_DECL extern#endif#endif#ifdef __cplusplusextern "C" {#endif/* Plug this function into the 'logger.func' struct item when initializing any of the sokol headers. For instance for sokol_audio.h it would look like this: saudio_setup(&(saudio_desc){ .logger = { .func = slog_func } });*/SOKOL_LOG_API_DECL void slog_func(const char* tag, uint32_t log_level, uint32_t log_item, const char* message, uint32_t line_nr, const char* filename, void* user_data);#ifdef __cplusplus} // extern "C"#endif#endif // SOKOL_LOG_INCLUDED// ██ ███ ███ ██████ ██ ███████ ███ ███ ███████ ███ ██ ████████ █████ ████████ ██ ██████ ███ ██// ██ ████ ████ ██ ██ ██ ██ ████ ████ ██ ████ ██ ██ ██ ██ ██ ██ ██ ██ ████ ██// ██ ██ ████ ██ ██████ ██ █████ ██ ████ ██ █████ ██ ██ ██ ██ ███████ ██ ██ ██ ██ ██ ██ ██// ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██// ██ ██ ██ ██ ███████ ███████ ██ ██ ███████ ██ ████ ██ ██ ██ ██ ██ ██████ ██ ████//// >>implementation#ifdef SOKOL_LOG_IMPL#define SOKOL_LOG_IMPL_INCLUDED (1)#ifndef SOKOL_API_IMPL #define SOKOL_API_IMPL#endif#ifndef SOKOL_DEBUG #ifndef NDEBUG #define SOKOL_DEBUG #endif#endif#ifndef SOKOL_ASSERT #include <assert.h> #define SOKOL_ASSERT(c) assert(c)#endif#ifndef _SOKOL_PRIVATE #if defined(__GNUC__) || defined(__clang__) #define _SOKOL_PRIVATE __attribute__((unused)) static #else #define _SOKOL_PRIVATE static #endif#endif#ifndef _SOKOL_UNUSED #define _SOKOL_UNUSED(x) (void)(x)#endif// platform detection#if defined(__APPLE__) #define _SLOG_APPLE (1)#elif defined(__EMSCRIPTEN__) #define _SLOG_EMSCRIPTEN (1)#elif defined(_WIN32) #define _SLOG_WINDOWS (1)#elif defined(__ANDROID__) #define _SLOG_ANDROID (1)#elif defined(__linux__) || defined(__unix__) #define _SLOG_LINUX (1)#else#error "sokol_log.h: unknown platform"#endif#include <stdlib.h> // abort#include <stdio.h> // fputs#include <stddef.h> // size_t#if defined(_SLOG_EMSCRIPTEN)#include <emscripten/emscripten.h>#elif defined(_SLOG_WINDOWS)#ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN#endif#ifndef NOMINMAX #define NOMINMAX#endif#include <windows.h>#elif defined(_SLOG_ANDROID)#include <android/log.h>#elif defined(_SLOG_LINUX) || defined(_SLOG_APPLE)#include <syslog.h>#endif// size of line buffer (on stack!) in bytes including terminating zero#define _SLOG_LINE_LENGTH (512)_SOKOL_PRIVATE char* _slog_append(const char* str, char* dst, char* end) { if (str) { char c; while (((c = *str++) != 0) && (dst < (end - 1))) { *dst++ = c; } } *dst = 0; return dst;}_SOKOL_PRIVATE char* _slog_itoa(uint32_t x, char* buf, size_t buf_size) { const size_t max_digits_and_null = 11; if (buf_size < max_digits_and_null) { return 0; } char* p = buf + max_digits_and_null; *--p = 0; do { *--p = '0' + (x % 10); x /= 10; } while (x != 0); return p;}#if defined(_SLOG_EMSCRIPTEN)EM_JS(void, slog_js_log, (uint32_t level, const char* c_str), { const str = UTF8ToString(c_str); switch (level) { case 0: console.error(str); break; case 1: console.error(str); break; case 2: console.warn(str); break; default: console.info(str); break; }})#endifSOKOL_API_IMPL void slog_func(const char* tag, uint32_t log_level, uint32_t log_item, const char* message, uint32_t line_nr, const char* filename, void* user_data) { _SOKOL_UNUSED(user_data); const char* log_level_str; switch (log_level) { case 0: log_level_str = "panic"; break; case 1: log_level_str = "error"; break; case 2: log_level_str = "warning"; break; default: log_level_str = "info"; break; } // build log output line char line_buf[_SLOG_LINE_LENGTH]; char* str = line_buf; char* end = line_buf + sizeof(line_buf); char num_buf[32]; if (tag) { str = _slog_append("[", str, end); str = _slog_append(tag, str, end); str = _slog_append("]", str, end); } str = _slog_append("[", str, end); str = _slog_append(log_level_str, str, end); str = _slog_append("]", str, end); str = _slog_append("[id:", str, end); str = _slog_append(_slog_itoa(log_item, num_buf, sizeof(num_buf)), str, end); str = _slog_append("]", str, end); // if a filename is provided, build a clickable log message that's compatible with compiler error messages if (filename) { str = _slog_append(" ", str, end); #if defined(_MSC_VER) // MSVC compiler error format str = _slog_append(filename, str, end); str = _slog_append("(", str, end); str = _slog_append(_slog_itoa(line_nr, num_buf, sizeof(num_buf)), str, end); str = _slog_append("): ", str, end); #else // gcc/clang compiler error format str = _slog_append(filename, str, end); str = _slog_append(":", str, end); str = _slog_append(_slog_itoa(line_nr, num_buf, sizeof(num_buf)), str, end); str = _slog_append(":0: ", str, end); #endif } else { str = _slog_append("[line:", str, end); str = _slog_append(_slog_itoa(line_nr, num_buf, sizeof(num_buf)), str, end); str = _slog_append("] ", str, end); } if (message) { str = _slog_append("\n\t", str, end); str = _slog_append(message, str, end); } str = _slog_append("\n\n", str, end); if (0 == log_level) { str = _slog_append("ABORTING because of [panic]\n", str, end); (void)str; } // print to stderr? #if defined(_SLOG_LINUX) || defined(_SLOG_WINDOWS) || defined(_SLOG_APPLE) fputs(line_buf, stderr); #endif // platform specific logging calls #if defined(_SLOG_WINDOWS) OutputDebugStringA(line_buf); #elif defined(_SLOG_ANDROID) int prio; switch (log_level) { case 0: prio = ANDROID_LOG_FATAL; break; case 1: prio = ANDROID_LOG_ERROR; break; case 2: prio = ANDROID_LOG_WARN; break; default: prio = ANDROID_LOG_INFO; break; } __android_log_write(prio, "SOKOL", line_buf); #elif defined(_SLOG_EMSCRIPTEN) slog_js_log(log_level, line_buf); #endif if (0 == log_level) { abort(); }}#endif // SOKOL_LOG_IMPLvendor/sokol/sokol_time.hdeleted
#if defined(SOKOL_IMPL) && !defined(SOKOL_TIME_IMPL)#define SOKOL_TIME_IMPL#endif#ifndef SOKOL_TIME_INCLUDED/* sokol_time.h -- simple cross-platform time measurement Project URL: https://github.com/floooh/sokol Do this: #define SOKOL_IMPL or #define SOKOL_TIME_IMPL before you include this file in *one* C or C++ file to create the implementation. Optionally provide the following defines with your own implementations: SOKOL_ASSERT(c) - your own assert macro (default: assert(c)) SOKOL_TIME_API_DECL - public function declaration prefix (default: extern) SOKOL_API_DECL - same as SOKOL_TIME_API_DECL SOKOL_API_IMPL - public function implementation prefix (default: -) If sokol_time.h is compiled as a DLL, define the following before including the declaration or implementation: SOKOL_DLL On Windows, SOKOL_DLL will define SOKOL_TIME_API_DECL as __declspec(dllexport) or __declspec(dllimport) as needed. void stm_setup(); Call once before any other functions to initialize sokol_time (this calls for instance QueryPerformanceFrequency on Windows) uint64_t stm_now(); Get current point in time in unspecified 'ticks'. The value that is returned has no relation to the 'wall-clock' time and is not in a specific time unit, it is only useful to compute time differences. uint64_t stm_diff(uint64_t new, uint64_t old); Computes the time difference between new and old. This will always return a positive, non-zero value. uint64_t stm_since(uint64_t start); Takes the current time, and returns the elapsed time since start (this is a shortcut for "stm_diff(stm_now(), start)") uint64_t stm_laptime(uint64_t* last_time); This is useful for measuring frame time and other recurring events. It takes the current time, returns the time difference to the value in last_time, and stores the current time in last_time for the next call. If the value in last_time is 0, the return value will be zero (this usually happens on the very first call). uint64_t stm_round_to_common_refresh_rate(uint64_t duration) This oddly named function takes a measured frame time and returns the closest "nearby" common display refresh rate frame duration in ticks. If the input duration isn't close to any common display refresh rate, the input duration will be returned unchanged as a fallback. The main purpose of this function is to remove jitter/inaccuracies from measured frame times, and instead use the display refresh rate as frame duration. NOTE: for more robust frame timing, consider using the sokol_app.h function sapp_frame_duration() Use the following functions to convert a duration in ticks into useful time units: double stm_sec(uint64_t ticks); double stm_ms(uint64_t ticks); double stm_us(uint64_t ticks); double stm_ns(uint64_t ticks); Converts a tick value into seconds, milliseconds, microseconds or nanoseconds. Note that not all platforms will have nanosecond or even microsecond precision. Uses the following time measurement functions under the hood: Windows: QueryPerformanceFrequency() / QueryPerformanceCounter() MacOS/iOS: mach_absolute_time() emscripten: emscripten_get_now() Linux+others: clock_gettime(CLOCK_MONOTONIC) zlib/libpng license Copyright (c) 2018 Andre Weissflog This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution.*/#define SOKOL_TIME_INCLUDED (1)#include <stdint.h>#if defined(SOKOL_API_DECL) && !defined(SOKOL_TIME_API_DECL)#define SOKOL_TIME_API_DECL SOKOL_API_DECL#endif#ifndef SOKOL_TIME_API_DECL#if defined(_WIN32) && defined(SOKOL_DLL) && defined(SOKOL_TIME_IMPL)#define SOKOL_TIME_API_DECL __declspec(dllexport)#elif defined(_WIN32) && defined(SOKOL_DLL)#define SOKOL_TIME_API_DECL __declspec(dllimport)#else#define SOKOL_TIME_API_DECL extern#endif#endif#ifdef __cplusplusextern "C" {#endifSOKOL_TIME_API_DECL void stm_setup(void);SOKOL_TIME_API_DECL uint64_t stm_now(void);SOKOL_TIME_API_DECL uint64_t stm_diff(uint64_t new_ticks, uint64_t old_ticks);SOKOL_TIME_API_DECL uint64_t stm_since(uint64_t start_ticks);SOKOL_TIME_API_DECL uint64_t stm_laptime(uint64_t* last_time);SOKOL_TIME_API_DECL uint64_t stm_round_to_common_refresh_rate(uint64_t frame_ticks);SOKOL_TIME_API_DECL double stm_sec(uint64_t ticks);SOKOL_TIME_API_DECL double stm_ms(uint64_t ticks);SOKOL_TIME_API_DECL double stm_us(uint64_t ticks);SOKOL_TIME_API_DECL double stm_ns(uint64_t ticks);#ifdef __cplusplus} /* extern "C" */#endif#endif // SOKOL_TIME_INCLUDED/*-- IMPLEMENTATION ----------------------------------------------------------*/#ifdef SOKOL_TIME_IMPL#define SOKOL_TIME_IMPL_INCLUDED (1)#include <string.h> /* memset */#ifndef SOKOL_API_IMPL #define SOKOL_API_IMPL#endif#ifndef SOKOL_ASSERT #include <assert.h> #define SOKOL_ASSERT(c) assert(c)#endif#ifndef _SOKOL_PRIVATE #if defined(__GNUC__) || defined(__clang__) #define _SOKOL_PRIVATE __attribute__((unused)) static #else #define _SOKOL_PRIVATE static #endif#endif#if defined(_WIN32)#ifndef WIN32_LEAN_AND_MEAN#define WIN32_LEAN_AND_MEAN#endif#include <windows.h>typedef struct { uint32_t initialized; LARGE_INTEGER freq; LARGE_INTEGER start;} _stm_state_t;#elif defined(__APPLE__) && defined(__MACH__)#include <mach/mach_time.h>typedef struct { uint32_t initialized; mach_timebase_info_data_t timebase; uint64_t start;} _stm_state_t;#elif defined(__EMSCRIPTEN__)#include <emscripten/emscripten.h>typedef struct { uint32_t initialized; double start;} _stm_state_t;#else /* anything else, this will need more care for non-Linux platforms */#ifdef ESP8266// On the ESP8266, clock_gettime ignores the first argument and CLOCK_MONOTONIC isn't defined#define CLOCK_MONOTONIC 0#endif#include <time.h>typedef struct { uint32_t initialized; uint64_t start;} _stm_state_t;#endifstatic _stm_state_t _stm;/* prevent 64-bit overflow when computing relative timestamp see https://gist.github.com/jspohr/3dc4f00033d79ec5bdaf67bc46c813e3*/#if defined(_WIN32) || (defined(__APPLE__) && defined(__MACH__))_SOKOL_PRIVATE int64_t _stm_int64_muldiv(int64_t value, int64_t numer, int64_t denom) { int64_t q = value / denom; int64_t r = value % denom; return q * numer + r * numer / denom;}#endifSOKOL_API_IMPL void stm_setup(void) { memset(&_stm, 0, sizeof(_stm)); _stm.initialized = 0xABCDABCD; #if defined(_WIN32) QueryPerformanceFrequency(&_stm.freq); QueryPerformanceCounter(&_stm.start); #elif defined(__APPLE__) && defined(__MACH__) mach_timebase_info(&_stm.timebase); _stm.start = mach_absolute_time(); #elif defined(__EMSCRIPTEN__) _stm.start = emscripten_get_now(); #else struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); _stm.start = (uint64_t)ts.tv_sec*1000000000 + (uint64_t)ts.tv_nsec; #endif}SOKOL_API_IMPL uint64_t stm_now(void) { SOKOL_ASSERT(_stm.initialized == 0xABCDABCD); uint64_t now; #if defined(_WIN32) LARGE_INTEGER qpc_t; QueryPerformanceCounter(&qpc_t); now = (uint64_t) _stm_int64_muldiv(qpc_t.QuadPart - _stm.start.QuadPart, 1000000000, _stm.freq.QuadPart); #elif defined(__APPLE__) && defined(__MACH__) const uint64_t mach_now = mach_absolute_time() - _stm.start; now = (uint64_t) _stm_int64_muldiv((int64_t)mach_now, (int64_t)_stm.timebase.numer, (int64_t)_stm.timebase.denom); #elif defined(__EMSCRIPTEN__) double js_now = emscripten_get_now() - _stm.start; now = (uint64_t) (js_now * 1000000.0); #else struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); now = ((uint64_t)ts.tv_sec*1000000000 + (uint64_t)ts.tv_nsec) - _stm.start; #endif return now;}SOKOL_API_IMPL uint64_t stm_diff(uint64_t new_ticks, uint64_t old_ticks) { if (new_ticks > old_ticks) { return new_ticks - old_ticks; } else { return 1; }}SOKOL_API_IMPL uint64_t stm_since(uint64_t start_ticks) { return stm_diff(stm_now(), start_ticks);}SOKOL_API_IMPL uint64_t stm_laptime(uint64_t* last_time) { SOKOL_ASSERT(last_time); uint64_t dt = 0; uint64_t now = stm_now(); if (0 != *last_time) { dt = stm_diff(now, *last_time); } *last_time = now; return dt;}// first number is frame duration in ns, second number is tolerance in ns,// the resulting min/max values must not overlap!static const uint64_t _stm_refresh_rates[][2] = { { 16666667, 1000000 }, // 60 Hz: 16.6667 +- 1ms { 13888889, 250000 }, // 72 Hz: 13.8889 +- 0.25ms { 13333333, 250000 }, // 75 Hz: 13.3333 +- 0.25ms { 11764706, 250000 }, // 85 Hz: 11.7647 +- 0.25 { 11111111, 250000 }, // 90 Hz: 11.1111 +- 0.25ms { 10000000, 500000 }, // 100 Hz: 10.0000 +- 0.5ms { 8333333, 500000 }, // 120 Hz: 8.3333 +- 0.5ms { 6944445, 500000 }, // 144 Hz: 6.9445 +- 0.5ms { 4166667, 1000000 }, // 240 Hz: 4.1666 +- 1ms { 0, 0 }, // keep the last element always at zero};SOKOL_API_IMPL uint64_t stm_round_to_common_refresh_rate(uint64_t ticks) { uint64_t ns; int i = 0; while (0 != (ns = _stm_refresh_rates[i][0])) { uint64_t tol = _stm_refresh_rates[i][1]; if ((ticks > (ns - tol)) && (ticks < (ns + tol))) { return ns; } i++; } // fallthrough: didn't fit into any buckets return ticks;}SOKOL_API_IMPL double stm_sec(uint64_t ticks) { return (double)ticks / 1000000000.0;}SOKOL_API_IMPL double stm_ms(uint64_t ticks) { return (double)ticks / 1000000.0;}SOKOL_API_IMPL double stm_us(uint64_t ticks) { return (double)ticks / 1000.0;}SOKOL_API_IMPL double stm_ns(uint64_t ticks) { return (double)ticks;}#endif /* SOKOL_TIME_IMPL */vendor/stb/stb_image.hdeleted
/* stb_image - v2.30 - public domain image loader - http://nothings.org/stb no warranty implied; use at your own risk Do this: #define STB_IMAGE_IMPLEMENTATION before you include this file in *one* C or C++ file to create the implementation. // i.e. it should look like this: #include ... #include ... #include ... #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" You can #define STBI_ASSERT(x) before the #include to avoid using assert.h. And #define STBI_MALLOC, STBI_REALLOC, and STBI_FREE to avoid using malloc,realloc,free QUICK NOTES: Primarily of interest to game developers and other people who can avoid problematic images and only need the trivial interface JPEG baseline & progressive (12 bpc/arithmetic not supported, same as stock IJG lib) PNG 1/2/4/8/16-bit-per-channel TGA (not sure what subset, if a subset) BMP non-1bpp, non-RLE PSD (composited view only, no extra channels, 8/16 bit-per-channel) GIF (*comp always reports as 4-channel) HDR (radiance rgbE format) PIC (Softimage PIC) PNM (PPM and PGM binary only) Animated GIF still needs a proper API, but here's one way to do it: http://gist.github.com/urraka/685d9a6340b26b830d49 - decode from memory or through FILE (define STBI_NO_STDIO to remove code) - decode from arbitrary I/O callbacks - SIMD acceleration on x86/x64 (SSE2) and ARM (NEON) Full documentation under "DOCUMENTATION" below.LICENSE See end of file for license information.RECENT REVISION HISTORY: 2.30 (2024-05-31) avoid erroneous gcc warning 2.29 (2023-05-xx) optimizations 2.28 (2023-01-29) many error fixes, security errors, just tons of stuff 2.27 (2021-07-11) document stbi_info better, 16-bit PNM support, bug fixes 2.26 (2020-07-13) many minor fixes 2.25 (2020-02-02) fix warnings 2.24 (2020-02-02) fix warnings; thread-local failure_reason and flip_vertically 2.23 (2019-08-11) fix clang static analysis warning 2.22 (2019-03-04) gif fixes, fix warnings 2.21 (2019-02-25) fix typo in comment 2.20 (2019-02-07) support utf8 filenames in Windows; fix warnings and platform ifdefs 2.19 (2018-02-11) fix warning 2.18 (2018-01-30) fix warnings 2.17 (2018-01-29) bugfix, 1-bit BMP, 16-bitness query, fix warnings 2.16 (2017-07-23) all functions have 16-bit variants; optimizations; bugfixes 2.15 (2017-03-18) fix png-1,2,4; all Imagenet JPGs; no runtime SSE detection on GCC 2.14 (2017-03-03) remove deprecated STBI_JPEG_OLD; fixes for Imagenet JPGs 2.13 (2016-12-04) experimental 16-bit API, only for PNG so far; fixes 2.12 (2016-04-02) fix typo in 2.11 PSD fix that caused crashes 2.11 (2016-04-02) 16-bit PNGS; enable SSE2 in non-gcc x64 RGB-format JPEG; remove white matting in PSD; allocate large structures on the stack; correct channel count for PNG & BMP 2.10 (2016-01-22) avoid warning introduced in 2.09 2.09 (2016-01-16) 16-bit TGA; comments in PNM files; STBI_REALLOC_SIZED See end of file for full revision history. ============================ Contributors ========================= Image formats Extensions, features Sean Barrett (jpeg, png, bmp) Jetro Lauha (stbi_info) Nicolas Schulz (hdr, psd) Martin "SpartanJ" Golini (stbi_info) Jonathan Dummer (tga) James "moose2000" Brown (iPhone PNG) Jean-Marc Lienher (gif) Ben "Disch" Wenger (io callbacks) Tom Seddon (pic) Omar Cornut (1/2/4-bit PNG) Thatcher Ulrich (psd) Nicolas Guillemot (vertical flip) Ken Miller (pgm, ppm) Richard Mitton (16-bit PSD) github:urraka (animated gif) Junggon Kim (PNM comments) Christopher Forseth (animated gif) Daniel Gibson (16-bit TGA) socks-the-fox (16-bit PNG) Jeremy Sawicki (handle all ImageNet JPGs) Optimizations & bugfixes Mikhail Morozov (1-bit BMP) Fabian "ryg" Giesen Anael Seghezzi (is-16-bit query) Arseny Kapoulkine Simon Breuss (16-bit PNM) John-Mark Allen Carmelo J Fdez-Aguera Bug & warning fixes Marc LeBlanc David Woo Guillaume George Martins Mozeiko Christpher Lloyd Jerry Jansson Joseph Thomson Blazej Dariusz Roszkowski Phil Jordan Dave Moore Roy Eltham Hayaki Saito Nathan Reed Won Chun Luke Graham Johan Duparc Nick Verigakis the Horde3D community Thomas Ruf Ronny Chevalier github:rlyeh Janez Zemva John Bartholomew Michal Cichon github:romigrou Jonathan Blow Ken Hamada Tero Hanninen github:svdijk Eugene Golushkov Laurent Gomila Cort Stratton github:snagar Aruelien Pocheville Sergio Gonzalez Thibault Reuille github:Zelex Cass Everitt Ryamond Barbiero github:grim210 Paul Du Bois Engin Manap Aldo Culquicondor github:sammyhw Philipp Wiesemann Dale Weiler Oriol Ferrer Mesia github:phprus Josh Tobin Neil Bickford Matthew Gregan github:poppolopoppo Julian Raschke Gregory Mullen Christian Floisand github:darealshinji Baldur Karlsson Kevin Schmidt JR Smith github:Michaelangel007 Brad Weinberger Matvey Cherevko github:mosra Luca Sas Alexander Veselov Zack Middleton [reserved] Ryan C. Gordon [reserved] [reserved] DO NOT ADD YOUR NAME HERE Jacko Dirks To add your name to the credits, pick a random blank space in the middle and fill it. 80% of merge conflicts on stb PRs are due to people adding their name at the end of the credits.*/#ifndef STBI_INCLUDE_STB_IMAGE_H#define STBI_INCLUDE_STB_IMAGE_H// DOCUMENTATION//// Limitations:// - no 12-bit-per-channel JPEG// - no JPEGs with arithmetic coding// - GIF always returns *comp=4//// Basic usage (see HDR discussion below for HDR usage):// int x,y,n;// unsigned char *data = stbi_load(filename, &x, &y, &n, 0);// // ... process data if not NULL ...// // ... x = width, y = height, n = # 8-bit components per pixel ...// // ... replace '0' with '1'..'4' to force that many components per pixel// // ... but 'n' will always be the number that it would have been if you said 0// stbi_image_free(data);//// Standard parameters:// int *x -- outputs image width in pixels// int *y -- outputs image height in pixels// int *channels_in_file -- outputs # of image components in image file// int desired_channels -- if non-zero, # of image components requested in result//// The return value from an image loader is an 'unsigned char *' which points// to the pixel data, or NULL on an allocation failure or if the image is// corrupt or invalid. The pixel data consists of *y scanlines of *x pixels,// with each pixel consisting of N interleaved 8-bit components; the first// pixel pointed to is top-left-most in the image. There is no padding between// image scanlines or between pixels, regardless of format. The number of// components N is 'desired_channels' if desired_channels is non-zero, or// *channels_in_file otherwise. If desired_channels is non-zero,// *channels_in_file has the number of components that _would_ have been// output otherwise. E.g. if you set desired_channels to 4, you will always// get RGBA output, but you can check *channels_in_file to see if it's trivially// opaque because e.g. there were only 3 channels in the source image.//// An output image with N components has the following components interleaved// in this order in each pixel://// N=#comp components// 1 grey// 2 grey, alpha// 3 red, green, blue// 4 red, green, blue, alpha//// If image loading fails for any reason, the return value will be NULL,// and *x, *y, *channels_in_file will be unchanged. The function// stbi_failure_reason() can be queried for an extremely brief, end-user// unfriendly explanation of why the load failed. Define STBI_NO_FAILURE_STRINGS// to avoid compiling these strings at all, and STBI_FAILURE_USERMSG to get slightly// more user-friendly ones.//// Paletted PNG, BMP, GIF, and PIC images are automatically depalettized.//// To query the width, height and component count of an image without having to// decode the full file, you can use the stbi_info family of functions://// int x,y,n,ok;// ok = stbi_info(filename, &x, &y, &n);// // returns ok=1 and sets x, y, n if image is a supported format,// // 0 otherwise.//// Note that stb_image pervasively uses ints in its public API for sizes,// including sizes of memory buffers. This is now part of the API and thus// hard to change without causing breakage. As a result, the various image// loaders all have certain limits on image size; these differ somewhat// by format but generally boil down to either just under 2GB or just under// 1GB. When the decoded image would be larger than this, stb_image decoding// will fail.//// Additionally, stb_image will reject image files that have any of their// dimensions set to a larger value than the configurable STBI_MAX_DIMENSIONS,// which defaults to 2**24 = 16777216 pixels. Due to the above memory limit,// the only way to have an image with such dimensions load correctly// is for it to have a rather extreme aspect ratio. Either way, the// assumption here is that such larger images are likely to be malformed// or malicious. If you do need to load an image with individual dimensions// larger than that, and it still fits in the overall size limit, you can// #define STBI_MAX_DIMENSIONS on your own to be something larger.//// ===========================================================================//// UNICODE://// If compiling for Windows and you wish to use Unicode filenames, compile// with// #define STBI_WINDOWS_UTF8// and pass utf8-encoded filenames. Call stbi_convert_wchar_to_utf8 to convert// Windows wchar_t filenames to utf8.//// ===========================================================================//// Philosophy//// stb libraries are designed with the following priorities://// 1. easy to use// 2. easy to maintain// 3. good performance//// Sometimes I let "good performance" creep up in priority over "easy to maintain",// and for best performance I may provide less-easy-to-use APIs that give higher// performance, in addition to the easy-to-use ones. Nevertheless, it's important// to keep in mind that from the standpoint of you, a client of this library,// all you care about is #1 and #3, and stb libraries DO NOT emphasize #3 above all.//// Some secondary priorities arise directly from the first two, some of which// provide more explicit reasons why performance can't be emphasized.//// - Portable ("ease of use")// - Small source code footprint ("easy to maintain")// - No dependencies ("ease of use")//// ===========================================================================//// I/O callbacks//// I/O callbacks allow you to read from arbitrary sources, like packaged// files or some other source. Data read from callbacks are processed// through a small internal buffer (currently 128 bytes) to try to reduce// overhead.//// The three functions you must define are "read" (reads some bytes of data),// "skip" (skips some bytes of data), "eof" (reports if the stream is at the end).//// ===========================================================================//// SIMD support//// The JPEG decoder will try to automatically use SIMD kernels on x86 when// supported by the compiler. For ARM Neon support, you must explicitly// request it.//// (The old do-it-yourself SIMD API is no longer supported in the current// code.)//// On x86, SSE2 will automatically be used when available based on a run-time// test; if not, the generic C versions are used as a fall-back. On ARM targets,// the typical path is to have separate builds for NEON and non-NEON devices// (at least this is true for iOS and Android). Therefore, the NEON support is// toggled by a build flag: define STBI_NEON to get NEON loops.//// If for some reason you do not want to use any of SIMD code, or if// you have issues compiling it, you can disable it entirely by// defining STBI_NO_SIMD.//// ===========================================================================//// HDR image support (disable by defining STBI_NO_HDR)//// stb_image supports loading HDR images in general, and currently the Radiance// .HDR file format specifically. You can still load any file through the existing// interface; if you attempt to load an HDR file, it will be automatically remapped// to LDR, assuming gamma 2.2 and an arbitrary scale factor defaulting to 1;// both of these constants can be reconfigured through this interface://// stbi_hdr_to_ldr_gamma(2.2f);// stbi_hdr_to_ldr_scale(1.0f);//// (note, do not use _inverse_ constants; stbi_image will invert them// appropriately).//// Additionally, there is a new, parallel interface for loading files as// (linear) floats to preserve the full dynamic range://// float *data = stbi_loadf(filename, &x, &y, &n, 0);//// If you load LDR images through this interface, those images will// be promoted to floating point values, run through the inverse of// constants corresponding to the above://// stbi_ldr_to_hdr_scale(1.0f);// stbi_ldr_to_hdr_gamma(2.2f);//// Finally, given a filename (or an open file or memory block--see header// file for details) containing image data, you can query for the "most// appropriate" interface to use (that is, whether the image is HDR or// not), using://// stbi_is_hdr(char *filename);//// ===========================================================================//// iPhone PNG support://// We optionally support converting iPhone-formatted PNGs (which store// premultiplied BGRA) back to RGB, even though they're internally encoded// differently. To enable this conversion, call// stbi_convert_iphone_png_to_rgb(1).//// Call stbi_set_unpremultiply_on_load(1) as well to force a divide per// pixel to remove any premultiplied alpha *only* if the image file explicitly// says there's premultiplied data (currently only happens in iPhone images,// and only if iPhone convert-to-rgb processing is on).//// ===========================================================================//// ADDITIONAL CONFIGURATION//// - You can suppress implementation of any of the decoders to reduce// your code footprint by #defining one or more of the following// symbols before creating the implementation.//// STBI_NO_JPEG// STBI_NO_PNG// STBI_NO_BMP// STBI_NO_PSD// STBI_NO_TGA// STBI_NO_GIF// STBI_NO_HDR// STBI_NO_PIC// STBI_NO_PNM (.ppm and .pgm)//// - You can request *only* certain decoders and suppress all other ones// (this will be more forward-compatible, as addition of new decoders// doesn't require you to disable them explicitly)://// STBI_ONLY_JPEG// STBI_ONLY_PNG// STBI_ONLY_BMP// STBI_ONLY_PSD// STBI_ONLY_TGA// STBI_ONLY_GIF// STBI_ONLY_HDR// STBI_ONLY_PIC// STBI_ONLY_PNM (.ppm and .pgm)//// - If you use STBI_NO_PNG (or _ONLY_ without PNG), and you still// want the zlib decoder to be available, #define STBI_SUPPORT_ZLIB//// - If you define STBI_MAX_DIMENSIONS, stb_image will reject images greater// than that size (in either width or height) without further processing.// This is to let programs in the wild set an upper bound to prevent// denial-of-service attacks on untrusted data, as one could generate a// valid image of gigantic dimensions and force stb_image to allocate a// huge block of memory and spend disproportionate time decoding it. By// default this is set to (1 << 24), which is 16777216, but that's still// very big.#ifndef STBI_NO_STDIO#include <stdio.h>#endif // STBI_NO_STDIO#define STBI_VERSION 1enum{ STBI_default = 0, // only used for desired_channels STBI_grey = 1, STBI_grey_alpha = 2, STBI_rgb = 3, STBI_rgb_alpha = 4};#include <stdlib.h>typedef unsigned char stbi_uc;typedef unsigned short stbi_us;#ifdef __cplusplusextern "C" {#endif#ifndef STBIDEF#ifdef STB_IMAGE_STATIC#define STBIDEF static#else#define STBIDEF extern#endif#endif////////////////////////////////////////////////////////////////////////////////// PRIMARY API - works on images of any type////// load image by filename, open file, or memory buffer//typedef struct{ int (*read) (void *user,char *data,int size); // fill 'data' with 'size' bytes. return number of bytes actually read void (*skip) (void *user,int n); // skip the next 'n' bytes, or 'unget' the last -n bytes if negative int (*eof) (void *user); // returns nonzero if we are at end of file/data} stbi_io_callbacks;//////////////////////////////////////// 8-bits-per-channel interface//STBIDEF stbi_uc *stbi_load_from_memory (stbi_uc const *buffer, int len , int *x, int *y, int *channels_in_file, int desired_channels);STBIDEF stbi_uc *stbi_load_from_callbacks(stbi_io_callbacks const *clbk , void *user, int *x, int *y, int *channels_in_file, int desired_channels);#ifndef STBI_NO_STDIOSTBIDEF stbi_uc *stbi_load (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels);STBIDEF stbi_uc *stbi_load_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels);// for stbi_load_from_file, file pointer is left pointing immediately after image#endif#ifndef STBI_NO_GIFSTBIDEF stbi_uc *stbi_load_gif_from_memory(stbi_uc const *buffer, int len, int **delays, int *x, int *y, int *z, int *comp, int req_comp);#endif#ifdef STBI_WINDOWS_UTF8STBIDEF int stbi_convert_wchar_to_utf8(char *buffer, size_t bufferlen, const wchar_t* input);#endif//////////////////////////////////////// 16-bits-per-channel interface//STBIDEF stbi_us *stbi_load_16_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels);STBIDEF stbi_us *stbi_load_16_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels);#ifndef STBI_NO_STDIOSTBIDEF stbi_us *stbi_load_16 (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels);STBIDEF stbi_us *stbi_load_from_file_16(FILE *f, int *x, int *y, int *channels_in_file, int desired_channels);#endif//////////////////////////////////////// float-per-channel interface//#ifndef STBI_NO_LINEAR STBIDEF float *stbi_loadf_from_memory (stbi_uc const *buffer, int len, int *x, int *y, int *channels_in_file, int desired_channels); STBIDEF float *stbi_loadf_from_callbacks (stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *channels_in_file, int desired_channels); #ifndef STBI_NO_STDIO STBIDEF float *stbi_loadf (char const *filename, int *x, int *y, int *channels_in_file, int desired_channels); STBIDEF float *stbi_loadf_from_file (FILE *f, int *x, int *y, int *channels_in_file, int desired_channels); #endif#endif#ifndef STBI_NO_HDR STBIDEF void stbi_hdr_to_ldr_gamma(float gamma); STBIDEF void stbi_hdr_to_ldr_scale(float scale);#endif // STBI_NO_HDR#ifndef STBI_NO_LINEAR STBIDEF void stbi_ldr_to_hdr_gamma(float gamma); STBIDEF void stbi_ldr_to_hdr_scale(float scale);#endif // STBI_NO_LINEAR// stbi_is_hdr is always defined, but always returns false if STBI_NO_HDRSTBIDEF int stbi_is_hdr_from_callbacks(stbi_io_callbacks const *clbk, void *user);STBIDEF int stbi_is_hdr_from_memory(stbi_uc const *buffer, int len);#ifndef STBI_NO_STDIOSTBIDEF int stbi_is_hdr (char const *filename);STBIDEF int stbi_is_hdr_from_file(FILE *f);#endif // STBI_NO_STDIO// get a VERY brief reason for failure// on most compilers (and ALL modern mainstream compilers) this is threadsafeSTBIDEF const char *stbi_failure_reason (void);// free the loaded image -- this is just free()STBIDEF void stbi_image_free (void *retval_from_stbi_load);// get image dimensions & components without fully decodingSTBIDEF int stbi_info_from_memory(stbi_uc const *buffer, int len, int *x, int *y, int *comp);STBIDEF int stbi_info_from_callbacks(stbi_io_callbacks const *clbk, void *user, int *x, int *y, int *comp);STBIDEF int stbi_is_16_bit_from_memory(stbi_uc const *buffer, int len);STBIDEF int stbi_is_16_bit_from_callbacks(stbi_io_callbacks const *clbk, void *user);#ifndef STBI_NO_STDIOShowing the first 500 of 7989 diff lines for this file. This diff is INCOMPLETE; read the file or clone the repository for the rest.
vendor/stb/stb_truetype.hdeleted
// stb_truetype.h - v1.26 - public domain// authored from 2009-2021 by Sean Barrett / RAD Game Tools//// =======================================================================//// NO SECURITY GUARANTEE -- DO NOT USE THIS ON UNTRUSTED FONT FILES//// This library does no range checking of the offsets found in the file,// meaning an attacker can use it to read arbitrary memory.//// =======================================================================//// This library processes TrueType files:// parse files// extract glyph metrics// extract glyph shapes// render glyphs to one-channel bitmaps with antialiasing (box filter)// render glyphs to one-channel SDF bitmaps (signed-distance field/function)//// Todo:// non-MS cmaps// crashproof on bad data// hinting? (no longer patented)// cleartype-style AA?// optimize: use simple memory allocator for intermediates// optimize: build edge-list directly from curves// optimize: rasterize directly from curves?//// ADDITIONAL CONTRIBUTORS//// Mikko Mononen: compound shape support, more cmap formats// Tor Andersson: kerning, subpixel rendering// Dougall Johnson: OpenType / Type 2 font handling// Daniel Ribeiro Maciel: basic GPOS-based kerning//// Misc other:// Ryan Gordon// Simon Glass// github:IntellectualKitty// Imanol Celaya// Daniel Ribeiro Maciel//// Bug/warning reports/fixes:// "Zer" on mollyrocket Fabian "ryg" Giesen github:NiLuJe// Cass Everitt Martins Mozeiko github:aloucks// stoiko (Haemimont Games) Cap Petschulat github:oyvindjam// Brian Hook Omar Cornut github:vassvik// Walter van Niftrik Ryan Griege// David Gow Peter LaValle// David Given Sergey Popov// Ivan-Assen Ivanov Giumo X. Clanjor// Anthony Pesch Higor Euripedes// Johan Duparc Thomas Fields// Hou Qiming Derek Vinyard// Rob Loach Cort Stratton// Kenney Phillis Jr. Brian Costabile// Ken Voskuil (kaesve) Yakov Galka//// VERSION HISTORY//// 1.26 (2021-08-28) fix broken rasterizer// 1.25 (2021-07-11) many fixes// 1.24 (2020-02-05) fix warning// 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS)// 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined// 1.21 (2019-02-25) fix warning// 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics()// 1.19 (2018-02-11) GPOS kerning, STBTT_fmod// 1.18 (2018-01-29) add missing function// 1.17 (2017-07-23) make more arguments const; doc fix// 1.16 (2017-07-12) SDF support// 1.15 (2017-03-03) make more arguments const// 1.14 (2017-01-16) num-fonts-in-TTC function// 1.13 (2017-01-02) support OpenType fonts, certain Apple fonts// 1.12 (2016-10-25) suppress warnings about casting away const with -Wcast-qual// 1.11 (2016-04-02) fix unused-variable warning// 1.10 (2016-04-02) user-defined fabs(); rare memory leak; remove duplicate typedef// 1.09 (2016-01-16) warning fix; avoid crash on outofmem; use allocation userdata properly// 1.08 (2015-09-13) document stbtt_Rasterize(); fixes for vertical & horizontal edges// 1.07 (2015-08-01) allow PackFontRanges to accept arrays of sparse codepoints;// variant PackFontRanges to pack and render in separate phases;// fix stbtt_GetFontOFfsetForIndex (never worked for non-0 input?);// fixed an assert() bug in the new rasterizer// replace assert() with STBTT_assert() in new rasterizer//// Full history can be found at the end of this file.//// LICENSE//// See end of file for license information.//// USAGE//// Include this file in whatever places need to refer to it. In ONE C/C++// file, write:// #define STB_TRUETYPE_IMPLEMENTATION// before the #include of this file. This expands out the actual// implementation into that C/C++ file.//// To make the implementation private to the file that generates the implementation,// #define STBTT_STATIC//// Simple 3D API (don't ship this, but it's fine for tools and quick start)// stbtt_BakeFontBitmap() -- bake a font to a bitmap for use as texture// stbtt_GetBakedQuad() -- compute quad to draw for a given char//// Improved 3D API (more shippable):// #include "stb_rect_pack.h" -- optional, but you really want it// stbtt_PackBegin()// stbtt_PackSetOversampling() -- for improved quality on small fonts// stbtt_PackFontRanges() -- pack and renders// stbtt_PackEnd()// stbtt_GetPackedQuad()//// "Load" a font file from a memory buffer (you have to keep the buffer loaded)// stbtt_InitFont()// stbtt_GetFontOffsetForIndex() -- indexing for TTC font collections// stbtt_GetNumberOfFonts() -- number of fonts for TTC font collections//// Render a unicode codepoint to a bitmap// stbtt_GetCodepointBitmap() -- allocates and returns a bitmap// stbtt_MakeCodepointBitmap() -- renders into bitmap you provide// stbtt_GetCodepointBitmapBox() -- how big the bitmap must be//// Character advance/positioning// stbtt_GetCodepointHMetrics()// stbtt_GetFontVMetrics()// stbtt_GetFontVMetricsOS2()// stbtt_GetCodepointKernAdvance()//// Starting with version 1.06, the rasterizer was replaced with a new,// faster and generally-more-precise rasterizer. The new rasterizer more// accurately measures pixel coverage for anti-aliasing, except in the case// where multiple shapes overlap, in which case it overestimates the AA pixel// coverage. Thus, anti-aliasing of intersecting shapes may look wrong. If// this turns out to be a problem, you can re-enable the old rasterizer with// #define STBTT_RASTERIZER_VERSION 1// which will incur about a 15% speed hit.//// ADDITIONAL DOCUMENTATION//// Immediately after this block comment are a series of sample programs.//// After the sample programs is the "header file" section. This section// includes documentation for each API function.//// Some important concepts to understand to use this library://// Codepoint// Characters are defined by unicode codepoints, e.g. 65 is// uppercase A, 231 is lowercase c with a cedilla, 0x7e30 is// the hiragana for "ma".//// Glyph// A visual character shape (every codepoint is rendered as// some glyph)//// Glyph index// A font-specific integer ID representing a glyph//// Baseline// Glyph shapes are defined relative to a baseline, which is the// bottom of uppercase characters. Characters extend both above// and below the baseline.//// Current Point// As you draw text to the screen, you keep track of a "current point"// which is the origin of each character. The current point's vertical// position is the baseline. Even "baked fonts" use this model.//// Vertical Font Metrics// The vertical qualities of the font, used to vertically position// and space the characters. See docs for stbtt_GetFontVMetrics.//// Font Size in Pixels or Points// The preferred interface for specifying font sizes in stb_truetype// is to specify how tall the font's vertical extent should be in pixels.// If that sounds good enough, skip the next paragraph.//// Most font APIs instead use "points", which are a common typographic// measurement for describing font size, defined as 72 points per inch.// stb_truetype provides a point API for compatibility. However, true// "per inch" conventions don't make much sense on computer displays// since different monitors have different number of pixels per// inch. For example, Windows traditionally uses a convention that// there are 96 pixels per inch, thus making 'inch' measurements have// nothing to do with inches, and thus effectively defining a point to// be 1.333 pixels. Additionally, the TrueType font data provides// an explicit scale factor to scale a given font's glyphs to points,// but the author has observed that this scale factor is often wrong// for non-commercial fonts, thus making fonts scaled in points// according to the TrueType spec incoherently sized in practice.//// DETAILED USAGE://// Scale:// Select how high you want the font to be, in points or pixels.// Call ScaleForPixelHeight or ScaleForMappingEmToPixels to compute// a scale factor SF that will be used by all other functions.//// Baseline:// You need to select a y-coordinate that is the baseline of where// your text will appear. Call GetFontBoundingBox to get the baseline-relative// bounding box for all characters. SF*-y0 will be the distance in pixels// that the worst-case character could extend above the baseline, so if// you want the top edge of characters to appear at the top of the// screen where y=0, then you would set the baseline to SF*-y0.//// Current point:// Set the current point where the first character will appear. The// first character could extend left of the current point; this is font// dependent. You can either choose a current point that is the leftmost// point and hope, or add some padding, or check the bounding box or// left-side-bearing of the first character to be displayed and set// the current point based on that.//// Displaying a character:// Compute the bounding box of the character. It will contain signed values// relative to <current_point, baseline>. I.e. if it returns x0,y0,x1,y1,// then the character should be displayed in the rectangle from// <current_point+SF*x0, baseline+SF*y0> to <current_point+SF*x1,baseline+SF*y1).//// Advancing for the next character:// Call GlyphHMetrics, and compute 'current_point += SF * advance'.////// ADVANCED USAGE//// Quality://// - Use the functions with Subpixel at the end to allow your characters// to have subpixel positioning. Since the font is anti-aliased, not// hinted, this is very import for quality. (This is not possible with// baked fonts.)//// - Kerning is now supported, and if you're supporting subpixel rendering// then kerning is worth using to give your text a polished look.//// Performance://// - Convert Unicode codepoints to glyph indexes and operate on the glyphs;// if you don't do this, stb_truetype is forced to do the conversion on// every call.//// - There are a lot of memory allocations. We should modify it to take// a temp buffer and allocate from the temp buffer (without freeing),// should help performance a lot.//// NOTES//// The system uses the raw data found in the .ttf file without changing it// and without building auxiliary data structures. This is a bit inefficient// on little-endian systems (the data is big-endian), but assuming you're// caching the bitmaps or glyph shapes this shouldn't be a big deal.//// It appears to be very hard to programmatically determine what font a// given file is in a general way. I provide an API for this, but I don't// recommend it.////// PERFORMANCE MEASUREMENTS FOR 1.06://// 32-bit 64-bit// Previous release: 8.83 s 7.68 s// Pool allocations: 7.72 s 6.34 s// Inline sort : 6.54 s 5.65 s// New rasterizer : 5.63 s 5.00 s//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// SAMPLE PROGRAMS//////// Incomplete text-in-3d-api example, which draws quads properly aligned to be lossless.// See "tests/truetype_demo_win32.c" for a complete version.#if 0#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation#include "stb_truetype.h"unsigned char ttf_buffer[1<<20];unsigned char temp_bitmap[512*512];stbtt_bakedchar cdata[96]; // ASCII 32..126 is 95 glyphsGLuint ftex;void my_stbtt_initfont(void){ fread(ttf_buffer, 1, 1<<20, fopen("c:/windows/fonts/times.ttf", "rb")); stbtt_BakeFontBitmap(ttf_buffer,0, 32.0, temp_bitmap,512,512, 32,96, cdata); // no guarantee this fits! // can free ttf_buffer at this point glGenTextures(1, &ftex); glBindTexture(GL_TEXTURE_2D, ftex); glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512,512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap); // can free temp_bitmap at this point glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);}void my_stbtt_print(float x, float y, char *text){ // assume orthographic projection with units = screen pixels, origin at top left glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, ftex); glBegin(GL_QUADS); while (*text) { if (*text >= 32 && *text < 128) { stbtt_aligned_quad q; stbtt_GetBakedQuad(cdata, 512,512, *text-32, &x,&y,&q,1);//1=opengl & d3d10+,0=d3d9 glTexCoord2f(q.s0,q.t0); glVertex2f(q.x0,q.y0); glTexCoord2f(q.s1,q.t0); glVertex2f(q.x1,q.y0); glTexCoord2f(q.s1,q.t1); glVertex2f(q.x1,q.y1); glTexCoord2f(q.s0,q.t1); glVertex2f(q.x0,q.y1); } ++text; } glEnd();}#endif////////////////////////////////////////////////////////////////////////////////////// Complete program (this compiles): get a single bitmap, print as ASCII art//#if 0#include <stdio.h>#define STB_TRUETYPE_IMPLEMENTATION // force following include to generate implementation#include "stb_truetype.h"char ttf_buffer[1<<25];int main(int argc, char **argv){ stbtt_fontinfo font; unsigned char *bitmap; int w,h,i,j,c = (argc > 1 ? atoi(argv[1]) : 'a'), s = (argc > 2 ? atoi(argv[2]) : 20); fread(ttf_buffer, 1, 1<<25, fopen(argc > 3 ? argv[3] : "c:/windows/fonts/arialbd.ttf", "rb")); stbtt_InitFont(&font, ttf_buffer, stbtt_GetFontOffsetForIndex(ttf_buffer,0)); bitmap = stbtt_GetCodepointBitmap(&font, 0,stbtt_ScaleForPixelHeight(&font, s), c, &w, &h, 0,0); for (j=0; j < h; ++j) { for (i=0; i < w; ++i) putchar(" .:ioVM@"[bitmap[j*w+i]>>5]); putchar('\n'); } return 0;}#endif//// Output://// .ii.// @@@@@@.// V@Mio@@o// :i. V@V// :oM@@M// :@@@MM@M// @@o o@M// :@@. M@M// @@@o@@@@// :M@@V:@@.//////////////////////////////////////////////////////////////////////////////////// Complete program: print "Hello World!" banner, with bugs//#if 0char buffer[24<<20];unsigned char screen[20][79];int main(int arg, char **argv){ stbtt_fontinfo font; int i,j,ascent,baseline,ch=0; float scale, xpos=2; // leave a little padding in case the character extends left char *text = "Heljo World!"; // intentionally misspelled to show 'lj' brokenness fread(buffer, 1, 1000000, fopen("c:/windows/fonts/arialbd.ttf", "rb")); stbtt_InitFont(&font, buffer, 0); scale = stbtt_ScaleForPixelHeight(&font, 15); stbtt_GetFontVMetrics(&font, &ascent,0,0); baseline = (int) (ascent*scale); while (text[ch]) { int advance,lsb,x0,y0,x1,y1; float x_shift = xpos - (float) floor(xpos); stbtt_GetCodepointHMetrics(&font, text[ch], &advance, &lsb); stbtt_GetCodepointBitmapBoxSubpixel(&font, text[ch], scale,scale,x_shift,0, &x0,&y0,&x1,&y1); stbtt_MakeCodepointBitmapSubpixel(&font, &screen[baseline + y0][(int) xpos + x0], x1-x0,y1-y0, 79, scale,scale,x_shift,0, text[ch]); // note that this stomps the old data, so where character boxes overlap (e.g. 'lj') it's wrong // because this API is really for baking character bitmaps into textures. if you want to render // a sequence of characters, you really need to render each bitmap to a temp buffer, then // "alpha blend" that into the working buffer xpos += (advance * scale); if (text[ch+1]) xpos += scale*stbtt_GetCodepointKernAdvance(&font, text[ch],text[ch+1]); ++ch; } for (j=0; j < 20; ++j) { for (i=0; i < 78; ++i) putchar(" .:ioVM@"[screen[j][i]>>5]); putchar('\n'); } return 0;}#endif//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// INTEGRATION WITH YOUR CODEBASE//////// The following sections allow you to supply alternate definitions//// of C library functions used by stb_truetype, e.g. if you don't//// link with the C runtime library.#ifdef STB_TRUETYPE_IMPLEMENTATION // #define your own (u)stbtt_int8/16/32 before including to override this #ifndef stbtt_uint8 typedef unsigned char stbtt_uint8; typedef signed char stbtt_int8; typedef unsigned short stbtt_uint16; typedef signed short stbtt_int16; typedef unsigned int stbtt_uint32; typedef signed int stbtt_int32; #endif typedef char stbtt__check_size32[sizeof(stbtt_int32)==4 ? 1 : -1]; typedef char stbtt__check_size16[sizeof(stbtt_int16)==2 ? 1 : -1]; // e.g. #define your own STBTT_ifloor/STBTT_iceil() to avoid math.h #ifndef STBTT_ifloor #include <math.h> #define STBTT_ifloor(x) ((int) floor(x)) #define STBTT_iceil(x) ((int) ceil(x)) #endif #ifndef STBTT_sqrt #include <math.h> #define STBTT_sqrt(x) sqrt(x) #define STBTT_pow(x,y) pow(x,y) #endif #ifndef STBTT_fmod #include <math.h> #define STBTT_fmod(x,y) fmod(x,y) #endif #ifndef STBTT_cos #include <math.h> #define STBTT_cos(x) cos(x) #define STBTT_acos(x) acos(x) #endif #ifndef STBTT_fabs #include <math.h> #define STBTT_fabs(x) fabs(x) #endif // #define your own functions "STBTT_malloc" / "STBTT_free" to avoid malloc.h #ifndef STBTT_malloc #include <stdlib.h> #define STBTT_malloc(x,u) ((void)(u),malloc(x)) #define STBTT_free(x,u) ((void)(u),free(x)) #endif #ifndef STBTT_assert #include <assert.h> #define STBTT_assert(x) assert(x) #endif #ifndef STBTT_strlen #include <string.h> #define STBTT_strlen(x) strlen(x) #endif #ifndef STBTT_memcpy #include <string.h> #define STBTT_memcpy memcpy #define STBTT_memset memset #endif#endif////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// INTERFACE////////#ifndef __STB_INCLUDE_STB_TRUETYPE_H__Showing the first 500 of 5080 diff lines for this file. This diff is INCOMPLETE; read the file or clone the repository for the rest.
vendor/stb/stb_vorbis.cdeleted
// Ogg Vorbis audio decoder - v1.22 - public domain// http://nothings.org/stb_vorbis///// Original version written by Sean Barrett in 2007.//// Originally sponsored by RAD Game Tools. Seeking implementation// sponsored by Phillip Bennefall, Marc Andersen, Aaron Baker,// Elias Software, Aras Pranckevicius, and Sean Barrett.//// LICENSE//// See end of file for license information.//// Limitations://// - floor 0 not supported (used in old ogg vorbis files pre-2004)// - lossless sample-truncation at beginning ignored// - cannot concatenate multiple vorbis streams// - sample positions are 32-bit, limiting seekable 192Khz// files to around 6 hours (Ogg supports 64-bit)//// Feature contributors:// Dougall Johnson (sample-exact seeking)//// Bugfix/warning contributors:// Terje Mathisen Niklas Frykholm Andy Hill// Casey Muratori John Bolton Gargaj// Laurent Gomila Marc LeBlanc Ronny Chevalier// Bernhard Wodo Evan Balster github:alxprd// Tom Beaumont Ingo Leitgeb Nicolas Guillemot// Phillip Bennefall Rohit Thiago Goulart// github:manxorist Saga Musix github:infatum// Timur Gagiev Maxwell Koo Peter Waller// github:audinowho Dougall Johnson David Reid// github:Clownacy Pedro J. Estebanez Remi Verschelde// AnthoFoxo github:morlat Gabriel Ravier//// Partial history:// 1.22 - 2021-07-11 - various small fixes// 1.21 - 2021-07-02 - fix bug for files with no comments// 1.20 - 2020-07-11 - several small fixes// 1.19 - 2020-02-05 - warnings// 1.18 - 2020-02-02 - fix seek bugs; parse header comments; misc warnings etc.// 1.17 - 2019-07-08 - fix CVE-2019-13217..CVE-2019-13223 (by ForAllSecure)// 1.16 - 2019-03-04 - fix warnings// 1.15 - 2019-02-07 - explicit failure if Ogg Skeleton data is found// 1.14 - 2018-02-11 - delete bogus dealloca usage// 1.13 - 2018-01-29 - fix truncation of last frame (hopefully)// 1.12 - 2017-11-21 - limit residue begin/end to blocksize/2 to avoid large temp allocs in bad/corrupt files// 1.11 - 2017-07-23 - fix MinGW compilation// 1.10 - 2017-03-03 - more robust seeking; fix negative ilog(); clear error in open_memory// 1.09 - 2016-04-04 - back out 'truncation of last frame' fix from previous version// 1.08 - 2016-04-02 - warnings; setup memory leaks; truncation of last frame// 1.07 - 2015-01-16 - fixes for crashes on invalid files; warning fixes; const// 1.06 - 2015-08-31 - full, correct support for seeking API (Dougall Johnson)// some crash fixes when out of memory or with corrupt files// fix some inappropriately signed shifts// 1.05 - 2015-04-19 - don't define __forceinline if it's redundant// 1.04 - 2014-08-27 - fix missing const-correct case in API// 1.03 - 2014-08-07 - warning fixes// 1.02 - 2014-07-09 - declare qsort comparison as explicitly _cdecl in Windows// 1.01 - 2014-06-18 - fix stb_vorbis_get_samples_float (interleaved was correct)// 1.0 - 2014-05-26 - fix memory leaks; fix warnings; fix bugs in >2-channel;// (API change) report sample rate for decode-full-file funcs//// See end of file for full version history.////////////////////////////////////////////////////////////////////////////////// HEADER BEGINS HERE//#ifndef STB_VORBIS_INCLUDE_STB_VORBIS_H#define STB_VORBIS_INCLUDE_STB_VORBIS_H#if defined(STB_VORBIS_NO_CRT) && !defined(STB_VORBIS_NO_STDIO)#define STB_VORBIS_NO_STDIO 1#endif#ifndef STB_VORBIS_NO_STDIO#include <stdio.h>#endif#ifdef __cplusplusextern "C" {#endif/////////// THREAD SAFETY// Individual stb_vorbis* handles are not thread-safe; you cannot decode from// them from multiple threads at the same time. However, you can have multiple// stb_vorbis* handles and decode from them independently in multiple thrads./////////// MEMORY ALLOCATION// normally stb_vorbis uses malloc() to allocate memory at startup,// and alloca() to allocate temporary memory during a frame on the// stack. (Memory consumption will depend on the amount of setup// data in the file and how you set the compile flags for speed// vs. size. In my test files the maximal-size usage is ~150KB.)//// You can modify the wrapper functions in the source (setup_malloc,// setup_temp_malloc, temp_malloc) to change this behavior, or you// can use a simpler allocation model: you pass in a buffer from// which stb_vorbis will allocate _all_ its memory (including the// temp memory). "open" may fail with a VORBIS_outofmem if you// do not pass in enough data; there is no way to determine how// much you do need except to succeed (at which point you can// query get_info to find the exact amount required. yes I know// this is lame).//// If you pass in a non-NULL buffer of the type below, allocation// will occur from it as described above. Otherwise just pass NULL// to use malloc()/alloca()typedef struct{ char *alloc_buffer; int alloc_buffer_length_in_bytes;} stb_vorbis_alloc;/////////// FUNCTIONS USEABLE WITH ALL INPUT MODEStypedef struct stb_vorbis stb_vorbis;typedef struct{ unsigned int sample_rate; int channels; unsigned int setup_memory_required; unsigned int setup_temp_memory_required; unsigned int temp_memory_required; int max_frame_size;} stb_vorbis_info;typedef struct{ char *vendor; int comment_list_length; char **comment_list;} stb_vorbis_comment;// get general information about the fileextern stb_vorbis_info stb_vorbis_get_info(stb_vorbis *f);// get ogg commentsextern stb_vorbis_comment stb_vorbis_get_comment(stb_vorbis *f);// get the last error detected (clears it, too)extern int stb_vorbis_get_error(stb_vorbis *f);// close an ogg vorbis file and free all memory in useextern void stb_vorbis_close(stb_vorbis *f);// this function returns the offset (in samples) from the beginning of the// file that will be returned by the next decode, if it is known, or -1// otherwise. after a flush_pushdata() call, this may take a while before// it becomes valid again.// NOT WORKING YET after a seek with PULLDATA APIextern int stb_vorbis_get_sample_offset(stb_vorbis *f);// returns the current seek point within the file, or offset from the beginning// of the memory buffer. In pushdata mode it returns 0.extern unsigned int stb_vorbis_get_file_offset(stb_vorbis *f);/////////// PUSHDATA API#ifndef STB_VORBIS_NO_PUSHDATA_API// this API allows you to get blocks of data from any source and hand// them to stb_vorbis. you have to buffer them; stb_vorbis will tell// you how much it used, and you have to give it the rest next time;// and stb_vorbis may not have enough data to work with and you will// need to give it the same data again PLUS more. Note that the Vorbis// specification does not bound the size of an individual frame.extern stb_vorbis *stb_vorbis_open_pushdata( const unsigned char * datablock, int datablock_length_in_bytes, int *datablock_memory_consumed_in_bytes, int *error, const stb_vorbis_alloc *alloc_buffer);// create a vorbis decoder by passing in the initial data block containing// the ogg&vorbis headers (you don't need to do parse them, just provide// the first N bytes of the file--you're told if it's not enough, see below)// on success, returns an stb_vorbis *, does not set error, returns the amount of// data parsed/consumed on this call in *datablock_memory_consumed_in_bytes;// on failure, returns NULL on error and sets *error, does not change *datablock_memory_consumed// if returns NULL and *error is VORBIS_need_more_data, then the input block was// incomplete and you need to pass in a larger block from the start of the fileextern int stb_vorbis_decode_frame_pushdata( stb_vorbis *f, const unsigned char *datablock, int datablock_length_in_bytes, int *channels, // place to write number of float * buffers float ***output, // place to write float ** array of float * buffers int *samples // place to write number of output samples );// decode a frame of audio sample data if possible from the passed-in data block//// return value: number of bytes we used from datablock//// possible cases:// 0 bytes used, 0 samples output (need more data)// N bytes used, 0 samples output (resynching the stream, keep going)// N bytes used, M samples output (one frame of data)// note that after opening a file, you will ALWAYS get one N-bytes,0-sample// frame, because Vorbis always "discards" the first frame.//// Note that on resynch, stb_vorbis will rarely consume all of the buffer,// instead only datablock_length_in_bytes-3 or less. This is because it wants// to avoid missing parts of a page header if they cross a datablock boundary,// without writing state-machiney code to record a partial detection.//// The number of channels returned are stored in *channels (which can be// NULL--it is always the same as the number of channels reported by// get_info). *output will contain an array of float* buffers, one per// channel. In other words, (*output)[0][0] contains the first sample from// the first channel, and (*output)[1][0] contains the first sample from// the second channel.//// *output points into stb_vorbis's internal output buffer storage; these// buffers are owned by stb_vorbis and application code should not free// them or modify their contents. They are transient and will be overwritten// once you ask for more data to get decoded, so be sure to grab any data// you need before then.extern void stb_vorbis_flush_pushdata(stb_vorbis *f);// inform stb_vorbis that your next datablock will not be contiguous with// previous ones (e.g. you've seeked in the data); future attempts to decode// frames will cause stb_vorbis to resynchronize (as noted above), and// once it sees a valid Ogg page (typically 4-8KB, as large as 64KB), it// will begin decoding the _next_ frame.//// if you want to seek using pushdata, you need to seek in your file, then// call stb_vorbis_flush_pushdata(), then start calling decoding, then once// decoding is returning you data, call stb_vorbis_get_sample_offset, and// if you don't like the result, seek your file again and repeat.#endif////////// PULLING INPUT API#ifndef STB_VORBIS_NO_PULLDATA_API// This API assumes stb_vorbis is allowed to pull data from a source--// either a block of memory containing the _entire_ vorbis stream, or a// FILE * that you or it create, or possibly some other reading mechanism// if you go modify the source to replace the FILE * case with some kind// of callback to your code. (But if you don't support seeking, you may// just want to go ahead and use pushdata.)#if !defined(STB_VORBIS_NO_STDIO) && !defined(STB_VORBIS_NO_INTEGER_CONVERSION)extern int stb_vorbis_decode_filename(const char *filename, int *channels, int *sample_rate, short **output);#endif#if !defined(STB_VORBIS_NO_INTEGER_CONVERSION)extern int stb_vorbis_decode_memory(const unsigned char *mem, int len, int *channels, int *sample_rate, short **output);#endif// decode an entire file and output the data interleaved into a malloc()ed// buffer stored in *output. The return value is the number of samples// decoded, or -1 if the file could not be opened or was not an ogg vorbis file.// When you're done with it, just free() the pointer returned in *output.extern stb_vorbis * stb_vorbis_open_memory(const unsigned char *data, int len, int *error, const stb_vorbis_alloc *alloc_buffer);// create an ogg vorbis decoder from an ogg vorbis stream in memory (note// this must be the entire stream!). on failure, returns NULL and sets *error#ifndef STB_VORBIS_NO_STDIOextern stb_vorbis * stb_vorbis_open_filename(const char *filename, int *error, const stb_vorbis_alloc *alloc_buffer);// create an ogg vorbis decoder from a filename via fopen(). on failure,// returns NULL and sets *error (possibly to VORBIS_file_open_failure).extern stb_vorbis * stb_vorbis_open_file(FILE *f, int close_handle_on_close, int *error, const stb_vorbis_alloc *alloc_buffer);// create an ogg vorbis decoder from an open FILE *, looking for a stream at// the _current_ seek point (ftell). on failure, returns NULL and sets *error.// note that stb_vorbis must "own" this stream; if you seek it in between// calls to stb_vorbis, it will become confused. Moreover, if you attempt to// perform stb_vorbis_seek_*() operations on this file, it will assume it// owns the _entire_ rest of the file after the start point. Use the next// function, stb_vorbis_open_file_section(), to limit it.extern stb_vorbis * stb_vorbis_open_file_section(FILE *f, int close_handle_on_close, int *error, const stb_vorbis_alloc *alloc_buffer, unsigned int len);// create an ogg vorbis decoder from an open FILE *, looking for a stream at// the _current_ seek point (ftell); the stream will be of length 'len' bytes.// on failure, returns NULL and sets *error. note that stb_vorbis must "own"// this stream; if you seek it in between calls to stb_vorbis, it will become// confused.#endifextern int stb_vorbis_seek_frame(stb_vorbis *f, unsigned int sample_number);extern int stb_vorbis_seek(stb_vorbis *f, unsigned int sample_number);// these functions seek in the Vorbis file to (approximately) 'sample_number'.// after calling seek_frame(), the next call to get_frame_*() will include// the specified sample. after calling stb_vorbis_seek(), the next call to// stb_vorbis_get_samples_* will start with the specified sample. If you// do not need to seek to EXACTLY the target sample when using get_samples_*,// you can also use seek_frame().extern int stb_vorbis_seek_start(stb_vorbis *f);// this function is equivalent to stb_vorbis_seek(f,0)extern unsigned int stb_vorbis_stream_length_in_samples(stb_vorbis *f);extern float stb_vorbis_stream_length_in_seconds(stb_vorbis *f);// these functions return the total length of the vorbis streamextern int stb_vorbis_get_frame_float(stb_vorbis *f, int *channels, float ***output);// decode the next frame and return the number of samples. the number of// channels returned are stored in *channels (which can be NULL--it is always// the same as the number of channels reported by get_info). *output will// contain an array of float* buffers, one per channel. These outputs will// be overwritten on the next call to stb_vorbis_get_frame_*.//// You generally should not intermix calls to stb_vorbis_get_frame_*()// and stb_vorbis_get_samples_*(), since the latter calls the former.#ifndef STB_VORBIS_NO_INTEGER_CONVERSIONextern int stb_vorbis_get_frame_short_interleaved(stb_vorbis *f, int num_c, short *buffer, int num_shorts);extern int stb_vorbis_get_frame_short (stb_vorbis *f, int num_c, short **buffer, int num_samples);#endif// decode the next frame and return the number of *samples* per channel.// Note that for interleaved data, you pass in the number of shorts (the// size of your array), but the return value is the number of samples per// channel, not the total number of samples.//// The data is coerced to the number of channels you request according to the// channel coercion rules (see below). You must pass in the size of your// buffer(s) so that stb_vorbis will not overwrite the end of the buffer.// The maximum buffer size needed can be gotten from get_info(); however,// the Vorbis I specification implies an absolute maximum of 4096 samples// per channel.// Channel coercion rules:// Let M be the number of channels requested, and N the number of channels present,// and Cn be the nth channel; let stereo L be the sum of all L and center channels,// and stereo R be the sum of all R and center channels (channel assignment from the// vorbis spec).// M N output// 1 k sum(Ck) for all k// 2 * stereo L, stereo R// k l k > l, the first l channels, then 0s// k l k <= l, the first k channels// Note that this is not _good_ surround etc. mixing at all! It's just so// you get something useful.extern int stb_vorbis_get_samples_float_interleaved(stb_vorbis *f, int channels, float *buffer, int num_floats);extern int stb_vorbis_get_samples_float(stb_vorbis *f, int channels, float **buffer, int num_samples);// gets num_samples samples, not necessarily on a frame boundary--this requires// buffering so you have to supply the buffers. DOES NOT APPLY THE COERCION RULES.// Returns the number of samples stored per channel; it may be less than requested// at the end of the file. If there are no more samples in the file, returns 0.#ifndef STB_VORBIS_NO_INTEGER_CONVERSIONextern int stb_vorbis_get_samples_short_interleaved(stb_vorbis *f, int channels, short *buffer, int num_shorts);extern int stb_vorbis_get_samples_short(stb_vorbis *f, int channels, short **buffer, int num_samples);#endif// gets num_samples samples, not necessarily on a frame boundary--this requires// buffering so you have to supply the buffers. Applies the coercion rules above// to produce 'channels' channels. Returns the number of samples stored per channel;// it may be less than requested at the end of the file. If there are no more// samples in the file, returns 0.#endif//////// ERROR CODESenum STBVorbisError{ VORBIS__no_error, VORBIS_need_more_data=1, // not a real error VORBIS_invalid_api_mixing, // can't mix API modes VORBIS_outofmem, // not enough memory VORBIS_feature_not_supported, // uses floor 0 VORBIS_too_many_channels, // STB_VORBIS_MAX_CHANNELS is too small VORBIS_file_open_failure, // fopen() failed VORBIS_seek_without_length, // can't seek in unknown-length file VORBIS_unexpected_eof=10, // file is truncated? VORBIS_seek_invalid, // seek past EOF // decoding errors (corrupt/invalid stream) -- you probably // don't care about the exact details of these // vorbis errors: VORBIS_invalid_setup=20, VORBIS_invalid_stream, // ogg errors: VORBIS_missing_capture_pattern=30, VORBIS_invalid_stream_structure_version, VORBIS_continued_packet_flag_invalid, VORBIS_incorrect_stream_serial_number, VORBIS_invalid_first_page, VORBIS_bad_packet_type, VORBIS_cant_find_last_page, VORBIS_seek_failed, VORBIS_ogg_skeleton_not_supported};#ifdef __cplusplus}#endif#endif // STB_VORBIS_INCLUDE_STB_VORBIS_H//// HEADER ENDS HERE////////////////////////////////////////////////////////////////////////////////#ifndef STB_VORBIS_HEADER_ONLY// global configuration settings (e.g. set these in the project/makefile),// or just set them in this file at the top (although ideally the first few// should be visible when the header file is compiled too, although it's not// crucial)// STB_VORBIS_NO_PUSHDATA_API// does not compile the code for the various stb_vorbis_*_pushdata()// functions// #define STB_VORBIS_NO_PUSHDATA_API// STB_VORBIS_NO_PULLDATA_API// does not compile the code for the non-pushdata APIs// #define STB_VORBIS_NO_PULLDATA_API// STB_VORBIS_NO_STDIO// does not compile the code for the APIs that use FILE *s internally// or externally (implied by STB_VORBIS_NO_PULLDATA_API)// #define STB_VORBIS_NO_STDIO// STB_VORBIS_NO_INTEGER_CONVERSION// does not compile the code for converting audio sample data from// float to integer (implied by STB_VORBIS_NO_PULLDATA_API)// #define STB_VORBIS_NO_INTEGER_CONVERSION// STB_VORBIS_NO_FAST_SCALED_FLOAT// does not use a fast float-to-int trick to accelerate float-to-int on// most platforms which requires endianness be defined correctly.//#define STB_VORBIS_NO_FAST_SCALED_FLOAT// STB_VORBIS_MAX_CHANNELS [number]// globally define this to the maximum number of channels you need.// The spec does not put a restriction on channels except that// the count is stored in a byte, so 255 is the hard limit.// Reducing this saves about 16 bytes per value, so using 16 saves// (255-16)*16 or around 4KB. Plus anything other memory usage// I forgot to account for. Can probably go as low as 8 (7.1 audio),// 6 (5.1 audio), or 2 (stereo only).#ifndef STB_VORBIS_MAX_CHANNELS#define STB_VORBIS_MAX_CHANNELS 16 // enough for anyone?#endif// STB_VORBIS_PUSHDATA_CRC_COUNT [number]// after a flush_pushdata(), stb_vorbis begins scanning for the// next valid page, without backtracking. when it finds something// that looks like a page, it streams through it and verifies its// CRC32. Should that validation fail, it keeps scanning. But it's// possible that _while_ streaming through to check the CRC32 of// one candidate page, it sees another candidate page. This #define// determines how many "overlapping" candidate pages it can search// at once. Note that "real" pages are typically ~4KB to ~8KB, whereas// garbage pages could be as big as 64KB, but probably average ~16KB.// So don't hose ourselves by scanning an apparent 64KB page and// missing a ton of real ones in the interim; so minimum of 2#ifndef STB_VORBIS_PUSHDATA_CRC_COUNT#define STB_VORBIS_PUSHDATA_CRC_COUNT 4#endif// STB_VORBIS_FAST_HUFFMAN_LENGTH [number]// sets the log size of the huffman-acceleration table. Maximum// supported value is 24. with larger numbers, more decodings are O(1),// but the table size is larger so worse cache missing, so you'll have// to probe (and try multiple ogg vorbis files) to find the sweet spot.#ifndef STB_VORBIS_FAST_HUFFMAN_LENGTH#define STB_VORBIS_FAST_HUFFMAN_LENGTH 10#endif// STB_VORBIS_FAST_BINARY_LENGTH [number]// sets the log size of the binary-search acceleration table. this// is used in similar fashion to the fast-huffman size to set initial// parameters for the binary search// STB_VORBIS_FAST_HUFFMAN_INT// The fast huffman tables are much more efficient if they can be// stored as 16-bit results instead of 32-bit results. This restricts// the codebooks to having only 65535 possible outcomes, though.// (At least, accelerated by the huffman table.)#ifndef STB_VORBIS_FAST_HUFFMAN_INTShowing the first 500 of 5585 diff lines for this file. This diff is INCOMPLETE; read the file or clone the repository for the rest.