Customized fork of github.com/rxi/lite

Compare changes

Choose any two refs to compare.

+16
.editorconfig
···
··· 1 + root = true 2 + 3 + [*.{c,h}] 4 + charset = utf-8 5 + end_of_line = lf 6 + indent_style = tab 7 + insert_final_newline = true 8 + trim_trailing_whitespace = true 9 + 10 + [*.{lua}] 11 + charset = utf-8 12 + end_of_line = lf 13 + indent_style = space 14 + indent_size = 2 15 + insert_final_newline = true 16 + trim_trailing_whitespace = true
-3
.github/FUNDING.yml
··· 1 - # These are supported funding model platforms 2 - 3 - github: rxi
···
+2
.gitignore
···
··· 1 + /lite 2 + /*.o
-19
LICENSE
··· 1 - Copyright (c) 2020 rxi 2 - 3 - Permission is hereby granted, free of charge, to any person obtaining a copy of 4 - this software and associated documentation files (the "Software"), to deal in 5 - the Software without restriction, including without limitation the rights to 6 - use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 - of the Software, and to permit persons to whom the Software is furnished to do 8 - so, subject to the following conditions: 9 - 10 - The above copyright notice and this permission notice shall be included in all 11 - copies or substantial portions of the Software. 12 - 13 - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 - SOFTWARE.
···
-40
README.md
··· 1 - # lite 2 - ![screenshot](https://user-images.githubusercontent.com/3920290/81471642-6c165880-91ea-11ea-8cd1-fae7ae8f0bc4.png) 3 - 4 - A lightweight text editor written in Lua 5 - 6 - * **[Get lite](https://github.com/rxi/lite/releases/latest)** โ€” Download 7 - for Windows and Linux 8 - * **[Get started](doc/usage.md)** โ€” A quick overview on how to get started 9 - * **[Get plugins](https://github.com/rxi/lite-plugins)** โ€” Add additional 10 - functionality 11 - * **[Get color themes](https://github.com/rxi/lite-colors)** โ€” Add additional colors 12 - themes 13 - 14 - ## Overview 15 - lite is a lightweight text editor written mostly in Lua โ€” it aims to provide 16 - something practical, pretty, *small* and fast, implemented as simply as 17 - possible; easy to modify and extend, or to use without doing either. 18 - 19 - ## Customization 20 - Additional functionality can be added through plugins which are available from 21 - the [plugins repository](https://github.com/rxi/lite-plugins); additional color 22 - themes can be found in the [colors repository](https://github.com/rxi/lite-colors). 23 - The editor can be customized by making changes to the 24 - [user module](data/user/init.lua). 25 - 26 - ## Building 27 - You can build the project yourself on Linux using the provided `build.sh` 28 - script. Note that the project does not need to be rebuilt if you are only making 29 - changes to the Lua portion of the code. 30 - 31 - ## Contributing 32 - Any additional functionality that can be added through a plugin should be done 33 - so as a plugin, after which a pull request to the 34 - [plugins repository](https://github.com/rxi/lite-plugins) can be made. In hopes 35 - of remaining lightweight, pull requests adding additional functionality to the 36 - core will likely not be merged. Bug reports and bug fixes are welcome. 37 - 38 - ## License 39 - This project is free software; you can redistribute it and/or modify it under 40 - the terms of the MIT license. See [LICENSE](LICENSE) for details.
···
+16
build.bat
···
··· 1 + @echo off 2 + 3 + rem download this: 4 + rem https://nuwen.net/mingw.html 5 + 6 + echo compiling (windows)... 7 + 8 + windres res.rc -O coff -o res.res 9 + gcc src/*.c src/api/*.c src/lib/lua52/*.c src/lib/stb/*.c^ 10 + -O3 -s -std=gnu11 -fno-strict-aliasing -Isrc -DLUA_USE_POPEN^ 11 + -Iwinlib/SDL2-2.0.10/x86_64-w64-mingw32/include^ 12 + -lmingw32 -lm -lSDL2main -lSDL2 -Lwinlib/SDL2-2.0.10/x86_64-w64-mingw32/lib^ 13 + -mwindows res.res^ 14 + -o lite.exe 15 + 16 + echo done
+1 -1
build.sh
··· 1 #!/bin/bash 2 3 cflags="-Wall -O3 -g -std=gnu11 -fno-strict-aliasing -Isrc" 4 - lflags="-lSDL2 -lm" 5 6 if [[ $* == *windows* ]]; then 7 platform="windows"
··· 1 #!/bin/bash 2 3 cflags="-Wall -O3 -g -std=gnu11 -fno-strict-aliasing -Isrc" 4 + lflags="-lSDL3 -lm" 5 6 if [[ $* == *windows* ]]; then 7 platform="windows"
+4
compile_flags.txt
···
··· 1 + -Wall 2 + -Isrc 3 + -fno-strict-aliasing 4 + -DLUA_USE_POSIX
+3 -1
data/core/commands/doc.lua
··· 160 local line1, _, line2 = doc():get_selection(true) 161 if line1 == line2 then line2 = line2 + 1 end 162 local text = doc():get_text(line1, 1, line2, math.huge) 163 - text = text:gsub("\n[\t ]*", " ") 164 doc():insert(line1, 1, text) 165 doc():remove(line1, #text + 1, line2, math.huge) 166 if doc():has_selection() then
··· 160 local line1, _, line2 = doc():get_selection(true) 161 if line1 == line2 then line2 = line2 + 1 end 162 local text = doc():get_text(line1, 1, line2, math.huge) 163 + text = text:gsub("(.-)\n[\t ]*", function(x) 164 + return x:find("^%s*$") and x or x .. " " 165 + end) 166 doc():insert(line1, 1, text) 167 doc():remove(line1, #text + 1, line2, math.huge) 168 if doc():has_selection() then
+1 -1
data/plugins/language_python.lua
··· 1 local syntax = require "core.syntax" 2 3 syntax.add { 4 - files = "%.py$", 5 headers = "^#!.*[ /]python", 6 comment = "#", 7 patterns = {
··· 1 local syntax = require "core.syntax" 2 3 syntax.add { 4 + files = { "%.py$", "%.pyw$" }, 5 headers = "^#!.*[ /]python", 6 comment = "#", 7 patterns = {
+4 -1
data/user/init.lua
··· 5 local config = require "core.config" 6 local style = require "core.style" 7 8 -- light theme: 9 - -- require "user.colors.summer" 10 11 -- key binding: 12 -- keymap.add { ["ctrl+escape"] = "core:quit" }
··· 5 local config = require "core.config" 6 local style = require "core.style" 7 8 + config.tab_type = "hard" 9 + config.indent_size = 8 10 + 11 -- light theme: 12 + require "user.colors.fall" 13 14 -- key binding: 15 -- keymap.add { ["ctrl+escape"] = "core:quit" }
+41
doc/original-readme.md
···
··· 1 + # lite 2 + ![screenshot](https://user-images.githubusercontent.com/3920290/81471642-6c165880-91ea-11ea-8cd1-fae7ae8f0bc4.png) 3 + 4 + A lightweight text editor written in Lua 5 + 6 + * **[Get lite](https://github.com/rxi/lite/releases/latest)** โ€” Download 7 + for Windows and Linux 8 + * **[Get started](doc/usage.md)** โ€” A quick overview on how to get started 9 + * **[Get plugins](https://github.com/rxi/lite-plugins)** โ€” Add additional 10 + functionality 11 + * **[Get color themes](https://github.com/rxi/lite-colors)** โ€” Add additional colors 12 + themes 13 + 14 + ## Overview 15 + lite is a lightweight text editor written mostly in Lua โ€” it aims to provide 16 + something practical, pretty, *small* and fast, implemented as simply as 17 + possible; easy to modify and extend, or to use without doing either. 18 + 19 + ## Customization 20 + Additional functionality can be added through plugins which are available from 21 + the [plugins repository](https://github.com/rxi/lite-plugins); additional color 22 + themes can be found in the [colors repository](https://github.com/rxi/lite-colors). 23 + The editor can be customized by making changes to the 24 + [user module](data/user/init.lua). 25 + 26 + ## Building 27 + You can build the project yourself on Linux using the `build.sh` script 28 + or on Windows using the `build.bat` script *([MinGW](https://nuwen.net/mingw.html) is required)*. 29 + Note that the project does not need to be rebuilt if you are only making changes 30 + to the Lua portion of the code. 31 + 32 + ## Contributing 33 + Any additional functionality that can be added through a plugin should be done 34 + so as a plugin, after which a pull request to the 35 + [plugins repository](https://github.com/rxi/lite-plugins) can be made. In hopes 36 + of remaining lightweight, pull requests adding additional functionality to the 37 + core will likely not be merged. Bug reports and bug fixes are welcome. 38 + 39 + ## License 40 + This project is free software; you can redistribute it and/or modify it under 41 + the terms of the MIT license. See [LICENSE](LICENSE) for details.
+53
license
···
··· 1 + === emmeline/lite: MIT License === 2 + 3 + Copyright (c) 2025 emmeline 4 + 5 + Permission is hereby granted, free of charge, to any person obtaining a copy of 6 + this software and associated documentation files (the "Software"), to deal in 7 + the Software without restriction, including without limitation the rights to 8 + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 + of the Software, and to permit persons to whom the Software is furnished to do 10 + so, subject to the following conditions: 11 + 12 + The above copyright notice and this permission notice shall be included in all 13 + copies or substantial portions of the Software. 14 + 15 + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 + SOFTWARE. 22 + 23 + === rxi/lite: MIT License === 24 + 25 + Copyright (c) 2020 rxi 26 + 27 + Permission is hereby granted, free of charge, to any person obtaining a copy of 28 + this software and associated documentation files (the "Software"), to deal in 29 + the Software without restriction, including without limitation the rights to 30 + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 31 + of the Software, and to permit persons to whom the Software is furnished to do 32 + so, subject to the following conditions: 33 + 34 + The above copyright notice and this permission notice shall be included in all 35 + copies or substantial portions of the Software. 36 + 37 + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 38 + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 39 + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 40 + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 41 + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 42 + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 43 + SOFTWARE. 44 + 45 + === lua 5.4.8: MIT License === 46 + 47 + Copyright ยฉ 1994โ€“2025 Lua.org, PUC-Rio. 48 + 49 + Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 50 + 51 + The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 52 + 53 + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+31
readme
···
··· 1 + lite (emmeline's fork) 2 + ====================== 3 + 4 + A fork of the [lite](https://github.com/rxi/lite) editor by rxi. 5 + 6 + A lightweight editor written in Lua. 7 + 8 + Usage: <doc/usage.md> 9 + Plugins: <https://github.com/rxi/lite-plugins> 10 + Themes: <https://github.com/rxi/lite-colors> 11 + 12 + Build 13 + ----- 14 + 15 + $ git clone https://tangled.org/emmeline.girlkisser.top/lite 16 + $ cd lite 17 + $ ./build.sh 18 + $ ln -s $(pwd)/lite ~/.local/bin/ 19 + $ lite . 20 + 21 + Changes 22 + ------- 23 + 24 + - [x] Reformat to my code style :p 25 + - [x] Update to SDL 3 26 + - [ ] Use LuaJIT 27 + 28 + License 29 + ------- 30 + 31 + MIT License
+9 -7
src/api/api.c
··· 6 7 8 static const luaL_Reg libs[] = { 9 - { "system", luaopen_system }, 10 - { "renderer", luaopen_renderer }, 11 - { NULL, NULL } 12 }; 13 14 - void api_load_libs(lua_State *L) { 15 - for (int i = 0; libs[i].name; i++) { 16 - luaL_requiref(L, libs[i].name, libs[i].func, 1); 17 - } 18 }
··· 6 7 8 static const luaL_Reg libs[] = { 9 + { "system", luaopen_system }, 10 + { "renderer", luaopen_renderer }, 11 + { NULL, NULL } 12 }; 13 14 + void api_load_libs(lua_State *L) 15 + { 16 + for (int i = 0 ; libs[i].name ; i++) 17 + { 18 + luaL_requiref(L, libs[i].name, libs[i].func, 1); 19 + } 20 }
+78 -70
src/api/renderer.c
··· 3 #include "rencache.h" 4 5 6 - static RenColor checkcolor(lua_State *L, int idx, int def) { 7 - RenColor color; 8 - if (lua_isnoneornil(L, idx)) { 9 - return (RenColor) { def, def, def, 255 }; 10 - } 11 - lua_rawgeti(L, idx, 1); 12 - lua_rawgeti(L, idx, 2); 13 - lua_rawgeti(L, idx, 3); 14 - lua_rawgeti(L, idx, 4); 15 - color.r = luaL_checknumber(L, -4); 16 - color.g = luaL_checknumber(L, -3); 17 - color.b = luaL_checknumber(L, -2); 18 - color.a = luaL_optnumber(L, -1, 255); 19 - lua_pop(L, 4); 20 - return color; 21 } 22 23 24 - static int f_show_debug(lua_State *L) { 25 - luaL_checkany(L, 1); 26 - rencache_show_debug(lua_toboolean(L, 1)); 27 - return 0; 28 } 29 30 31 - static int f_get_size(lua_State *L) { 32 - int w, h; 33 - ren_get_size(&w, &h); 34 - lua_pushnumber(L, w); 35 - lua_pushnumber(L, h); 36 - return 2; 37 } 38 39 40 - static int f_begin_frame(lua_State *L) { 41 - rencache_begin_frame(); 42 - return 0; 43 } 44 45 46 - static int f_end_frame(lua_State *L) { 47 - rencache_end_frame(); 48 - return 0; 49 } 50 51 52 - static int f_set_clip_rect(lua_State *L) { 53 - RenRect rect; 54 - rect.x = luaL_checknumber(L, 1); 55 - rect.y = luaL_checknumber(L, 2); 56 - rect.width = luaL_checknumber(L, 3); 57 - rect.height = luaL_checknumber(L, 4); 58 - rencache_set_clip_rect(rect); 59 - return 0; 60 } 61 62 63 - static int f_draw_rect(lua_State *L) { 64 - RenRect rect; 65 - rect.x = luaL_checknumber(L, 1); 66 - rect.y = luaL_checknumber(L, 2); 67 - rect.width = luaL_checknumber(L, 3); 68 - rect.height = luaL_checknumber(L, 4); 69 - RenColor color = checkcolor(L, 5, 255); 70 - rencache_draw_rect(rect, color); 71 - return 0; 72 } 73 74 75 - static int f_draw_text(lua_State *L) { 76 - RenFont **font = luaL_checkudata(L, 1, API_TYPE_FONT); 77 - const char *text = luaL_checkstring(L, 2); 78 - int x = luaL_checknumber(L, 3); 79 - int y = luaL_checknumber(L, 4); 80 - RenColor color = checkcolor(L, 5, 255); 81 - x = rencache_draw_text(*font, text, x, y, color); 82 - lua_pushnumber(L, x); 83 - return 1; 84 } 85 86 87 static const luaL_Reg lib[] = { 88 - { "show_debug", f_show_debug }, 89 - { "get_size", f_get_size }, 90 - { "begin_frame", f_begin_frame }, 91 - { "end_frame", f_end_frame }, 92 - { "set_clip_rect", f_set_clip_rect }, 93 - { "draw_rect", f_draw_rect }, 94 - { "draw_text", f_draw_text }, 95 - { NULL, NULL } 96 }; 97 98 99 int luaopen_renderer_font(lua_State *L); 100 101 - int luaopen_renderer(lua_State *L) { 102 - luaL_newlib(L, lib); 103 - luaopen_renderer_font(L); 104 - lua_setfield(L, -2, "font"); 105 - return 1; 106 }
··· 3 #include "rencache.h" 4 5 6 + static RenColor checkcolor(lua_State *L, int idx, int def) 7 + { 8 + RenColor color; 9 + if (lua_isnoneornil(L, idx)) 10 + return (RenColor){ def, def, def, 255 }; 11 + lua_rawgeti(L, idx, 1); 12 + lua_rawgeti(L, idx, 2); 13 + lua_rawgeti(L, idx, 3); 14 + lua_rawgeti(L, idx, 4); 15 + color.r = luaL_checknumber(L, -4); 16 + color.g = luaL_checknumber(L, -3); 17 + color.b = luaL_checknumber(L, -2); 18 + color.a = luaL_optnumber(L, -1, 255); 19 + lua_pop(L, 4); 20 + return color; 21 } 22 23 24 + static int f_show_debug(lua_State *L) 25 + { 26 + luaL_checkany(L, 1); 27 + rencache_show_debug(lua_toboolean(L, 1)); 28 + return 0; 29 } 30 31 32 + static int f_get_size(lua_State *L) 33 + { 34 + int w, h; 35 + ren_get_size(&w, &h); 36 + lua_pushnumber(L, w); 37 + lua_pushnumber(L, h); 38 + return 2; 39 } 40 41 42 + static int f_begin_frame(lua_State *L) 43 + { 44 + rencache_begin_frame(); 45 + return 0; 46 } 47 48 49 + static int f_end_frame(lua_State *L) 50 + { 51 + rencache_end_frame(); 52 + return 0; 53 } 54 55 56 + static int f_set_clip_rect(lua_State *L) 57 + { 58 + RenRect rect; 59 + rect.x = luaL_checknumber(L, 1); 60 + rect.y = luaL_checknumber(L, 2); 61 + rect.width = luaL_checknumber(L, 3); 62 + rect.height = luaL_checknumber(L, 4); 63 + rencache_set_clip_rect(rect); 64 + return 0; 65 } 66 67 68 + static int f_draw_rect(lua_State *L) 69 + { 70 + RenRect rect; 71 + rect.x = luaL_checknumber(L, 1); 72 + rect.y = luaL_checknumber(L, 2); 73 + rect.width = luaL_checknumber(L, 3); 74 + rect.height = luaL_checknumber(L, 4); 75 + RenColor color = checkcolor(L, 5, 255); 76 + rencache_draw_rect(rect, color); 77 + return 0; 78 } 79 80 81 + static int f_draw_text(lua_State *L) 82 + { 83 + RenFont **font = luaL_checkudata(L, 1, API_TYPE_FONT); 84 + const char *text = luaL_checkstring(L, 2); 85 + int x = luaL_checknumber(L, 3); 86 + int y = luaL_checknumber(L, 4); 87 + RenColor color = checkcolor(L, 5, 255); 88 + x = rencache_draw_text(*font, text, x, y, color); 89 + lua_pushnumber(L, x); 90 + return 1; 91 } 92 93 94 static const luaL_Reg lib[] = { 95 + { "show_debug", f_show_debug }, 96 + { "get_size", f_get_size }, 97 + { "begin_frame", f_begin_frame }, 98 + { "end_frame", f_end_frame }, 99 + { "set_clip_rect", f_set_clip_rect }, 100 + { "draw_rect", f_draw_rect }, 101 + { "draw_text", f_draw_text }, 102 + { NULL, NULL } 103 }; 104 105 106 int luaopen_renderer_font(lua_State *L); 107 108 + int luaopen_renderer(lua_State *L) 109 + { 110 + luaL_newlib(L, lib); 111 + luaopen_renderer_font(L); 112 + lua_setfield(L, -2, "font"); 113 + return 1; 114 }
+46 -38
src/api/renderer_font.c
··· 3 #include "rencache.h" 4 5 6 - static int f_load(lua_State *L) { 7 - const char *filename = luaL_checkstring(L, 1); 8 - float size = luaL_checknumber(L, 2); 9 - RenFont **self = lua_newuserdata(L, sizeof(*self)); 10 - luaL_setmetatable(L, API_TYPE_FONT); 11 - *self = ren_load_font(filename, size); 12 - if (!*self) { luaL_error(L, "failed to load font"); } 13 - return 1; 14 } 15 16 17 - static int f_set_tab_width(lua_State *L) { 18 - RenFont **self = luaL_checkudata(L, 1, API_TYPE_FONT); 19 - int n = luaL_checknumber(L, 2); 20 - ren_set_font_tab_width(*self, n); 21 - return 0; 22 } 23 24 25 - static int f_gc(lua_State *L) { 26 - RenFont **self = luaL_checkudata(L, 1, API_TYPE_FONT); 27 - if (*self) { rencache_free_font(*self); } 28 - return 0; 29 } 30 31 32 - static int f_get_width(lua_State *L) { 33 - RenFont **self = luaL_checkudata(L, 1, API_TYPE_FONT); 34 - const char *text = luaL_checkstring(L, 2); 35 - lua_pushnumber(L, ren_get_font_width(*self, text) ); 36 - return 1; 37 } 38 39 40 - static int f_get_height(lua_State *L) { 41 - RenFont **self = luaL_checkudata(L, 1, API_TYPE_FONT); 42 - lua_pushnumber(L, ren_get_font_height(*self) ); 43 - return 1; 44 } 45 46 47 static const luaL_Reg lib[] = { 48 - { "__gc", f_gc }, 49 - { "load", f_load }, 50 - { "set_tab_width", f_set_tab_width }, 51 - { "get_width", f_get_width }, 52 - { "get_height", f_get_height }, 53 - { NULL, NULL } 54 }; 55 56 - int luaopen_renderer_font(lua_State *L) { 57 - luaL_newmetatable(L, API_TYPE_FONT); 58 - luaL_setfuncs(L, lib, 0); 59 - lua_pushvalue(L, -1); 60 - lua_setfield(L, -2, "__index"); 61 - return 1; 62 }
··· 3 #include "rencache.h" 4 5 6 + static int f_load(lua_State *L) 7 + { 8 + const char *filename = luaL_checkstring(L, 1); 9 + float size = luaL_checknumber(L, 2); 10 + RenFont **self = lua_newuserdata(L, sizeof(*self)); 11 + luaL_setmetatable(L, API_TYPE_FONT); 12 + *self = ren_load_font(filename, size); 13 + if (!*self) 14 + luaL_error(L, "failed to load font"); 15 + return 1; 16 } 17 18 19 + static int f_set_tab_width(lua_State *L) 20 + { 21 + RenFont **self = luaL_checkudata(L, 1, API_TYPE_FONT); 22 + int n = luaL_checknumber(L, 2); 23 + ren_set_font_tab_width(*self, n); 24 + return 0; 25 } 26 27 28 + static int f_gc(lua_State *L) 29 + { 30 + RenFont **self = luaL_checkudata(L, 1, API_TYPE_FONT); 31 + if (*self) 32 + rencache_free_font(*self); 33 + return 0; 34 } 35 36 37 + static int f_get_width(lua_State *L) 38 + { 39 + RenFont **self = luaL_checkudata(L, 1, API_TYPE_FONT); 40 + const char *text = luaL_checkstring(L, 2); 41 + lua_pushnumber(L, ren_get_font_width(*self, text) ); 42 + return 1; 43 } 44 45 46 + static int f_get_height(lua_State *L) 47 + { 48 + RenFont **self = luaL_checkudata(L, 1, API_TYPE_FONT); 49 + lua_pushnumber(L, ren_get_font_height(*self) ); 50 + return 1; 51 } 52 53 54 static const luaL_Reg lib[] = { 55 + { "__gc", f_gc }, 56 + { "load", f_load }, 57 + { "set_tab_width", f_set_tab_width }, 58 + { "get_width", f_get_width }, 59 + { "get_height", f_get_height }, 60 + { NULL, NULL } 61 }; 62 63 + int luaopen_renderer_font(lua_State *L) 64 + { 65 + luaL_newmetatable(L, API_TYPE_FONT); 66 + luaL_setfuncs(L, lib, 0); 67 + lua_pushvalue(L, -1); 68 + lua_setfield(L, -2, "__index"); 69 + return 1; 70 }
+333 -292
src/api/system.c
··· 1 - #include <SDL2/SDL.h> 2 #include <stdbool.h> 3 #include <ctype.h> 4 #include <dirent.h> 5 #include <unistd.h> ··· 8 #include "api.h" 9 #include "rencache.h" 10 #ifdef _WIN32 11 - #include <windows.h> 12 #endif 13 14 extern SDL_Window *window; 15 16 17 - static const char* button_name(int button) { 18 - switch (button) { 19 - case 1 : return "left"; 20 - case 2 : return "middle"; 21 - case 3 : return "right"; 22 - default : return "?"; 23 - } 24 } 25 26 27 - static char* key_name(char *dst, int sym) { 28 - strcpy(dst, SDL_GetKeyName(sym)); 29 - char *p = dst; 30 - while (*p) { 31 - *p = tolower(*p); 32 - p++; 33 - } 34 - return dst; 35 } 36 37 38 - static int f_poll_event(lua_State *L) { 39 - char buf[16]; 40 - int mx, my, wx, wy; 41 - SDL_Event e; 42 43 top: 44 - if ( !SDL_PollEvent(&e) ) { 45 - return 0; 46 - } 47 48 - switch (e.type) { 49 - case SDL_QUIT: 50 - lua_pushstring(L, "quit"); 51 - return 1; 52 53 - case SDL_WINDOWEVENT: 54 - if (e.window.event == SDL_WINDOWEVENT_RESIZED) { 55 - lua_pushstring(L, "resized"); 56 - lua_pushnumber(L, e.window.data1); 57 - lua_pushnumber(L, e.window.data2); 58 - return 3; 59 - } else if (e.window.event == SDL_WINDOWEVENT_EXPOSED) { 60 - rencache_invalidate(); 61 - lua_pushstring(L, "exposed"); 62 - return 1; 63 - } 64 - /* on some systems, when alt-tabbing to the window SDL will queue up 65 - ** several KEYDOWN events for the `tab` key; we flush all keydown 66 - ** events on focus so these are discarded */ 67 - if (e.window.event == SDL_WINDOWEVENT_FOCUS_GAINED) { 68 - SDL_FlushEvent(SDL_KEYDOWN); 69 - } 70 - goto top; 71 72 - case SDL_DROPFILE: 73 - SDL_GetGlobalMouseState(&mx, &my); 74 - SDL_GetWindowPosition(window, &wx, &wy); 75 - lua_pushstring(L, "filedropped"); 76 - lua_pushstring(L, e.drop.file); 77 - lua_pushnumber(L, mx - wx); 78 - lua_pushnumber(L, my - wy); 79 - SDL_free(e.drop.file); 80 - return 4; 81 82 - case SDL_KEYDOWN: 83 - lua_pushstring(L, "keypressed"); 84 - lua_pushstring(L, key_name(buf, e.key.keysym.sym)); 85 - return 2; 86 87 - case SDL_KEYUP: 88 - lua_pushstring(L, "keyreleased"); 89 - lua_pushstring(L, key_name(buf, e.key.keysym.sym)); 90 - return 2; 91 92 - case SDL_TEXTINPUT: 93 - lua_pushstring(L, "textinput"); 94 - lua_pushstring(L, e.text.text); 95 - return 2; 96 97 - case SDL_MOUSEBUTTONDOWN: 98 - if (e.button.button == 1) { SDL_CaptureMouse(1); } 99 - lua_pushstring(L, "mousepressed"); 100 - lua_pushstring(L, button_name(e.button.button)); 101 - lua_pushnumber(L, e.button.x); 102 - lua_pushnumber(L, e.button.y); 103 - lua_pushnumber(L, e.button.clicks); 104 - return 5; 105 106 - case SDL_MOUSEBUTTONUP: 107 - if (e.button.button == 1) { SDL_CaptureMouse(0); } 108 - lua_pushstring(L, "mousereleased"); 109 - lua_pushstring(L, button_name(e.button.button)); 110 - lua_pushnumber(L, e.button.x); 111 - lua_pushnumber(L, e.button.y); 112 - return 4; 113 114 - case SDL_MOUSEMOTION: 115 - lua_pushstring(L, "mousemoved"); 116 - lua_pushnumber(L, e.motion.x); 117 - lua_pushnumber(L, e.motion.y); 118 - lua_pushnumber(L, e.motion.xrel); 119 - lua_pushnumber(L, e.motion.yrel); 120 - return 5; 121 122 - case SDL_MOUSEWHEEL: 123 - lua_pushstring(L, "mousewheel"); 124 - lua_pushnumber(L, e.wheel.y); 125 - return 2; 126 127 - default: 128 - goto top; 129 - } 130 131 - return 0; 132 } 133 134 135 - static int f_wait_event(lua_State *L) { 136 - double n = luaL_checknumber(L, 1); 137 - lua_pushboolean(L, SDL_WaitEventTimeout(NULL, n * 1000)); 138 - return 1; 139 } 140 141 142 - static SDL_Cursor* cursor_cache[SDL_SYSTEM_CURSOR_HAND + 1]; 143 144 static const char *cursor_opts[] = { 145 - "arrow", 146 - "ibeam", 147 - "sizeh", 148 - "sizev", 149 - "hand", 150 - NULL 151 }; 152 153 static const int cursor_enums[] = { 154 - SDL_SYSTEM_CURSOR_ARROW, 155 - SDL_SYSTEM_CURSOR_IBEAM, 156 - SDL_SYSTEM_CURSOR_SIZEWE, 157 - SDL_SYSTEM_CURSOR_SIZENS, 158 - SDL_SYSTEM_CURSOR_HAND 159 }; 160 161 - static int f_set_cursor(lua_State *L) { 162 - int opt = luaL_checkoption(L, 1, "arrow", cursor_opts); 163 - int n = cursor_enums[opt]; 164 - SDL_Cursor *cursor = cursor_cache[n]; 165 - if (!cursor) { 166 - cursor = SDL_CreateSystemCursor(n); 167 - cursor_cache[n] = cursor; 168 - } 169 - SDL_SetCursor(cursor); 170 - return 0; 171 } 172 173 174 - static int f_set_window_title(lua_State *L) { 175 - const char *title = luaL_checkstring(L, 1); 176 - SDL_SetWindowTitle(window, title); 177 - return 0; 178 } 179 180 181 static const char *window_opts[] = { "normal", "maximized", "fullscreen", 0 }; 182 enum { WIN_NORMAL, WIN_MAXIMIZED, WIN_FULLSCREEN }; 183 184 - static int f_set_window_mode(lua_State *L) { 185 - int n = luaL_checkoption(L, 1, "normal", window_opts); 186 - SDL_SetWindowFullscreen(window, 187 - n == WIN_FULLSCREEN ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0); 188 - if (n == WIN_NORMAL) { SDL_RestoreWindow(window); } 189 - if (n == WIN_MAXIMIZED) { SDL_MaximizeWindow(window); } 190 - return 0; 191 } 192 193 194 - static int f_window_has_focus(lua_State *L) { 195 - unsigned flags = SDL_GetWindowFlags(window); 196 - lua_pushboolean(L, flags & SDL_WINDOW_INPUT_FOCUS); 197 - return 1; 198 } 199 200 201 - static int f_show_confirm_dialog(lua_State *L) { 202 - const char *title = luaL_checkstring(L, 1); 203 - const char *msg = luaL_checkstring(L, 2); 204 205 - #if _WIN32 206 - int id = MessageBox(0, msg, title, MB_YESNO | MB_ICONWARNING); 207 - lua_pushboolean(L, id == IDYES); 208 209 - #else 210 - SDL_MessageBoxButtonData buttons[] = { 211 - { SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, 1, "Yes" }, 212 - { SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, 0, "No" }, 213 - }; 214 - SDL_MessageBoxData data = { 215 - .title = title, 216 - .message = msg, 217 - .numbuttons = 2, 218 - .buttons = buttons, 219 - }; 220 - int buttonid; 221 - SDL_ShowMessageBox(&data, &buttonid); 222 - lua_pushboolean(L, buttonid == 1); 223 - #endif 224 - return 1; 225 } 226 227 228 - static int f_chdir(lua_State *L) { 229 - const char *path = luaL_checkstring(L, 1); 230 - int err = chdir(path); 231 - if (err) { luaL_error(L, "chdir() failed"); } 232 - return 0; 233 } 234 235 236 - static int f_list_dir(lua_State *L) { 237 - const char *path = luaL_checkstring(L, 1); 238 239 - DIR *dir = opendir(path); 240 - if (!dir) { 241 - lua_pushnil(L); 242 - lua_pushstring(L, strerror(errno)); 243 - return 2; 244 - } 245 246 - lua_newtable(L); 247 - int i = 1; 248 - struct dirent *entry; 249 - while ( (entry = readdir(dir)) ) { 250 - if (strcmp(entry->d_name, "." ) == 0) { continue; } 251 - if (strcmp(entry->d_name, "..") == 0) { continue; } 252 - lua_pushstring(L, entry->d_name); 253 - lua_rawseti(L, -2, i); 254 - i++; 255 - } 256 257 - closedir(dir); 258 - return 1; 259 } 260 261 262 #ifdef _WIN32 263 - #include <windows.h> 264 - #define realpath(x, y) _fullpath(y, x, MAX_PATH) 265 #endif 266 267 - static int f_absolute_path(lua_State *L) { 268 - const char *path = luaL_checkstring(L, 1); 269 - char *res = realpath(path, NULL); 270 - if (!res) { return 0; } 271 - lua_pushstring(L, res); 272 - free(res); 273 - return 1; 274 } 275 276 277 - static int f_get_file_info(lua_State *L) { 278 - const char *path = luaL_checkstring(L, 1); 279 280 - struct stat s; 281 - int err = stat(path, &s); 282 - if (err < 0) { 283 - lua_pushnil(L); 284 - lua_pushstring(L, strerror(errno)); 285 - return 2; 286 - } 287 288 - lua_newtable(L); 289 - lua_pushnumber(L, s.st_mtime); 290 - lua_setfield(L, -2, "modified"); 291 292 - lua_pushnumber(L, s.st_size); 293 - lua_setfield(L, -2, "size"); 294 295 - if (S_ISREG(s.st_mode)) { 296 - lua_pushstring(L, "file"); 297 - } else if (S_ISDIR(s.st_mode)) { 298 - lua_pushstring(L, "dir"); 299 - } else { 300 - lua_pushnil(L); 301 - } 302 - lua_setfield(L, -2, "type"); 303 304 - return 1; 305 } 306 307 308 - static int f_get_clipboard(lua_State *L) { 309 - char *text = SDL_GetClipboardText(); 310 - if (!text) { return 0; } 311 - lua_pushstring(L, text); 312 - SDL_free(text); 313 - return 1; 314 } 315 316 317 - static int f_set_clipboard(lua_State *L) { 318 - const char *text = luaL_checkstring(L, 1); 319 - SDL_SetClipboardText(text); 320 - return 0; 321 } 322 323 324 - static int f_get_time(lua_State *L) { 325 - double n = SDL_GetPerformanceCounter() / (double) SDL_GetPerformanceFrequency(); 326 - lua_pushnumber(L, n); 327 - return 1; 328 } 329 330 331 - static int f_sleep(lua_State *L) { 332 - double n = luaL_checknumber(L, 1); 333 - SDL_Delay(n * 1000); 334 - return 0; 335 } 336 337 338 - static int f_exec(lua_State *L) { 339 - size_t len; 340 - const char *cmd = luaL_checklstring(L, 1, &len); 341 - char *buf = malloc(len + 32); 342 - if (!buf) { luaL_error(L, "buffer allocation failed"); } 343 - #if _WIN32 344 - sprintf(buf, "cmd /c \"%s\"", cmd); 345 - WinExec(buf, SW_HIDE); 346 - #else 347 - sprintf(buf, "%s &", cmd); 348 - int res = system(buf); 349 - (void) res; 350 - #endif 351 - free(buf); 352 - return 0; 353 } 354 355 356 - static int f_fuzzy_match(lua_State *L) { 357 - const char *str = luaL_checkstring(L, 1); 358 - const char *ptn = luaL_checkstring(L, 2); 359 - int score = 0; 360 - int run = 0; 361 362 - while (*str && *ptn) { 363 - while (*str == ' ') { str++; } 364 - while (*ptn == ' ') { ptn++; } 365 - if (tolower(*str) == tolower(*ptn)) { 366 - score += run * 10 - (*str != *ptn); 367 - run++; 368 - ptn++; 369 - } else { 370 - score -= 10; 371 - run = 0; 372 - } 373 - str++; 374 - } 375 - if (*ptn) { return 0; } 376 377 - lua_pushnumber(L, score - (int) strlen(str)); 378 - return 1; 379 } 380 381 382 static const luaL_Reg lib[] = { 383 - { "poll_event", f_poll_event }, 384 - { "wait_event", f_wait_event }, 385 - { "set_cursor", f_set_cursor }, 386 - { "set_window_title", f_set_window_title }, 387 - { "set_window_mode", f_set_window_mode }, 388 - { "window_has_focus", f_window_has_focus }, 389 - { "show_confirm_dialog", f_show_confirm_dialog }, 390 - { "chdir", f_chdir }, 391 - { "list_dir", f_list_dir }, 392 - { "absolute_path", f_absolute_path }, 393 - { "get_file_info", f_get_file_info }, 394 - { "get_clipboard", f_get_clipboard }, 395 - { "set_clipboard", f_set_clipboard }, 396 - { "get_time", f_get_time }, 397 - { "sleep", f_sleep }, 398 - { "exec", f_exec }, 399 - { "fuzzy_match", f_fuzzy_match }, 400 - { NULL, NULL } 401 }; 402 403 404 - int luaopen_system(lua_State *L) { 405 - luaL_newlib(L, lib); 406 - return 1; 407 }
··· 1 + #include <SDL3/SDL.h> 2 #include <stdbool.h> 3 + #include <stdlib.h> 4 #include <ctype.h> 5 #include <dirent.h> 6 #include <unistd.h> ··· 9 #include "api.h" 10 #include "rencache.h" 11 #ifdef _WIN32 12 + # include <windows.h> 13 #endif 14 15 extern SDL_Window *window; 16 17 18 + static const char* button_name(int button) 19 + { 20 + switch (button) 21 + { 22 + case 1 : return "left"; 23 + case 2 : return "middle"; 24 + case 3 : return "right"; 25 + default : return "?"; 26 + } 27 } 28 29 30 + static char* key_name(char *dst, int sym) 31 + { 32 + strcpy(dst, SDL_GetKeyName(sym)); 33 + char *p = dst; 34 + while (*p) 35 + { 36 + *p = tolower(*p); 37 + p++; 38 + } 39 + return dst; 40 } 41 42 43 + static int f_poll_event(lua_State *L) 44 + { 45 + char buf[16]; 46 + float mx, my; 47 + int wx, wy; 48 + SDL_Event e; 49 50 top: 51 + if (!SDL_PollEvent(&e)) 52 + return 0; 53 + 54 + switch (e.type) 55 + { 56 + case SDL_EVENT_QUIT : 57 + lua_pushstring(L, "quit"); 58 + return 1; 59 + 60 + case SDL_EVENT_WINDOW_RESIZED: 61 + lua_pushstring(L, "resized"); 62 + lua_pushnumber(L, e.window.data1); 63 + lua_pushnumber(L, e.window.data2); 64 + return 3; 65 66 + case SDL_EVENT_WINDOW_EXPOSED: 67 + rencache_invalidate(); 68 + lua_pushstring(L, "exposed"); 69 + return 1; 70 71 + case SDL_EVENT_WINDOW_FOCUS_GAINED: 72 + /* on some systems, when alt-tabbing to the window SDL will queue up 73 + ** several KEYDOWN events for the `tab` key; we flush all keydown 74 + ** events on focus so these are discarded */ 75 + SDL_FlushEvent(SDL_EVENT_KEY_DOWN); 76 + goto top; 77 78 + case SDL_EVENT_DROP_FILE : 79 + SDL_GetGlobalMouseState(&mx, &my); 80 + SDL_GetWindowPosition(window, &wx, &wy); 81 + lua_pushstring(L, "filedropped"); 82 + lua_pushstring(L, e.drop.data); 83 + lua_pushnumber(L, mx - wx); 84 + lua_pushnumber(L, my - wy); 85 + return 4; 86 87 + case SDL_EVENT_KEY_DOWN : 88 + lua_pushstring(L, "keypressed"); 89 + lua_pushstring(L, key_name(buf, e.key.key)); 90 + return 2; 91 92 + case SDL_EVENT_KEY_UP : 93 + lua_pushstring(L, "keyreleased"); 94 + lua_pushstring(L, key_name(buf, e.key.key)); 95 + return 2; 96 97 + case SDL_EVENT_TEXT_INPUT : 98 + lua_pushstring(L, "textinput"); 99 + lua_pushstring(L, e.text.text); 100 + return 2; 101 102 + case SDL_EVENT_MOUSE_BUTTON_DOWN : 103 + if (e.button.button == 1) 104 + SDL_CaptureMouse(1); 105 + lua_pushstring(L, "mousepressed"); 106 + lua_pushstring(L, button_name(e.button.button)); 107 + lua_pushnumber(L, e.button.x); 108 + lua_pushnumber(L, e.button.y); 109 + lua_pushnumber(L, e.button.clicks); 110 + return 5; 111 112 + case SDL_EVENT_MOUSE_BUTTON_UP : 113 + if (e.button.button == 1) 114 + SDL_CaptureMouse(0); 115 + lua_pushstring(L, "mousereleased"); 116 + lua_pushstring(L, button_name(e.button.button)); 117 + lua_pushnumber(L, e.button.x); 118 + lua_pushnumber(L, e.button.y); 119 + return 4; 120 121 + case SDL_EVENT_MOUSE_MOTION : 122 + lua_pushstring(L, "mousemoved"); 123 + lua_pushnumber(L, e.motion.x); 124 + lua_pushnumber(L, e.motion.y); 125 + lua_pushnumber(L, e.motion.xrel); 126 + lua_pushnumber(L, e.motion.yrel); 127 + return 5; 128 129 + case SDL_EVENT_MOUSE_WHEEL : 130 + lua_pushstring(L, "mousewheel"); 131 + lua_pushnumber(L, e.wheel.y); 132 + return 2; 133 134 + default: 135 + goto top; 136 + } 137 138 + return 0; 139 } 140 141 142 + static int f_wait_event(lua_State *L) 143 + { 144 + double n = luaL_checknumber(L, 1); 145 + lua_pushboolean(L, SDL_WaitEventTimeout(NULL, n * 1000)); 146 + return 1; 147 } 148 149 150 + static SDL_Cursor* cursor_cache[SDL_SYSTEM_CURSOR_POINTER + 1]; 151 152 static const char *cursor_opts[] = { 153 + "arrow", 154 + "ibeam", 155 + "sizeh", 156 + "sizev", 157 + "hand", 158 + NULL 159 }; 160 161 static const int cursor_enums[] = { 162 + SDL_SYSTEM_CURSOR_DEFAULT, 163 + SDL_SYSTEM_CURSOR_TEXT, 164 + SDL_SYSTEM_CURSOR_EW_RESIZE, 165 + SDL_SYSTEM_CURSOR_NS_RESIZE, 166 + SDL_SYSTEM_CURSOR_POINTER 167 }; 168 169 + static int f_set_cursor(lua_State *L) 170 + { 171 + int opt = luaL_checkoption(L, 1, "arrow", cursor_opts); 172 + int n = cursor_enums[opt]; 173 + SDL_Cursor *cursor = cursor_cache[n]; 174 + if (!cursor) 175 + { 176 + cursor = SDL_CreateSystemCursor(n); 177 + cursor_cache[n] = cursor; 178 + } 179 + SDL_SetCursor(cursor); 180 + return 0; 181 } 182 183 184 + static int f_set_window_title(lua_State *L) 185 + { 186 + const char *title = luaL_checkstring(L, 1); 187 + SDL_SetWindowTitle(window, title); 188 + return 0; 189 } 190 191 192 static const char *window_opts[] = { "normal", "maximized", "fullscreen", 0 }; 193 enum { WIN_NORMAL, WIN_MAXIMIZED, WIN_FULLSCREEN }; 194 195 + static int f_set_window_mode(lua_State *L) 196 + { 197 + int n = luaL_checkoption(L, 1, "normal", window_opts); 198 + SDL_SetWindowFullscreen(window, n == WIN_FULLSCREEN ? SDL_WINDOW_FULLSCREEN : 0); 199 + if (n == WIN_NORMAL) 200 + SDL_RestoreWindow(window); 201 + if (n == WIN_MAXIMIZED) 202 + SDL_MaximizeWindow(window); 203 + return 0; 204 } 205 206 207 + static int f_window_has_focus(lua_State *L) 208 + { 209 + unsigned flags = SDL_GetWindowFlags(window); 210 + lua_pushboolean(L, flags & SDL_WINDOW_INPUT_FOCUS); 211 + return 1; 212 } 213 214 215 + static int f_show_confirm_dialog(lua_State *L) 216 + { 217 + const char *title = luaL_checkstring(L, 1); 218 + const char *msg = luaL_checkstring(L, 2); 219 220 + # if _WIN32 221 + int id = MessageBox(0, msg, title, MB_YESNO | MB_ICONWARNING); 222 + lua_pushboolean(L, id == IDYES); 223 + # else 224 + SDL_MessageBoxButtonData buttons[] = { 225 + { SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, 1, "Yes" }, 226 + { SDL_MESSAGEBOX_BUTTON_ESCAPEKEY_DEFAULT, 0, "No" }, 227 + }; 228 + SDL_MessageBoxData data = { 229 + .title = title, 230 + .message = msg, 231 + .numbuttons = 2, 232 + .buttons = buttons, 233 + }; 234 + int buttonid; 235 + SDL_ShowMessageBox(&data, &buttonid); 236 + lua_pushboolean(L, buttonid == 1); 237 + # endif 238 239 + return 1; 240 } 241 242 243 + static int f_chdir(lua_State *L) 244 + { 245 + const char *path = luaL_checkstring(L, 1); 246 + int err = chdir(path); 247 + if (err) 248 + luaL_error(L, "chdir() failed"); 249 + return 0; 250 } 251 252 253 + static int f_list_dir(lua_State *L) 254 + { 255 + const char *path = luaL_checkstring(L, 1); 256 257 + DIR *dir = opendir(path); 258 + if (!dir) 259 + { 260 + lua_pushnil(L); 261 + lua_pushstring(L, strerror(errno)); 262 + return 2; 263 + } 264 265 + lua_newtable(L); 266 + int i = 1; 267 + struct dirent *entry; 268 + while ((entry = readdir(dir))) 269 + { 270 + if (strcmp(entry->d_name, "." ) == 0 || strcmp(entry->d_name, "..") == 0) 271 + continue; 272 + lua_pushstring(L, entry->d_name); 273 + lua_rawseti(L, -2, i); 274 + i++; 275 + } 276 277 + closedir(dir); 278 + return 1; 279 } 280 281 282 #ifdef _WIN32 283 + # include <windows.h> 284 + # define realpath(x, y) _fullpath(y, x, MAX_PATH) 285 #endif 286 287 + static int f_absolute_path(lua_State *L) 288 + { 289 + const char *path = luaL_checkstring(L, 1); 290 + char *res = realpath(path, NULL); 291 + if (!res) 292 + return 0; 293 + lua_pushstring(L, res); 294 + free(res); 295 + return 1; 296 } 297 298 299 + static int f_get_file_info(lua_State *L) 300 + { 301 + const char *path = luaL_checkstring(L, 1); 302 303 + struct stat s; 304 + int err = stat(path, &s); 305 + if (err < 0) 306 + { 307 + lua_pushnil(L); 308 + lua_pushstring(L, strerror(errno)); 309 + return 2; 310 + } 311 312 + lua_newtable(L); 313 + lua_pushnumber(L, s.st_mtime); 314 + lua_setfield(L, -2, "modified"); 315 316 + lua_pushnumber(L, s.st_size); 317 + lua_setfield(L, -2, "size"); 318 319 + if (S_ISREG(s.st_mode)) 320 + lua_pushstring(L, "file"); 321 + else if (S_ISDIR(s.st_mode)) 322 + lua_pushstring(L, "dir"); 323 + else 324 + lua_pushnil(L); 325 + lua_setfield(L, -2, "type"); 326 327 + return 1; 328 } 329 330 331 + static int f_get_clipboard(lua_State *L) 332 + { 333 + char *text = SDL_GetClipboardText(); 334 + if (!text) 335 + return 0; 336 + lua_pushstring(L, text); 337 + SDL_free(text); 338 + return 1; 339 } 340 341 342 + static int f_set_clipboard(lua_State *L) 343 + { 344 + const char *text = luaL_checkstring(L, 1); 345 + SDL_SetClipboardText(text); 346 + return 0; 347 } 348 349 350 + static int f_get_time(lua_State *L) 351 + { 352 + double n = SDL_GetPerformanceCounter() / (double) SDL_GetPerformanceFrequency(); 353 + lua_pushnumber(L, n); 354 + return 1; 355 } 356 357 358 + static int f_sleep(lua_State *L) 359 + { 360 + double n = luaL_checknumber(L, 1); 361 + SDL_Delay(n * 1000); 362 + return 0; 363 } 364 365 366 + static int f_exec(lua_State *L) 367 + { 368 + size_t len; 369 + const char *cmd = luaL_checklstring(L, 1, &len); 370 + char *buf = malloc(len + 32); 371 + if (!buf) 372 + luaL_error(L, "buffer allocation failed"); 373 + 374 + # if _WIN32 375 + sprintf(buf, "cmd /c \"%s\"", cmd); 376 + WinExec(buf, SW_HIDE); 377 + # else 378 + sprintf(buf, "%s &", cmd); 379 + int res = system(buf); 380 + (void)res; 381 + # endif 382 + 383 + free(buf); 384 + return 0; 385 } 386 387 388 + static int f_fuzzy_match(lua_State *L) 389 + { 390 + const char *str = luaL_checkstring(L, 1); 391 + const char *ptn = luaL_checkstring(L, 2); 392 + int score = 0; 393 + int run = 0; 394 395 + while (*str && *ptn) 396 + { 397 + while (*str == ' ') 398 + str++; 399 + while (*ptn == ' ') 400 + ptn++; 401 + if (tolower(*str) == tolower(*ptn)) 402 + { 403 + score += run * 10 - (*str != *ptn); 404 + run++; 405 + ptn++; 406 + } 407 + else 408 + { 409 + score -= 10; 410 + run = 0; 411 + } 412 + str++; 413 + } 414 + if (*ptn) 415 + return 0; 416 417 + lua_pushnumber(L, score - (int) strlen(str)); 418 + return 1; 419 } 420 421 422 static const luaL_Reg lib[] = { 423 + { "poll_event", f_poll_event }, 424 + { "wait_event", f_wait_event }, 425 + { "set_cursor", f_set_cursor }, 426 + { "set_window_title", f_set_window_title }, 427 + { "set_window_mode", f_set_window_mode }, 428 + { "window_has_focus", f_window_has_focus }, 429 + { "show_confirm_dialog", f_show_confirm_dialog }, 430 + { "chdir", f_chdir }, 431 + { "list_dir", f_list_dir }, 432 + { "absolute_path", f_absolute_path }, 433 + { "get_file_info", f_get_file_info }, 434 + { "get_clipboard", f_get_clipboard }, 435 + { "set_clipboard", f_set_clipboard }, 436 + { "get_time", f_get_time }, 437 + { "sleep", f_sleep }, 438 + { "exec", f_exec }, 439 + { "fuzzy_match", f_fuzzy_match }, 440 + { NULL, NULL } 441 }; 442 443 444 + int luaopen_system(lua_State *L) 445 + { 446 + luaL_newlib(L, lib); 447 + return 1; 448 }
+262 -104
src/lib/stb/stb_truetype.h
··· 1 - // stb_truetype.h - v1.19 - public domain 2 - // authored from 2009-2016 by Sean Barrett / RAD Game Tools 3 // 4 // This library processes TrueType files: 5 // parse files ··· 32 // Daniel Ribeiro Maciel 33 // 34 // Bug/warning reports/fixes: 35 - // "Zer" on mollyrocket Fabian "ryg" Giesen 36 - // Cass Everitt Martins Mozeiko 37 - // stoiko (Haemimont Games) Cap Petschulat 38 - // Brian Hook Omar Cornut 39 - // Walter van Niftrik github:aloucks 40 // David Gow Peter LaValle 41 // David Given Sergey Popov 42 // Ivan-Assen Ivanov Giumo X. Clanjor ··· 44 // Johan Duparc Thomas Fields 45 // Hou Qiming Derek Vinyard 46 // Rob Loach Cort Stratton 47 - // Kenney Phillis Jr. github:oyvindjam 48 - // Brian Costabile github:vassvik 49 - // 50 // VERSION HISTORY 51 // 52 // 1.19 (2018-02-11) GPOS kerning, STBTT_fmod 53 // 1.18 (2018-01-29) add missing function 54 // 1.17 (2017-07-23) make more arguments const; doc fix ··· 75 // 76 // USAGE 77 // 78 - // Include this file in whatever places neeed to refer to it. In ONE C/C++ 79 // file, write: 80 // #define STB_TRUETYPE_IMPLEMENTATION 81 // before the #include of this file. This expands out the actual ··· 206 // 207 // Advancing for the next character: 208 // Call GlyphHMetrics, and compute 'current_point += SF * advance'. 209 - // 210 // 211 // ADVANCED USAGE 212 // ··· 242 // recommend it. 243 // 244 // 245 - // SOURCE STATISTICS (based on v0.6c, 2050 LOC) 246 - // 247 - // Documentation & header file 520 LOC \___ 660 LOC documentation 248 - // Sample code 140 LOC / 249 - // Truetype parsing 620 LOC ---- 620 LOC TrueType 250 - // Software rasterization 240 LOC \ . 251 - // Curve tesselation 120 LOC \__ 550 LOC Bitmap creation 252 - // Bitmap management 100 LOC / 253 - // Baked bitmap interface 70 LOC / 254 - // Font name matching & access 150 LOC ---- 150 255 - // C runtime library abstraction 60 LOC ---- 60 256 - // 257 - // 258 // PERFORMANCE MEASUREMENTS FOR 1.06: 259 // 260 // 32-bit 64-bit ··· 344 } 345 return 0; 346 } 347 - #endif 348 // 349 // Output: 350 // ··· 358 // :@@. M@M 359 // @@@o@@@@ 360 // :M@@V:@@. 361 - // 362 ////////////////////////////////////////////////////////////////////////////// 363 - // 364 // Complete program: print "Hello World!" banner, with bugs 365 // 366 #if 0 ··· 556 // 557 // It's inefficient; you might want to c&p it and optimize it. 558 559 560 561 ////////////////////////////////////////////////////////////////////////////// ··· 641 // To use with PackFontRangesGather etc., you must set it before calls 642 // call to PackFontRangesGatherRects. 643 644 STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, // same data as above 645 int char_index, // character to display 646 float *xpos, float *ypos, // pointers to current position in screen pixel space ··· 653 // Calling these functions in sequence is roughly equivalent to calling 654 // stbtt_PackFontRanges(). If you more control over the packing of multiple 655 // fonts, or if you want to pack custom data into a font texture, take a look 656 - // at the source to of stbtt_PackFontRanges() and create a custom version 657 // using these functions, e.g. call GatherRects multiple times, 658 // building up a single array of rects, then call PackRects once, 659 // then call RenderIntoRects repeatedly. This may result in a ··· 669 int height; 670 int stride_in_bytes; 671 int padding; 672 unsigned int h_oversample, v_oversample; 673 unsigned char *pixels; 674 void *nodes; ··· 694 // file will only define one font and it always be at offset 0, so it will 695 // return '0' for index 0, and -1 for all other indices. 696 697 - // The following structure is defined publically so you can declare one on 698 // the stack or as a global or etc, but you should treat it as opaque. 699 struct stbtt_fontinfo 700 { ··· 704 705 int numGlyphs; // number of glyphs, needed for range checking 706 707 - int loca,head,glyf,hhea,hmtx,kern,gpos; // table locations as offset from start of .ttf 708 int index_map; // a cmap mapping for our chosen character encoding 709 int indexToLocFormat; // format needed to map from glyph index to glyph 710 ··· 733 // and you want a speed-up, call this function with the character you're 734 // going to process, then use glyph-based functions instead of the 735 // codepoint-based functions. 736 737 738 ////////////////////////////////////////////////////////////////////////////// ··· 786 STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); 787 // as above, but takes one or more glyph indices for greater efficiency 788 789 790 ////////////////////////////////////////////////////////////////////////////// 791 // ··· 820 // returns # of vertices and fills *vertices with the pointer to them 821 // these are expressed in "unscaled" coordinates 822 // 823 - // The shape is a series of countours. Each one starts with 824 // a STBTT_moveto, then consists of a series of mixed 825 // STBTT_lineto and STBTT_curveto segments. A lineto 826 // draws a line from previous endpoint to its x,y; a curveto ··· 829 830 STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); 831 // frees the data allocated above 832 833 ////////////////////////////////////////////////////////////////////////////// 834 // ··· 916 STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); 917 // These functions compute a discretized SDF field for a single character, suitable for storing 918 // in a single-channel texture, sampling with bilinear filtering, and testing against 919 - // larger than some threshhold to produce scalable fonts. 920 // info -- the font 921 // scale -- controls the size of the resulting SDF bitmap, same as it would be creating a regular bitmap 922 // glyph/codepoint -- the character to generate the SDF for ··· 959 // and computing from that can allow drop-out prevention). 960 // 961 // The algorithm has not been optimized at all, so expect it to be slow 962 - // if computing lots of characters or very large sizes. 963 964 965 ··· 1331 return stbtt__cff_get_index(&cff); 1332 } 1333 1334 static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, int fontstart) 1335 { 1336 stbtt_uint32 cmap, t; ··· 1409 info->numGlyphs = ttUSHORT(data+t+4); 1410 else 1411 info->numGlyphs = 0xffff; 1412 1413 // find a cmap encoding table we understand *now* to avoid searching 1414 // later. (todo: could make this installable) ··· 1716 if (i != 0) 1717 num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); 1718 1719 - // now start the new one 1720 start_off = !(flags & 1); 1721 if (start_off) { 1722 // if we start off with an off-curve point, then when we need to find a point on the curve ··· 1758 } 1759 } 1760 num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); 1761 - } else if (numberOfContours == -1) { 1762 // Compound shapes. 1763 int more = 1; 1764 stbtt_uint8 *comp = data + g + 10; ··· 1769 int comp_num_verts = 0, i; 1770 stbtt_vertex *comp_verts = 0, *tmp = 0; 1771 float mtx[6] = {1,0,0,1,0,0}, m, n; 1772 - 1773 flags = ttSHORT(comp); comp+=2; 1774 gidx = ttSHORT(comp); comp+=2; 1775 ··· 1799 mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; 1800 mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; 1801 } 1802 - 1803 // Find transformation scales. 1804 m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); 1805 n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); ··· 1835 // More components ? 1836 more = flags & (1<<5); 1837 } 1838 - } else if (numberOfContours < 0) { 1839 - // @TODO other compound variations? 1840 - STBTT_assert(0); 1841 } else { 1842 // numberOfCounters == 0, do nothing 1843 } ··· 2266 } 2267 } 2268 2269 static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) 2270 { 2271 stbtt_uint8 *data = info->data + info->kern; ··· 2463 if (valueFormat2 != 0) return 0; 2464 2465 STBTT_assert(coverageIndex < pairSetCount); 2466 2467 needle=glyph2; 2468 r=pairValueCount-1; ··· 2540 2541 if (info->gpos) 2542 xAdvance += stbtt__GetGlyphGPOSInfoAdvance(info, g1, g2); 2543 - 2544 - if (info->kern) 2545 xAdvance += stbtt__GetGlyphKernInfoAdvance(info, g1, g2); 2546 2547 return xAdvance; ··· 2602 STBTT_free(v, info->userdata); 2603 } 2604 2605 ////////////////////////////////////////////////////////////////////////////// 2606 // 2607 // antialiasing software rasterizer ··· 2727 float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); 2728 STBTT_assert(z != NULL); 2729 if (!z) return z; 2730 - 2731 // round dx down to avoid overshooting 2732 if (dxdy < 0) 2733 z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy); ··· 2805 } 2806 } 2807 } 2808 - 2809 e = e->next; 2810 } 2811 } ··· 3160 if (e->y0 != e->y1) { 3161 stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata); 3162 if (z != NULL) { 3163 - STBTT_assert(z->ey >= scan_y_top); 3164 // insert at front 3165 z->next = active; 3166 active = z; ··· 3229 3230 static void stbtt__sort_edges_quicksort(stbtt__edge *p, int n) 3231 { 3232 - /* threshhold for transitioning to insertion sort */ 3233 while (n > 12) { 3234 stbtt__edge t; 3235 int c01,c12,c,m,i,j; ··· 3364 points[n].y = y; 3365 } 3366 3367 - // tesselate until threshhold p is happy... @TODO warped to compensate for non-linear stretching 3368 static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n) 3369 { 3370 // midpoint ··· 3527 { 3528 int ix0,iy0,ix1,iy1; 3529 stbtt__bitmap gbm; 3530 - stbtt_vertex *vertices; 3531 int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); 3532 3533 if (scale_x == 0) scale_x = scale_y; ··· 3550 if (height) *height = gbm.h; 3551 if (xoff ) *xoff = ix0; 3552 if (yoff ) *yoff = iy0; 3553 - 3554 if (gbm.w && gbm.h) { 3555 gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata); 3556 if (gbm.pixels) { ··· 3561 } 3562 STBTT_free(vertices, info->userdata); 3563 return gbm.pixels; 3564 - } 3565 3566 STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) 3567 { ··· 3573 int ix0,iy0; 3574 stbtt_vertex *vertices; 3575 int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); 3576 - stbtt__bitmap gbm; 3577 3578 stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0); 3579 gbm.pixels = output; ··· 3595 STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff) 3596 { 3597 return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); 3598 - } 3599 3600 STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint) 3601 { ··· 3610 STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) 3611 { 3612 return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff); 3613 - } 3614 3615 STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) 3616 { ··· 3735 con->y = 0; 3736 con->bottom_y = 0; 3737 STBTT__NOTUSED(nodes); 3738 - STBTT__NOTUSED(num_nodes); 3739 } 3740 3741 static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects) ··· 3789 spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw; 3790 spc->h_oversample = 1; 3791 spc->v_oversample = 1; 3792 3793 stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes); 3794 ··· 3814 spc->v_oversample = v_oversample; 3815 } 3816 3817 #define STBTT__OVER_MASK (STBTT_MAX_OVERSAMPLE-1) 3818 3819 static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) ··· 3956 STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) 3957 { 3958 int i,j,k; 3959 3960 k=0; 3961 for (i=0; i < num_ranges; ++i) { ··· 3967 int x0,y0,x1,y1; 3968 int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; 3969 int glyph = stbtt_FindGlyphIndex(info, codepoint); 3970 - stbtt_GetGlyphBitmapBoxSubpixel(info,glyph, 3971 - scale * spc->h_oversample, 3972 - scale * spc->v_oversample, 3973 - 0,0, 3974 - &x0,&y0,&x1,&y1); 3975 - rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1); 3976 - rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1); 3977 ++k; 3978 } 3979 } ··· 4007 // rects array must be big enough to accommodate all characters in the given ranges 4008 STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) 4009 { 4010 - int i,j,k, return_value = 1; 4011 4012 // save current values 4013 int old_h_over = spc->h_oversample; ··· 4026 sub_y = stbtt__oversample_shift(spc->v_oversample); 4027 for (j=0; j < ranges[i].num_chars; ++j) { 4028 stbrp_rect *r = &rects[k]; 4029 - if (r->was_packed) { 4030 stbtt_packedchar *bc = &ranges[i].chardata_for_range[j]; 4031 int advance, lsb, x0,y0,x1,y1; 4032 int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; ··· 4072 bc->yoff = (float) y0 * recip_v + sub_y; 4073 bc->xoff2 = (x0 + r->w) * recip_h + sub_x; 4074 bc->yoff2 = (y0 + r->h) * recip_v + sub_y; 4075 } else { 4076 return_value = 0; // if any fail, report failure 4077 } ··· 4110 n = 0; 4111 for (i=0; i < num_ranges; ++i) 4112 n += ranges[i].num_chars; 4113 - 4114 rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context); 4115 if (rects == NULL) 4116 return 0; ··· 4121 n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects); 4122 4123 stbtt_PackFontRangesPackRects(spc, rects, n); 4124 - 4125 return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects); 4126 4127 STBTT_free(rects, spc->user_allocator_context); ··· 4138 range.chardata_for_range = chardata_for_range; 4139 range.font_size = font_size; 4140 return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1); 4141 } 4142 4143 STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer) ··· 4269 int x1 = (int) verts[i ].x, y1 = (int) verts[i ].y; 4270 if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { 4271 float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; 4272 - if (x_inter < x) 4273 winding += (y0 < y1) ? 1 : -1; 4274 } 4275 } ··· 4295 y1 = (int)verts[i ].y; 4296 if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { 4297 float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; 4298 - if (x_inter < x) 4299 winding += (y0 < y1) ? 1 : -1; 4300 } 4301 } else { ··· 4307 if (hits[1][0] < 0) 4308 winding += (hits[1][1] < 0 ? -1 : 1); 4309 } 4310 - } 4311 } 4312 } 4313 return winding; ··· 4360 int w,h; 4361 unsigned char *data; 4362 4363 - // if one scale is 0, use same scale for both 4364 - if (scale_x == 0) scale_x = scale_y; 4365 - if (scale_y == 0) { 4366 - if (scale_x == 0) return NULL; // if both scales are 0, return NULL 4367 - scale_y = scale_x; 4368 - } 4369 4370 stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale, scale, 0.0f,0.0f, &ix0,&iy0,&ix1,&iy1); 4371 ··· 4388 4389 // invert for y-downwards bitmaps 4390 scale_y = -scale_y; 4391 - 4392 { 4393 int x,y,i,j; 4394 float *precompute; ··· 4537 STBTT_free(verts, info->userdata); 4538 } 4539 return data; 4540 - } 4541 4542 STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) 4543 { ··· 4555 // 4556 4557 // check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string 4558 - static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2) 4559 { 4560 stbtt_int32 i=0; 4561 ··· 4594 return i; 4595 } 4596 4597 - static int stbtt_CompareUTF8toUTF16_bigendian_internal(char *s1, int len1, char *s2, int len2) 4598 { 4599 return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2); 4600 } ··· 4723 4724 STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index) 4725 { 4726 - return stbtt_GetFontOffsetForIndex_internal((unsigned char *) data, index); 4727 } 4728 4729 STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data) ··· 4816 ------------------------------------------------------------------------------ 4817 ALTERNATIVE A - MIT License 4818 Copyright (c) 2017 Sean Barrett 4819 - Permission is hereby granted, free of charge, to any person obtaining a copy of 4820 - this software and associated documentation files (the "Software"), to deal in 4821 - the Software without restriction, including without limitation the rights to 4822 - use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 4823 - of the Software, and to permit persons to whom the Software is furnished to do 4824 so, subject to the following conditions: 4825 - The above copyright notice and this permission notice shall be included in all 4826 copies or substantial portions of the Software. 4827 - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4828 - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4829 - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 4830 - AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 4831 - LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 4832 - OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 4833 SOFTWARE. 4834 ------------------------------------------------------------------------------ 4835 ALTERNATIVE B - Public Domain (www.unlicense.org) 4836 This is free and unencumbered software released into the public domain. 4837 - Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 4838 - software, either in source code form or as a compiled binary, for any purpose, 4839 commercial or non-commercial, and by any means. 4840 - In jurisdictions that recognize copyright laws, the author or authors of this 4841 - software dedicate any and all copyright interest in the software to the public 4842 - domain. We make this dedication for the benefit of the public at large and to 4843 - the detriment of our heirs and successors. We intend this dedication to be an 4844 - overt act of relinquishment in perpetuity of all present and future rights to 4845 this software under copyright law. 4846 - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4847 - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4848 - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 4849 - AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 4850 - ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 4851 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 4852 ------------------------------------------------------------------------------ 4853 */
··· 1 + // stb_truetype.h - v1.24 - public domain 2 + // authored from 2009-2020 by Sean Barrett / RAD Game Tools 3 + // 4 + // ======================================================================= 5 + // 6 + // NO SECURITY GUARANTEE -- DO NOT USE THIS ON UNTRUSTED FONT FILES 7 + // 8 + // This library does no range checking of the offsets found in the file, 9 + // meaning an attacker can use it to read arbitrary memory. 10 + // 11 + // ======================================================================= 12 // 13 // This library processes TrueType files: 14 // parse files ··· 41 // Daniel Ribeiro Maciel 42 // 43 // Bug/warning reports/fixes: 44 + // "Zer" on mollyrocket Fabian "ryg" Giesen github:NiLuJe 45 + // Cass Everitt Martins Mozeiko github:aloucks 46 + // stoiko (Haemimont Games) Cap Petschulat github:oyvindjam 47 + // Brian Hook Omar Cornut github:vassvik 48 + // Walter van Niftrik Ryan Griege 49 // David Gow Peter LaValle 50 // David Given Sergey Popov 51 // Ivan-Assen Ivanov Giumo X. Clanjor ··· 53 // Johan Duparc Thomas Fields 54 // Hou Qiming Derek Vinyard 55 // Rob Loach Cort Stratton 56 + // Kenney Phillis Jr. Brian Costabile 57 + // Ken Voskuil (kaesve) 58 + // 59 // VERSION HISTORY 60 // 61 + // 1.24 (2020-02-05) fix warning 62 + // 1.23 (2020-02-02) query SVG data for glyphs; query whole kerning table (but only kern not GPOS) 63 + // 1.22 (2019-08-11) minimize missing-glyph duplication; fix kerning if both 'GPOS' and 'kern' are defined 64 + // 1.21 (2019-02-25) fix warning 65 + // 1.20 (2019-02-07) PackFontRange skips missing codepoints; GetScaleFontVMetrics() 66 // 1.19 (2018-02-11) GPOS kerning, STBTT_fmod 67 // 1.18 (2018-01-29) add missing function 68 // 1.17 (2017-07-23) make more arguments const; doc fix ··· 89 // 90 // USAGE 91 // 92 + // Include this file in whatever places need to refer to it. In ONE C/C++ 93 // file, write: 94 // #define STB_TRUETYPE_IMPLEMENTATION 95 // before the #include of this file. This expands out the actual ··· 220 // 221 // Advancing for the next character: 222 // Call GlyphHMetrics, and compute 'current_point += SF * advance'. 223 + // 224 // 225 // ADVANCED USAGE 226 // ··· 256 // recommend it. 257 // 258 // 259 // PERFORMANCE MEASUREMENTS FOR 1.06: 260 // 261 // 32-bit 64-bit ··· 345 } 346 return 0; 347 } 348 + #endif 349 // 350 // Output: 351 // ··· 359 // :@@. M@M 360 // @@@o@@@@ 361 // :M@@V:@@. 362 + // 363 ////////////////////////////////////////////////////////////////////////////// 364 + // 365 // Complete program: print "Hello World!" banner, with bugs 366 // 367 #if 0 ··· 557 // 558 // It's inefficient; you might want to c&p it and optimize it. 559 560 + STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap); 561 + // Query the font vertical metrics without having to create a font first. 562 563 564 ////////////////////////////////////////////////////////////////////////////// ··· 644 // To use with PackFontRangesGather etc., you must set it before calls 645 // call to PackFontRangesGatherRects. 646 647 + STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip); 648 + // If skip != 0, this tells stb_truetype to skip any codepoints for which 649 + // there is no corresponding glyph. If skip=0, which is the default, then 650 + // codepoints without a glyph recived the font's "missing character" glyph, 651 + // typically an empty box by convention. 652 + 653 STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, // same data as above 654 int char_index, // character to display 655 float *xpos, float *ypos, // pointers to current position in screen pixel space ··· 662 // Calling these functions in sequence is roughly equivalent to calling 663 // stbtt_PackFontRanges(). If you more control over the packing of multiple 664 // fonts, or if you want to pack custom data into a font texture, take a look 665 + // at the source to of stbtt_PackFontRanges() and create a custom version 666 // using these functions, e.g. call GatherRects multiple times, 667 // building up a single array of rects, then call PackRects once, 668 // then call RenderIntoRects repeatedly. This may result in a ··· 678 int height; 679 int stride_in_bytes; 680 int padding; 681 + int skip_missing; 682 unsigned int h_oversample, v_oversample; 683 unsigned char *pixels; 684 void *nodes; ··· 704 // file will only define one font and it always be at offset 0, so it will 705 // return '0' for index 0, and -1 for all other indices. 706 707 + // The following structure is defined publicly so you can declare one on 708 // the stack or as a global or etc, but you should treat it as opaque. 709 struct stbtt_fontinfo 710 { ··· 714 715 int numGlyphs; // number of glyphs, needed for range checking 716 717 + int loca,head,glyf,hhea,hmtx,kern,gpos,svg; // table locations as offset from start of .ttf 718 int index_map; // a cmap mapping for our chosen character encoding 719 int indexToLocFormat; // format needed to map from glyph index to glyph 720 ··· 743 // and you want a speed-up, call this function with the character you're 744 // going to process, then use glyph-based functions instead of the 745 // codepoint-based functions. 746 + // Returns 0 if the character codepoint is not defined in the font. 747 748 749 ////////////////////////////////////////////////////////////////////////////// ··· 797 STBTT_DEF int stbtt_GetGlyphBox(const stbtt_fontinfo *info, int glyph_index, int *x0, int *y0, int *x1, int *y1); 798 // as above, but takes one or more glyph indices for greater efficiency 799 800 + typedef struct stbtt_kerningentry 801 + { 802 + int glyph1; // use stbtt_FindGlyphIndex 803 + int glyph2; 804 + int advance; 805 + } stbtt_kerningentry; 806 + 807 + STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info); 808 + STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length); 809 + // Retrieves a complete list of all of the kerning pairs provided by the font 810 + // stbtt_GetKerningTable never writes more than table_length entries and returns how many entries it did write. 811 + // The table will be sorted by (a.glyph1 == b.glyph1)?(a.glyph2 < b.glyph2):(a.glyph1 < b.glyph1) 812 813 ////////////////////////////////////////////////////////////////////////////// 814 // ··· 843 // returns # of vertices and fills *vertices with the pointer to them 844 // these are expressed in "unscaled" coordinates 845 // 846 + // The shape is a series of contours. Each one starts with 847 // a STBTT_moveto, then consists of a series of mixed 848 // STBTT_lineto and STBTT_curveto segments. A lineto 849 // draws a line from previous endpoint to its x,y; a curveto ··· 852 853 STBTT_DEF void stbtt_FreeShape(const stbtt_fontinfo *info, stbtt_vertex *vertices); 854 // frees the data allocated above 855 + 856 + STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg); 857 + STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg); 858 + // fills svg with the character's SVG data. 859 + // returns data size or 0 if SVG not found. 860 861 ////////////////////////////////////////////////////////////////////////////// 862 // ··· 944 STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff); 945 // These functions compute a discretized SDF field for a single character, suitable for storing 946 // in a single-channel texture, sampling with bilinear filtering, and testing against 947 + // larger than some threshold to produce scalable fonts. 948 // info -- the font 949 // scale -- controls the size of the resulting SDF bitmap, same as it would be creating a regular bitmap 950 // glyph/codepoint -- the character to generate the SDF for ··· 987 // and computing from that can allow drop-out prevention). 988 // 989 // The algorithm has not been optimized at all, so expect it to be slow 990 + // if computing lots of characters or very large sizes. 991 992 993 ··· 1359 return stbtt__cff_get_index(&cff); 1360 } 1361 1362 + // since most people won't use this, find this table the first time it's needed 1363 + static int stbtt__get_svg(stbtt_fontinfo *info) 1364 + { 1365 + stbtt_uint32 t; 1366 + if (info->svg < 0) { 1367 + t = stbtt__find_table(info->data, info->fontstart, "SVG "); 1368 + if (t) { 1369 + stbtt_uint32 offset = ttULONG(info->data + t + 2); 1370 + info->svg = t + offset; 1371 + } else { 1372 + info->svg = 0; 1373 + } 1374 + } 1375 + return info->svg; 1376 + } 1377 + 1378 static int stbtt_InitFont_internal(stbtt_fontinfo *info, unsigned char *data, int fontstart) 1379 { 1380 stbtt_uint32 cmap, t; ··· 1453 info->numGlyphs = ttUSHORT(data+t+4); 1454 else 1455 info->numGlyphs = 0xffff; 1456 + 1457 + info->svg = -1; 1458 1459 // find a cmap encoding table we understand *now* to avoid searching 1460 // later. (todo: could make this installable) ··· 1762 if (i != 0) 1763 num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); 1764 1765 + // now start the new one 1766 start_off = !(flags & 1); 1767 if (start_off) { 1768 // if we start off with an off-curve point, then when we need to find a point on the curve ··· 1804 } 1805 } 1806 num_vertices = stbtt__close_shape(vertices, num_vertices, was_off, start_off, sx,sy,scx,scy,cx,cy); 1807 + } else if (numberOfContours < 0) { 1808 // Compound shapes. 1809 int more = 1; 1810 stbtt_uint8 *comp = data + g + 10; ··· 1815 int comp_num_verts = 0, i; 1816 stbtt_vertex *comp_verts = 0, *tmp = 0; 1817 float mtx[6] = {1,0,0,1,0,0}, m, n; 1818 + 1819 flags = ttSHORT(comp); comp+=2; 1820 gidx = ttSHORT(comp); comp+=2; 1821 ··· 1845 mtx[2] = ttSHORT(comp)/16384.0f; comp+=2; 1846 mtx[3] = ttSHORT(comp)/16384.0f; comp+=2; 1847 } 1848 + 1849 // Find transformation scales. 1850 m = (float) STBTT_sqrt(mtx[0]*mtx[0] + mtx[1]*mtx[1]); 1851 n = (float) STBTT_sqrt(mtx[2]*mtx[2] + mtx[3]*mtx[3]); ··· 1881 // More components ? 1882 more = flags & (1<<5); 1883 } 1884 } else { 1885 // numberOfCounters == 0, do nothing 1886 } ··· 2309 } 2310 } 2311 2312 + STBTT_DEF int stbtt_GetKerningTableLength(const stbtt_fontinfo *info) 2313 + { 2314 + stbtt_uint8 *data = info->data + info->kern; 2315 + 2316 + // we only look at the first table. it must be 'horizontal' and format 0. 2317 + if (!info->kern) 2318 + return 0; 2319 + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 2320 + return 0; 2321 + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format 2322 + return 0; 2323 + 2324 + return ttUSHORT(data+10); 2325 + } 2326 + 2327 + STBTT_DEF int stbtt_GetKerningTable(const stbtt_fontinfo *info, stbtt_kerningentry* table, int table_length) 2328 + { 2329 + stbtt_uint8 *data = info->data + info->kern; 2330 + int k, length; 2331 + 2332 + // we only look at the first table. it must be 'horizontal' and format 0. 2333 + if (!info->kern) 2334 + return 0; 2335 + if (ttUSHORT(data+2) < 1) // number of tables, need at least 1 2336 + return 0; 2337 + if (ttUSHORT(data+8) != 1) // horizontal flag must be set in format 2338 + return 0; 2339 + 2340 + length = ttUSHORT(data+10); 2341 + if (table_length < length) 2342 + length = table_length; 2343 + 2344 + for (k = 0; k < length; k++) 2345 + { 2346 + table[k].glyph1 = ttUSHORT(data+18+(k*6)); 2347 + table[k].glyph2 = ttUSHORT(data+20+(k*6)); 2348 + table[k].advance = ttSHORT(data+22+(k*6)); 2349 + } 2350 + 2351 + return length; 2352 + } 2353 + 2354 static int stbtt__GetGlyphKernInfoAdvance(const stbtt_fontinfo *info, int glyph1, int glyph2) 2355 { 2356 stbtt_uint8 *data = info->data + info->kern; ··· 2548 if (valueFormat2 != 0) return 0; 2549 2550 STBTT_assert(coverageIndex < pairSetCount); 2551 + STBTT__NOTUSED(pairSetCount); 2552 2553 needle=glyph2; 2554 r=pairValueCount-1; ··· 2626 2627 if (info->gpos) 2628 xAdvance += stbtt__GetGlyphGPOSInfoAdvance(info, g1, g2); 2629 + else if (info->kern) 2630 xAdvance += stbtt__GetGlyphKernInfoAdvance(info, g1, g2); 2631 2632 return xAdvance; ··· 2687 STBTT_free(v, info->userdata); 2688 } 2689 2690 + STBTT_DEF stbtt_uint8 *stbtt_FindSVGDoc(const stbtt_fontinfo *info, int gl) 2691 + { 2692 + int i; 2693 + stbtt_uint8 *data = info->data; 2694 + stbtt_uint8 *svg_doc_list = data + stbtt__get_svg((stbtt_fontinfo *) info); 2695 + 2696 + int numEntries = ttUSHORT(svg_doc_list); 2697 + stbtt_uint8 *svg_docs = svg_doc_list + 2; 2698 + 2699 + for(i=0; i<numEntries; i++) { 2700 + stbtt_uint8 *svg_doc = svg_docs + (12 * i); 2701 + if ((gl >= ttUSHORT(svg_doc)) && (gl <= ttUSHORT(svg_doc + 2))) 2702 + return svg_doc; 2703 + } 2704 + return 0; 2705 + } 2706 + 2707 + STBTT_DEF int stbtt_GetGlyphSVG(const stbtt_fontinfo *info, int gl, const char **svg) 2708 + { 2709 + stbtt_uint8 *data = info->data; 2710 + stbtt_uint8 *svg_doc; 2711 + 2712 + if (info->svg == 0) 2713 + return 0; 2714 + 2715 + svg_doc = stbtt_FindSVGDoc(info, gl); 2716 + if (svg_doc != NULL) { 2717 + *svg = (char *) data + info->svg + ttULONG(svg_doc + 4); 2718 + return ttULONG(svg_doc + 8); 2719 + } else { 2720 + return 0; 2721 + } 2722 + } 2723 + 2724 + STBTT_DEF int stbtt_GetCodepointSVG(const stbtt_fontinfo *info, int unicode_codepoint, const char **svg) 2725 + { 2726 + return stbtt_GetGlyphSVG(info, stbtt_FindGlyphIndex(info, unicode_codepoint), svg); 2727 + } 2728 + 2729 ////////////////////////////////////////////////////////////////////////////// 2730 // 2731 // antialiasing software rasterizer ··· 2851 float dxdy = (e->x1 - e->x0) / (e->y1 - e->y0); 2852 STBTT_assert(z != NULL); 2853 if (!z) return z; 2854 + 2855 // round dx down to avoid overshooting 2856 if (dxdy < 0) 2857 z->dx = -STBTT_ifloor(STBTT_FIX * -dxdy); ··· 2929 } 2930 } 2931 } 2932 + 2933 e = e->next; 2934 } 2935 } ··· 3284 if (e->y0 != e->y1) { 3285 stbtt__active_edge *z = stbtt__new_active(&hh, e, off_x, scan_y_top, userdata); 3286 if (z != NULL) { 3287 + if (j == 0 && off_y != 0) { 3288 + if (z->ey < scan_y_top) { 3289 + // this can happen due to subpixel positioning and some kind of fp rounding error i think 3290 + z->ey = scan_y_top; 3291 + } 3292 + } 3293 + STBTT_assert(z->ey >= scan_y_top); // if we get really unlucky a tiny bit of an edge can be out of bounds 3294 // insert at front 3295 z->next = active; 3296 active = z; ··· 3359 3360 static void stbtt__sort_edges_quicksort(stbtt__edge *p, int n) 3361 { 3362 + /* threshold for transitioning to insertion sort */ 3363 while (n > 12) { 3364 stbtt__edge t; 3365 int c01,c12,c,m,i,j; ··· 3494 points[n].y = y; 3495 } 3496 3497 + // tessellate until threshold p is happy... @TODO warped to compensate for non-linear stretching 3498 static int stbtt__tesselate_curve(stbtt__point *points, int *num_points, float x0, float y0, float x1, float y1, float x2, float y2, float objspace_flatness_squared, int n) 3499 { 3500 // midpoint ··· 3657 { 3658 int ix0,iy0,ix1,iy1; 3659 stbtt__bitmap gbm; 3660 + stbtt_vertex *vertices; 3661 int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); 3662 3663 if (scale_x == 0) scale_x = scale_y; ··· 3680 if (height) *height = gbm.h; 3681 if (xoff ) *xoff = ix0; 3682 if (yoff ) *yoff = iy0; 3683 + 3684 if (gbm.w && gbm.h) { 3685 gbm.pixels = (unsigned char *) STBTT_malloc(gbm.w * gbm.h, info->userdata); 3686 if (gbm.pixels) { ··· 3691 } 3692 STBTT_free(vertices, info->userdata); 3693 return gbm.pixels; 3694 + } 3695 3696 STBTT_DEF unsigned char *stbtt_GetGlyphBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int glyph, int *width, int *height, int *xoff, int *yoff) 3697 { ··· 3703 int ix0,iy0; 3704 stbtt_vertex *vertices; 3705 int num_verts = stbtt_GetGlyphShape(info, glyph, &vertices); 3706 + stbtt__bitmap gbm; 3707 3708 stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale_x, scale_y, shift_x, shift_y, &ix0,&iy0,0,0); 3709 gbm.pixels = output; ··· 3725 STBTT_DEF unsigned char *stbtt_GetCodepointBitmapSubpixel(const stbtt_fontinfo *info, float scale_x, float scale_y, float shift_x, float shift_y, int codepoint, int *width, int *height, int *xoff, int *yoff) 3726 { 3727 return stbtt_GetGlyphBitmapSubpixel(info, scale_x, scale_y,shift_x,shift_y, stbtt_FindGlyphIndex(info,codepoint), width,height,xoff,yoff); 3728 + } 3729 3730 STBTT_DEF void stbtt_MakeCodepointBitmapSubpixelPrefilter(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, float shift_x, float shift_y, int oversample_x, int oversample_y, float *sub_x, float *sub_y, int codepoint) 3731 { ··· 3740 STBTT_DEF unsigned char *stbtt_GetCodepointBitmap(const stbtt_fontinfo *info, float scale_x, float scale_y, int codepoint, int *width, int *height, int *xoff, int *yoff) 3741 { 3742 return stbtt_GetCodepointBitmapSubpixel(info, scale_x, scale_y, 0.0f,0.0f, codepoint, width,height,xoff,yoff); 3743 + } 3744 3745 STBTT_DEF void stbtt_MakeCodepointBitmap(const stbtt_fontinfo *info, unsigned char *output, int out_w, int out_h, int out_stride, float scale_x, float scale_y, int codepoint) 3746 { ··· 3865 con->y = 0; 3866 con->bottom_y = 0; 3867 STBTT__NOTUSED(nodes); 3868 + STBTT__NOTUSED(num_nodes); 3869 } 3870 3871 static void stbrp_pack_rects(stbrp_context *con, stbrp_rect *rects, int num_rects) ··· 3919 spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw; 3920 spc->h_oversample = 1; 3921 spc->v_oversample = 1; 3922 + spc->skip_missing = 0; 3923 3924 stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes); 3925 ··· 3945 spc->v_oversample = v_oversample; 3946 } 3947 3948 + STBTT_DEF void stbtt_PackSetSkipMissingCodepoints(stbtt_pack_context *spc, int skip) 3949 + { 3950 + spc->skip_missing = skip; 3951 + } 3952 + 3953 #define STBTT__OVER_MASK (STBTT_MAX_OVERSAMPLE-1) 3954 3955 static void stbtt__h_prefilter(unsigned char *pixels, int w, int h, int stride_in_bytes, unsigned int kernel_width) ··· 4092 STBTT_DEF int stbtt_PackFontRangesGatherRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) 4093 { 4094 int i,j,k; 4095 + int missing_glyph_added = 0; 4096 4097 k=0; 4098 for (i=0; i < num_ranges; ++i) { ··· 4104 int x0,y0,x1,y1; 4105 int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; 4106 int glyph = stbtt_FindGlyphIndex(info, codepoint); 4107 + if (glyph == 0 && (spc->skip_missing || missing_glyph_added)) { 4108 + rects[k].w = rects[k].h = 0; 4109 + } else { 4110 + stbtt_GetGlyphBitmapBoxSubpixel(info,glyph, 4111 + scale * spc->h_oversample, 4112 + scale * spc->v_oversample, 4113 + 0,0, 4114 + &x0,&y0,&x1,&y1); 4115 + rects[k].w = (stbrp_coord) (x1-x0 + spc->padding + spc->h_oversample-1); 4116 + rects[k].h = (stbrp_coord) (y1-y0 + spc->padding + spc->v_oversample-1); 4117 + if (glyph == 0) 4118 + missing_glyph_added = 1; 4119 + } 4120 ++k; 4121 } 4122 } ··· 4150 // rects array must be big enough to accommodate all characters in the given ranges 4151 STBTT_DEF int stbtt_PackFontRangesRenderIntoRects(stbtt_pack_context *spc, const stbtt_fontinfo *info, stbtt_pack_range *ranges, int num_ranges, stbrp_rect *rects) 4152 { 4153 + int i,j,k, missing_glyph = -1, return_value = 1; 4154 4155 // save current values 4156 int old_h_over = spc->h_oversample; ··· 4169 sub_y = stbtt__oversample_shift(spc->v_oversample); 4170 for (j=0; j < ranges[i].num_chars; ++j) { 4171 stbrp_rect *r = &rects[k]; 4172 + if (r->was_packed && r->w != 0 && r->h != 0) { 4173 stbtt_packedchar *bc = &ranges[i].chardata_for_range[j]; 4174 int advance, lsb, x0,y0,x1,y1; 4175 int codepoint = ranges[i].array_of_unicode_codepoints == NULL ? ranges[i].first_unicode_codepoint_in_range + j : ranges[i].array_of_unicode_codepoints[j]; ··· 4215 bc->yoff = (float) y0 * recip_v + sub_y; 4216 bc->xoff2 = (x0 + r->w) * recip_h + sub_x; 4217 bc->yoff2 = (y0 + r->h) * recip_v + sub_y; 4218 + 4219 + if (glyph == 0) 4220 + missing_glyph = j; 4221 + } else if (spc->skip_missing) { 4222 + return_value = 0; 4223 + } else if (r->was_packed && r->w == 0 && r->h == 0 && missing_glyph >= 0) { 4224 + ranges[i].chardata_for_range[j] = ranges[i].chardata_for_range[missing_glyph]; 4225 } else { 4226 return_value = 0; // if any fail, report failure 4227 } ··· 4260 n = 0; 4261 for (i=0; i < num_ranges; ++i) 4262 n += ranges[i].num_chars; 4263 + 4264 rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context); 4265 if (rects == NULL) 4266 return 0; ··· 4271 n = stbtt_PackFontRangesGatherRects(spc, &info, ranges, num_ranges, rects); 4272 4273 stbtt_PackFontRangesPackRects(spc, rects, n); 4274 + 4275 return_value = stbtt_PackFontRangesRenderIntoRects(spc, &info, ranges, num_ranges, rects); 4276 4277 STBTT_free(rects, spc->user_allocator_context); ··· 4288 range.chardata_for_range = chardata_for_range; 4289 range.font_size = font_size; 4290 return stbtt_PackFontRanges(spc, fontdata, font_index, &range, 1); 4291 + } 4292 + 4293 + STBTT_DEF void stbtt_GetScaledFontVMetrics(const unsigned char *fontdata, int index, float size, float *ascent, float *descent, float *lineGap) 4294 + { 4295 + int i_ascent, i_descent, i_lineGap; 4296 + float scale; 4297 + stbtt_fontinfo info; 4298 + stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata, index)); 4299 + scale = size > 0 ? stbtt_ScaleForPixelHeight(&info, size) : stbtt_ScaleForMappingEmToPixels(&info, -size); 4300 + stbtt_GetFontVMetrics(&info, &i_ascent, &i_descent, &i_lineGap); 4301 + *ascent = (float) i_ascent * scale; 4302 + *descent = (float) i_descent * scale; 4303 + *lineGap = (float) i_lineGap * scale; 4304 } 4305 4306 STBTT_DEF void stbtt_GetPackedQuad(const stbtt_packedchar *chardata, int pw, int ph, int char_index, float *xpos, float *ypos, stbtt_aligned_quad *q, int align_to_integer) ··· 4432 int x1 = (int) verts[i ].x, y1 = (int) verts[i ].y; 4433 if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { 4434 float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; 4435 + if (x_inter < x) 4436 winding += (y0 < y1) ? 1 : -1; 4437 } 4438 } ··· 4458 y1 = (int)verts[i ].y; 4459 if (y > STBTT_min(y0,y1) && y < STBTT_max(y0,y1) && x > STBTT_min(x0,x1)) { 4460 float x_inter = (y - y0) / (y1 - y0) * (x1-x0) + x0; 4461 + if (x_inter < x) 4462 winding += (y0 < y1) ? 1 : -1; 4463 } 4464 } else { ··· 4470 if (hits[1][0] < 0) 4471 winding += (hits[1][1] < 0 ? -1 : 1); 4472 } 4473 + } 4474 } 4475 } 4476 return winding; ··· 4523 int w,h; 4524 unsigned char *data; 4525 4526 + if (scale == 0) return NULL; 4527 4528 stbtt_GetGlyphBitmapBoxSubpixel(info, glyph, scale, scale, 0.0f,0.0f, &ix0,&iy0,&ix1,&iy1); 4529 ··· 4546 4547 // invert for y-downwards bitmaps 4548 scale_y = -scale_y; 4549 + 4550 { 4551 int x,y,i,j; 4552 float *precompute; ··· 4695 STBTT_free(verts, info->userdata); 4696 } 4697 return data; 4698 + } 4699 4700 STBTT_DEF unsigned char * stbtt_GetCodepointSDF(const stbtt_fontinfo *info, float scale, int codepoint, int padding, unsigned char onedge_value, float pixel_dist_scale, int *width, int *height, int *xoff, int *yoff) 4701 { ··· 4713 // 4714 4715 // check if a utf8 string contains a prefix which is the utf16 string; if so return length of matching utf8 string 4716 + static stbtt_int32 stbtt__CompareUTF8toUTF16_bigendian_prefix(stbtt_uint8 *s1, stbtt_int32 len1, stbtt_uint8 *s2, stbtt_int32 len2) 4717 { 4718 stbtt_int32 i=0; 4719 ··· 4752 return i; 4753 } 4754 4755 + static int stbtt_CompareUTF8toUTF16_bigendian_internal(char *s1, int len1, char *s2, int len2) 4756 { 4757 return len1 == stbtt__CompareUTF8toUTF16_bigendian_prefix((stbtt_uint8*) s1, len1, (stbtt_uint8*) s2, len2); 4758 } ··· 4881 4882 STBTT_DEF int stbtt_GetFontOffsetForIndex(const unsigned char *data, int index) 4883 { 4884 + return stbtt_GetFontOffsetForIndex_internal((unsigned char *) data, index); 4885 } 4886 4887 STBTT_DEF int stbtt_GetNumberOfFonts(const unsigned char *data) ··· 4974 ------------------------------------------------------------------------------ 4975 ALTERNATIVE A - MIT License 4976 Copyright (c) 2017 Sean Barrett 4977 + Permission is hereby granted, free of charge, to any person obtaining a copy of 4978 + this software and associated documentation files (the "Software"), to deal in 4979 + the Software without restriction, including without limitation the rights to 4980 + use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 4981 + of the Software, and to permit persons to whom the Software is furnished to do 4982 so, subject to the following conditions: 4983 + The above copyright notice and this permission notice shall be included in all 4984 copies or substantial portions of the Software. 4985 + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 4986 + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 4987 + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 4988 + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 4989 + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 4990 + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 4991 SOFTWARE. 4992 ------------------------------------------------------------------------------ 4993 ALTERNATIVE B - Public Domain (www.unlicense.org) 4994 This is free and unencumbered software released into the public domain. 4995 + Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 4996 + software, either in source code form or as a compiled binary, for any purpose, 4997 commercial or non-commercial, and by any means. 4998 + In jurisdictions that recognize copyright laws, the author or authors of this 4999 + software dedicate any and all copyright interest in the software to the public 5000 + domain. We make this dedication for the benefit of the public at large and to 5001 + the detriment of our heirs and successors. We intend this dedication to be an 5002 + overt act of relinquishment in perpetuity of all present and future rights to 5003 this software under copyright law. 5004 + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 5005 + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 5006 + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 5007 + AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 5008 + ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 5009 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 5010 ------------------------------------------------------------------------------ 5011 */
+103 -108
src/main.c
··· 1 #include <stdio.h> 2 - #include <SDL2/SDL.h> 3 #include "api/api.h" 4 #include "renderer.h" 5 6 #ifdef _WIN32 7 - #include <windows.h> 8 #elif __linux__ 9 - #include <unistd.h> 10 #elif __APPLE__ 11 - #include <mach-o/dyld.h> 12 #endif 13 14 15 SDL_Window *window; 16 17 18 - static double get_scale(void) { 19 - float dpi; 20 - SDL_GetDisplayDPI(0, NULL, &dpi, NULL); 21 - #if _WIN32 22 - return dpi / 96.0; 23 - #else 24 - return 1.0; 25 - #endif 26 } 27 28 - 29 - static void get_exe_filename(char *buf, int sz) { 30 - #if _WIN32 31 - int len = GetModuleFileName(NULL, buf, sz - 1); 32 - buf[len] = '\0'; 33 - #elif __linux__ 34 - char path[512]; 35 - sprintf(path, "/proc/%d/exe", getpid()); 36 - int len = readlink(path, buf, sz - 1); 37 - buf[len] = '\0'; 38 - #elif __APPLE__ 39 - unsigned size = sz; 40 - _NSGetExecutablePath(buf, &size); 41 - #else 42 - strcpy(buf, "./lite"); 43 - #endif 44 } 45 46 47 - static void init_window_icon(void) { 48 - #ifndef _WIN32 49 - #include "../icon.inl" 50 - (void) icon_rgba_len; /* unused */ 51 - SDL_Surface *surf = SDL_CreateRGBSurfaceFrom( 52 - icon_rgba, 64, 64, 53 - 32, 64 * 4, 54 - 0x000000ff, 55 - 0x0000ff00, 56 - 0x00ff0000, 57 - 0xff000000); 58 - SDL_SetWindowIcon(window, surf); 59 - SDL_FreeSurface(surf); 60 - #endif 61 } 62 63 64 - int main(int argc, char **argv) { 65 - #ifdef _WIN32 66 - HINSTANCE lib = LoadLibrary("user32.dll"); 67 - int (*SetProcessDPIAware)() = (void*) GetProcAddress(lib, "SetProcessDPIAware"); 68 - SetProcessDPIAware(); 69 - #endif 70 71 - SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS); 72 - SDL_EnableScreenSaver(); 73 - SDL_EventState(SDL_DROPFILE, SDL_ENABLE); 74 - atexit(SDL_Quit); 75 76 - #ifdef SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR /* Available since 2.0.8 */ 77 - SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0"); 78 - #endif 79 - #if SDL_VERSION_ATLEAST(2, 0, 5) 80 - SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1"); 81 - #endif 82 83 - SDL_DisplayMode dm; 84 - SDL_GetCurrentDisplayMode(0, &dm); 85 86 - window = SDL_CreateWindow( 87 - "", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, dm.w * 0.8, dm.h * 0.8, 88 - SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN); 89 - init_window_icon(); 90 - ren_init(window); 91 92 93 - lua_State *L = luaL_newstate(); 94 - luaL_openlibs(L); 95 - api_load_libs(L); 96 97 98 - lua_newtable(L); 99 - for (int i = 0; i < argc; i++) { 100 - lua_pushstring(L, argv[i]); 101 - lua_rawseti(L, -2, i + 1); 102 - } 103 - lua_setglobal(L, "ARGS"); 104 105 - lua_pushstring(L, "1.11"); 106 - lua_setglobal(L, "VERSION"); 107 108 - lua_pushstring(L, SDL_GetPlatform()); 109 - lua_setglobal(L, "PLATFORM"); 110 111 - lua_pushnumber(L, get_scale()); 112 - lua_setglobal(L, "SCALE"); 113 114 - char exename[2048]; 115 - get_exe_filename(exename, sizeof(exename)); 116 - lua_pushstring(L, exename); 117 - lua_setglobal(L, "EXEFILE"); 118 - 119 - 120 - (void) luaL_dostring(L, 121 - "local core\n" 122 - "xpcall(function()\n" 123 - " SCALE = tonumber(os.getenv(\"LITE_SCALE\")) or SCALE\n" 124 - " PATHSEP = package.config:sub(1, 1)\n" 125 - " EXEDIR = EXEFILE:match(\"^(.+)[/\\\\].*$\")\n" 126 - " package.path = EXEDIR .. '/data/?.lua;' .. package.path\n" 127 - " package.path = EXEDIR .. '/data/?/init.lua;' .. package.path\n" 128 - " core = require('core')\n" 129 - " core.init()\n" 130 - " core.run()\n" 131 - "end, function(err)\n" 132 - " print('Error: ' .. tostring(err))\n" 133 - " print(debug.traceback(nil, 2))\n" 134 - " if core and core.on_error then\n" 135 - " pcall(core.on_error, err)\n" 136 - " end\n" 137 - " os.exit(1)\n" 138 - "end)"); 139 140 141 - lua_close(L); 142 - SDL_DestroyWindow(window); 143 144 - return EXIT_SUCCESS; 145 }
··· 1 #include <stdio.h> 2 + #include <stdlib.h> 3 + #include <SDL3/SDL.h> 4 #include "api/api.h" 5 #include "renderer.h" 6 7 #ifdef _WIN32 8 + # include <windows.h> 9 #elif __linux__ 10 + # include <unistd.h> 11 #elif __APPLE__ 12 + # include <mach-o/dyld.h> 13 #endif 14 15 16 SDL_Window *window; 17 18 19 + static double get_scale(void) 20 + { 21 + return (double)SDL_min(SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()), 1.0); 22 } 23 24 + static void get_exe_filename(char *buf, int sz) 25 + { 26 + # if _WIN32 27 + int len = GetModuleFileName(NULL, buf, sz - 1); 28 + buf[len] = '\0'; 29 + # elif __linux__ 30 + char path[512]; 31 + sprintf(path, "/proc/%d/exe", getpid()); 32 + int len = readlink(path, buf, sz - 1); 33 + buf[len] = '\0'; 34 + # elif __APPLE__ 35 + unsigned size = sz; 36 + _NSGetExecutablePath(buf, &size); 37 + # else 38 + strcpy(buf, "./lite"); 39 + # endif 40 } 41 42 43 + static void init_window_icon(void) 44 + { 45 + # ifndef _WIN32 46 + # include "../icon.inl" 47 + (void) icon_rgba_len; /* unused */ 48 + SDL_Surface *surf = SDL_CreateSurfaceFrom(64, 64, SDL_PIXELFORMAT_ABGR8888, icon_rgba, 64 * 4); 49 + SDL_SetWindowIcon(window, surf); 50 + SDL_DestroySurface(surf); 51 + # endif 52 } 53 54 55 + int main(int argc, char **argv) 56 + { 57 + # ifdef _WIN32 58 + HINSTANCE lib = LoadLibrary("user32.dll"); 59 + int (*SetProcessDPIAware)() = (void *)GetProcAddress(lib, "SetProcessDPIAware"); 60 + SetProcessDPIAware(); 61 + # endif 62 63 + SDL_Init(SDL_INIT_VIDEO | SDL_INIT_EVENTS); 64 + SDL_EnableScreenSaver(); 65 + SDL_SetEventEnabled(SDL_EVENT_DROP_FILE, true); 66 + atexit(SDL_Quit); 67 68 + SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0"); 69 + SDL_SetHint(SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH, "1"); 70 71 + const SDL_DisplayMode *dm = SDL_GetCurrentDisplayMode(SDL_GetPrimaryDisplay()); 72 + window = SDL_CreateWindow("", (int)(dm->w * 0.8), (int)(dm->h * 0.8), SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIGH_PIXEL_DENSITY | SDL_WINDOW_HIDDEN); 73 + if (!window) 74 + { 75 + SDL_LogError(SDL_LOG_CATEGORY_ERROR, "Failed to create window: %s\n", SDL_GetError()); 76 + return EXIT_FAILURE; 77 + } 78 + init_window_icon(); 79 + ren_init(window); 80 81 + if (!SDL_StartTextInput(window)) 82 + { 83 + # ifndef _WIN32 84 + SDL_LogError(SDL_LOG_CATEGORY_ERROR, "ERROR: failed SDL_StartTextInput() - text entry may not work"); 85 + # endif 86 + } 87 88 89 + lua_State *L = luaL_newstate(); 90 + luaL_openlibs(L); 91 + api_load_libs(L); 92 93 + lua_newtable(L); 94 + for (int i = 0 ; i < argc ; i++) 95 + { 96 + lua_pushstring(L, argv[i]); 97 + lua_rawseti(L, -2, i + 1); 98 + } 99 + lua_setglobal(L, "ARGS"); 100 101 + lua_pushstring(L, "1.11"); 102 + lua_setglobal(L, "VERSION"); 103 104 + lua_pushstring(L, SDL_GetPlatform()); 105 + lua_setglobal(L, "PLATFORM"); 106 107 + lua_pushnumber(L, get_scale()); 108 + lua_setglobal(L, "SCALE"); 109 110 + char exename[2048]; 111 + get_exe_filename(exename, sizeof(exename)); 112 + lua_pushstring(L, exename); 113 + lua_setglobal(L, "EXEFILE"); 114 115 + (void) luaL_dostring(L, 116 + "local core\n" 117 + "xpcall(function()\n" 118 + " SCALE = tonumber(os.getenv(\"LITE_SCALE\")) or SCALE\n" 119 + " PATHSEP = package.config:sub(1, 1)\n" 120 + " EXEDIR = EXEFILE:match(\"^(.+)[/\\\\].*$\")\n" 121 + " package.path = EXEDIR .. '/data/?.lua;' .. package.path\n" 122 + " package.path = EXEDIR .. '/data/?/init.lua;' .. package.path\n" 123 + " core = require('core')\n" 124 + " core.init()\n" 125 + " core.run()\n" 126 + "end, function(err)\n" 127 + " print('Error: ' .. tostring(err))\n" 128 + " print(debug.traceback(nil, 2))\n" 129 + " if core and core.on_error then\n" 130 + " pcall(core.on_error, err)\n" 131 + " end\n" 132 + " os.exit(1)\n" 133 + "end)"); 134 135 136 + lua_close(L); 137 + SDL_DestroyWindow(window); 138 139 + return EXIT_SUCCESS; 140 }
+265 -194
src/rencache.c
··· 11 #define CELL_SIZE 96 12 #define COMMAND_BUF_SIZE (1024 * 512) 13 14 - enum { FREE_FONT, SET_CLIP, DRAW_TEXT, DRAW_RECT }; 15 16 - typedef struct { 17 - int type, size; 18 - RenRect rect; 19 - RenColor color; 20 - RenFont *font; 21 - char text[0]; 22 } Command; 23 24 ··· 39 /* 32bit fnv-1a hash */ 40 #define HASH_INITIAL 2166136261 41 42 - static void hash(unsigned *h, const void *data, int size) { 43 - const unsigned char *p = data; 44 - while (size--) { 45 - *h = (*h ^ *p++) * 16777619; 46 - } 47 } 48 49 50 - static inline int cell_idx(int x, int y) { 51 - return x + y * CELLS_X; 52 } 53 54 55 - static inline bool rects_overlap(RenRect a, RenRect b) { 56 - return b.x + b.width >= a.x && b.x <= a.x + a.width 57 - && b.y + b.height >= a.y && b.y <= a.y + a.height; 58 } 59 60 61 - static RenRect intersect_rects(RenRect a, RenRect b) { 62 - int x1 = max(a.x, b.x); 63 - int y1 = max(a.y, b.y); 64 - int x2 = min(a.x + a.width, b.x + b.width); 65 - int y2 = min(a.y + a.height, b.y + b.height); 66 - return (RenRect) { x1, y1, max(0, x2 - x1), max(0, y2 - y1) }; 67 } 68 69 70 - static RenRect merge_rects(RenRect a, RenRect b) { 71 - int x1 = min(a.x, b.x); 72 - int y1 = min(a.y, b.y); 73 - int x2 = max(a.x + a.width, b.x + b.width); 74 - int y2 = max(a.y + a.height, b.y + b.height); 75 - return (RenRect) { x1, y1, x2 - x1, y2 - y1 }; 76 } 77 78 79 - static Command* push_command(int type, int size) { 80 - Command *cmd = (Command*) (command_buf + command_buf_idx); 81 - int n = command_buf_idx + size; 82 - if (n > COMMAND_BUF_SIZE) { 83 - fprintf(stderr, "Warning: (" __FILE__ "): exhausted command buffer\n"); 84 - return NULL; 85 - } 86 - command_buf_idx = n; 87 - memset(cmd, 0, sizeof(Command)); 88 - cmd->type = type; 89 - cmd->size = size; 90 - return cmd; 91 } 92 93 94 - static bool next_command(Command **prev) { 95 - if (*prev == NULL) { 96 - *prev = (Command*) command_buf; 97 - } else { 98 - *prev = (Command*) (((char*) *prev) + (*prev)->size); 99 - } 100 - return *prev != ((Command*) (command_buf + command_buf_idx)); 101 } 102 103 104 - void rencache_show_debug(bool enable) { 105 - show_debug = enable; 106 } 107 108 109 - void rencache_free_font(RenFont *font) { 110 - Command *cmd = push_command(FREE_FONT, sizeof(Command)); 111 - if (cmd) { cmd->font = font; } 112 } 113 114 115 - void rencache_set_clip_rect(RenRect rect) { 116 - Command *cmd = push_command(SET_CLIP, sizeof(Command)); 117 - if (cmd) { cmd->rect = intersect_rects(rect, screen_rect); } 118 } 119 120 121 - void rencache_draw_rect(RenRect rect, RenColor color) { 122 - if (!rects_overlap(screen_rect, rect)) { return; } 123 - Command *cmd = push_command(DRAW_RECT, sizeof(Command)); 124 - if (cmd) { 125 - cmd->rect = rect; 126 - cmd->color = color; 127 - } 128 } 129 130 131 - int rencache_draw_text(RenFont *font, const char *text, int x, int y, RenColor color) { 132 - RenRect rect; 133 - rect.x = x; 134 - rect.y = y; 135 - rect.width = ren_get_font_width(font, text); 136 - rect.height = ren_get_font_height(font); 137 138 - if (rects_overlap(screen_rect, rect)) { 139 - int sz = strlen(text) + 1; 140 - Command *cmd = push_command(DRAW_TEXT, sizeof(Command) + sz); 141 - if (cmd) { 142 - memcpy(cmd->text, text, sz); 143 - cmd->color = color; 144 - cmd->font = font; 145 - cmd->rect = rect; 146 - } 147 - } 148 149 - return x + rect.width; 150 } 151 152 153 - void rencache_invalidate(void) { 154 - memset(cells_prev, 0xff, sizeof(cells_buf1)); 155 } 156 157 158 - void rencache_begin_frame(void) { 159 - /* reset all cells if the screen width/height has changed */ 160 - int w, h; 161 - ren_get_size(&w, &h); 162 - if (screen_rect.width != w || h != screen_rect.height) { 163 - screen_rect.width = w; 164 - screen_rect.height = h; 165 - rencache_invalidate(); 166 - } 167 } 168 169 170 - static void update_overlapping_cells(RenRect r, unsigned h) { 171 - int x1 = r.x / CELL_SIZE; 172 - int y1 = r.y / CELL_SIZE; 173 - int x2 = (r.x + r.width) / CELL_SIZE; 174 - int y2 = (r.y + r.height) / CELL_SIZE; 175 176 - for (int y = y1; y <= y2; y++) { 177 - for (int x = x1; x <= x2; x++) { 178 - int idx = cell_idx(x, y); 179 - hash(&cells[idx], &h, sizeof(h)); 180 - } 181 - } 182 } 183 184 185 - static void push_rect(RenRect r, int *count) { 186 - /* try to merge with existing rectangle */ 187 - for (int i = *count - 1; i >= 0; i--) { 188 - RenRect *rp = &rect_buf[i]; 189 - if (rects_overlap(*rp, r)) { 190 - *rp = merge_rects(*rp, r); 191 - return; 192 - } 193 - } 194 - /* couldn't merge with previous rectangle: push */ 195 - rect_buf[(*count)++] = r; 196 } 197 198 199 - void rencache_end_frame(void) { 200 - /* update cells from commands */ 201 - Command *cmd = NULL; 202 - RenRect cr = screen_rect; 203 - while (next_command(&cmd)) { 204 - if (cmd->type == SET_CLIP) { cr = cmd->rect; } 205 - RenRect r = intersect_rects(cmd->rect, cr); 206 - if (r.width == 0 || r.height == 0) { continue; } 207 - unsigned h = HASH_INITIAL; 208 - hash(&h, cmd, cmd->size); 209 - update_overlapping_cells(r, h); 210 - } 211 212 - /* push rects for all cells changed from last frame, reset cells */ 213 - int rect_count = 0; 214 - int max_x = screen_rect.width / CELL_SIZE + 1; 215 - int max_y = screen_rect.height / CELL_SIZE + 1; 216 - for (int y = 0; y < max_y; y++) { 217 - for (int x = 0; x < max_x; x++) { 218 - /* compare previous and current cell for change */ 219 - int idx = cell_idx(x, y); 220 - if (cells[idx] != cells_prev[idx]) { 221 - push_rect((RenRect) { x, y, 1, 1 }, &rect_count); 222 - } 223 - cells_prev[idx] = HASH_INITIAL; 224 - } 225 - } 226 227 - /* expand rects from cells to pixels */ 228 - for (int i = 0; i < rect_count; i++) { 229 - RenRect *r = &rect_buf[i]; 230 - r->x *= CELL_SIZE; 231 - r->y *= CELL_SIZE; 232 - r->width *= CELL_SIZE; 233 - r->height *= CELL_SIZE; 234 - *r = intersect_rects(*r, screen_rect); 235 - } 236 237 - /* redraw updated regions */ 238 - bool has_free_commands = false; 239 - for (int i = 0; i < rect_count; i++) { 240 - /* draw */ 241 - RenRect r = rect_buf[i]; 242 - ren_set_clip_rect(r); 243 244 - cmd = NULL; 245 - while (next_command(&cmd)) { 246 - switch (cmd->type) { 247 - case FREE_FONT: 248 - has_free_commands = true; 249 - break; 250 - case SET_CLIP: 251 - ren_set_clip_rect(intersect_rects(cmd->rect, r)); 252 - break; 253 - case DRAW_RECT: 254 - ren_draw_rect(cmd->rect, cmd->color); 255 - break; 256 - case DRAW_TEXT: 257 - ren_draw_text(cmd->font, cmd->text, cmd->rect.x, cmd->rect.y, cmd->color); 258 - break; 259 - } 260 - } 261 262 - if (show_debug) { 263 - RenColor color = { rand(), rand(), rand(), 50 }; 264 - ren_draw_rect(r, color); 265 - } 266 - } 267 268 - /* update dirty rects */ 269 - if (rect_count > 0) { 270 - ren_update_rects(rect_buf, rect_count); 271 - } 272 273 - /* free fonts */ 274 - if (has_free_commands) { 275 - cmd = NULL; 276 - while (next_command(&cmd)) { 277 - if (cmd->type == FREE_FONT) { 278 - ren_free_font(cmd->font); 279 - } 280 - } 281 - } 282 283 - /* swap cell buffer and reset */ 284 - unsigned *tmp = cells; 285 - cells = cells_prev; 286 - cells_prev = tmp; 287 - command_buf_idx = 0; 288 }
··· 11 #define CELL_SIZE 96 12 #define COMMAND_BUF_SIZE (1024 * 512) 13 14 + enum 15 + { 16 + FREE_FONT, 17 + SET_CLIP, 18 + DRAW_TEXT, 19 + DRAW_RECT 20 + }; 21 22 + typedef struct 23 + { 24 + int type, size; 25 + RenRect rect; 26 + RenColor color; 27 + RenFont *font; 28 + int tab_width; 29 + char text[0]; 30 } Command; 31 32 ··· 47 /* 32bit fnv-1a hash */ 48 #define HASH_INITIAL 2166136261 49 50 + static void hash(unsigned *h, const void *data, int size) 51 + { 52 + const unsigned char *p = data; 53 + while (size--) 54 + { 55 + *h = (*h ^ *p++) * 16777619; 56 + } 57 } 58 59 60 + static inline int cell_idx(int x, int y) 61 + { 62 + return x + y * CELLS_X; 63 } 64 65 66 + static inline bool rects_overlap(RenRect a, RenRect b) 67 + { 68 + return b.x + b.width >= a.x && 69 + b.x <= a.x + a.width && 70 + b.y + b.height >= a.y && 71 + b.y <= a.y + a.height; 72 } 73 74 75 + static RenRect intersect_rects(RenRect a, RenRect b) 76 + { 77 + int x1 = max(a.x, b.x); 78 + int y1 = max(a.y, b.y); 79 + int x2 = min(a.x + a.width, b.x + b.width); 80 + int y2 = min(a.y + a.height, b.y + b.height); 81 + return (RenRect){ x1, y1, max(0, x2 - x1), max(0, y2 - y1) }; 82 } 83 84 85 + static RenRect merge_rects(RenRect a, RenRect b) 86 + { 87 + int x1 = min(a.x, b.x); 88 + int y1 = min(a.y, b.y); 89 + int x2 = max(a.x + a.width, b.x + b.width); 90 + int y2 = max(a.y + a.height, b.y + b.height); 91 + return (RenRect){ x1, y1, x2 - x1, y2 - y1 }; 92 } 93 94 95 + static Command* push_command(int type, int size) 96 + { 97 + Command *cmd = (Command *)(command_buf + command_buf_idx); 98 + int n = command_buf_idx + size; 99 + if (n > COMMAND_BUF_SIZE) 100 + { 101 + fprintf(stderr, "Warning: (" __FILE__ "): exhausted command buffer\n"); 102 + return NULL; 103 + } 104 + command_buf_idx = n; 105 + memset(cmd, 0, sizeof(Command)); 106 + cmd->type = type; 107 + cmd->size = size; 108 + return cmd; 109 } 110 111 112 + static bool next_command(Command **prev) 113 + { 114 + if (*prev == NULL) 115 + { 116 + *prev = (Command *)command_buf; 117 + } 118 + else 119 + { 120 + *prev = (Command *)(((char *)*prev) + (*prev)->size); 121 + } 122 + 123 + return *prev != ((Command *)(command_buf + command_buf_idx)); 124 } 125 126 127 + void rencache_show_debug(bool enable) 128 + { 129 + show_debug = enable; 130 } 131 132 133 + void rencache_free_font(RenFont *font) 134 + { 135 + Command *cmd = push_command(FREE_FONT, sizeof(Command)); 136 + if (cmd) 137 + { 138 + cmd->font = font; 139 + } 140 } 141 142 143 + void rencache_set_clip_rect(RenRect rect) 144 + { 145 + Command *cmd = push_command(SET_CLIP, sizeof(Command)); 146 + if (cmd) 147 + { 148 + cmd->rect = intersect_rects(rect, screen_rect); 149 + } 150 } 151 152 153 + void rencache_draw_rect(RenRect rect, RenColor color) 154 + { 155 + if (!rects_overlap(screen_rect, rect)) 156 + { 157 + return; 158 + } 159 + Command *cmd = push_command(DRAW_RECT, sizeof(Command)); 160 + if (cmd) 161 + { 162 + cmd->rect = rect; 163 + cmd->color = color; 164 + } 165 } 166 167 168 + int rencache_draw_text(RenFont *font, const char *text, int x, int y, RenColor color) 169 + { 170 + RenRect rect; 171 + rect.x = x; 172 + rect.y = y; 173 + rect.width = ren_get_font_width(font, text); 174 + rect.height = ren_get_font_height(font); 175 176 + if (rects_overlap(screen_rect, rect)) 177 + { 178 + int sz = strlen(text) + 1; 179 + Command *cmd = push_command(DRAW_TEXT, sizeof(Command) + sz); 180 + if (cmd) 181 + { 182 + memcpy(cmd->text, text, sz); 183 + cmd->color = color; 184 + cmd->font = font; 185 + cmd->rect = rect; 186 + cmd->tab_width = ren_get_font_tab_width(font); 187 + } 188 + } 189 190 + return x + rect.width; 191 } 192 193 194 + void rencache_invalidate(void) 195 + { 196 + memset(cells_prev, 0xff, sizeof(cells_buf1)); 197 } 198 199 200 + void rencache_begin_frame(void) 201 + { 202 + /* reset all cells if the screen width/height has changed */ 203 + int w, h; 204 + ren_get_size(&w, &h); 205 + if (screen_rect.width != w || h != screen_rect.height) 206 + { 207 + screen_rect.width = w; 208 + screen_rect.height = h; 209 + rencache_invalidate(); 210 + } 211 } 212 213 214 + static void update_overlapping_cells(RenRect r, unsigned h) 215 + { 216 + int x1 = r.x / CELL_SIZE; 217 + int y1 = r.y / CELL_SIZE; 218 + int x2 = (r.x + r.width) / CELL_SIZE; 219 + int y2 = (r.y + r.height) / CELL_SIZE; 220 221 + for (int y = y1 ; y <= y2 ; y++) 222 + { 223 + for (int x = x1 ; x <= x2 ; x++) 224 + { 225 + int idx = cell_idx(x, y); 226 + hash(&cells[idx], &h, sizeof(h)); 227 + } 228 + } 229 } 230 231 232 + static void push_rect(RenRect r, int *count) 233 + { 234 + /* try to merge with existing rectangle */ 235 + for (int i = *count - 1 ; i >= 0 ; i--) 236 + { 237 + RenRect *rp = &rect_buf[i]; 238 + if (rects_overlap(*rp, r)) 239 + { 240 + *rp = merge_rects(*rp, r); 241 + return; 242 + } 243 + } 244 + /* couldn't merge with previous rectangle: push */ 245 + rect_buf[(*count)++] = r; 246 } 247 248 249 + void rencache_end_frame(void) 250 + { 251 + /* update cells from commands */ 252 + Command *cmd = NULL; 253 + RenRect cr = screen_rect; 254 + while (next_command(&cmd)) 255 + { 256 + if (cmd->type == SET_CLIP) 257 + { 258 + cr = cmd->rect; 259 + } 260 + RenRect r = intersect_rects(cmd->rect, cr); 261 + if (r.width == 0 || r.height == 0) 262 + { 263 + continue; 264 + } 265 + unsigned h = HASH_INITIAL; 266 + hash(&h, cmd, cmd->size); 267 + update_overlapping_cells(r, h); 268 + } 269 270 + /* push rects for all cells changed from last frame, reset cells */ 271 + int rect_count = 0; 272 + int max_x = screen_rect.width / CELL_SIZE + 1; 273 + int max_y = screen_rect.height / CELL_SIZE + 1; 274 + for (int y = 0 ; y < max_y; y++) 275 + { 276 + for (int x = 0 ; x < max_x ; x++) 277 + { 278 + /* compare previous and current cell for change */ 279 + int idx = cell_idx(x, y); 280 + if (cells[idx] != cells_prev[idx]) 281 + { 282 + push_rect((RenRect) { x, y, 1, 1 }, &rect_count); 283 + } 284 + cells_prev[idx] = HASH_INITIAL; 285 + } 286 + } 287 288 + /* expand rects from cells to pixels */ 289 + for (int i = 0; i < rect_count; i++) 290 + { 291 + RenRect *r = &rect_buf[i]; 292 + r->x *= CELL_SIZE; 293 + r->y *= CELL_SIZE; 294 + r->width *= CELL_SIZE; 295 + r->height *= CELL_SIZE; 296 + *r = intersect_rects(*r, screen_rect); 297 + } 298 299 + /* redraw updated regions */ 300 + bool has_free_commands = false; 301 + for (int i = 0; i < rect_count; i++) 302 + { 303 + /* draw */ 304 + RenRect r = rect_buf[i]; 305 + ren_set_clip_rect(r); 306 307 + cmd = NULL; 308 + while (next_command(&cmd)) 309 + { 310 + switch (cmd->type) 311 + { 312 + case FREE_FONT: 313 + has_free_commands = true; 314 + break; 315 + case SET_CLIP: 316 + ren_set_clip_rect(intersect_rects(cmd->rect, r)); 317 + break; 318 + case DRAW_RECT: 319 + ren_draw_rect(cmd->rect, cmd->color); 320 + break; 321 + case DRAW_TEXT: 322 + ren_set_font_tab_width(cmd->font, cmd->tab_width); 323 + ren_draw_text(cmd->font, cmd->text, cmd->rect.x, cmd->rect.y, cmd->color); 324 + break; 325 + } 326 + } 327 328 + if (show_debug) 329 + { 330 + RenColor color = { rand(), rand(), rand(), 50 }; 331 + ren_draw_rect(r, color); 332 + } 333 + } 334 335 + /* update dirty rects */ 336 + if (rect_count > 0) 337 + { 338 + ren_update_rects(rect_buf, rect_count); 339 + } 340 341 + /* free fonts */ 342 + if (has_free_commands) 343 + { 344 + cmd = NULL; 345 + while (next_command(&cmd)) 346 + { 347 + if (cmd->type == FREE_FONT) 348 + { 349 + ren_free_font(cmd->font); 350 + } 351 + } 352 + } 353 354 + /* swap cell buffer and reset */ 355 + unsigned *tmp = cells; 356 + cells = cells_prev; 357 + cells_prev = tmp; 358 + command_buf_idx = 0; 359 }
+301 -246
src/renderer.c
··· 7 8 #define MAX_GLYPHSET 256 9 10 - struct RenImage { 11 - RenColor *pixels; 12 - int width, height; 13 }; 14 15 - typedef struct { 16 - RenImage *image; 17 - stbtt_bakedchar glyphs[256]; 18 } GlyphSet; 19 20 - struct RenFont { 21 - void *data; 22 - stbtt_fontinfo stbfont; 23 - GlyphSet *sets[MAX_GLYPHSET]; 24 - float size; 25 - int height; 26 }; 27 28 29 static SDL_Window *window; 30 - static struct { int left, top, right, bottom; } clip; 31 32 33 - static void* check_alloc(void *ptr) { 34 - if (!ptr) { 35 - fprintf(stderr, "Fatal error: memory allocation failed\n"); 36 - exit(EXIT_FAILURE); 37 - } 38 - return ptr; 39 } 40 41 42 - static const char* utf8_to_codepoint(const char *p, unsigned *dst) { 43 - unsigned res, n; 44 - switch (*p & 0xf0) { 45 - case 0xf0 : res = *p & 0x07; n = 3; break; 46 - case 0xe0 : res = *p & 0x0f; n = 2; break; 47 - case 0xd0 : 48 - case 0xc0 : res = *p & 0x1f; n = 1; break; 49 - default : res = *p; n = 0; break; 50 - } 51 - while (n--) { 52 - res = (res << 6) | (*(++p) & 0x3f); 53 - } 54 - *dst = res; 55 - return p + 1; 56 } 57 58 59 - void ren_init(SDL_Window *win) { 60 - assert(win); 61 - window = win; 62 - SDL_Surface *surf = SDL_GetWindowSurface(window); 63 - ren_set_clip_rect( (RenRect) { 0, 0, surf->w, surf->h } ); 64 } 65 66 67 - void ren_update_rects(RenRect *rects, int count) { 68 - SDL_UpdateWindowSurfaceRects(window, (SDL_Rect*) rects, count); 69 - static bool initial_frame = true; 70 - if (initial_frame) { 71 - SDL_ShowWindow(window); 72 - initial_frame = false; 73 - } 74 } 75 76 77 - void ren_set_clip_rect(RenRect rect) { 78 - clip.left = rect.x; 79 - clip.top = rect.y; 80 - clip.right = rect.x + rect.width; 81 - clip.bottom = rect.y + rect.height; 82 } 83 84 85 - void ren_get_size(int *x, int *y) { 86 - SDL_Surface *surf = SDL_GetWindowSurface(window); 87 - *x = surf->w; 88 - *y = surf->h; 89 } 90 91 92 - RenImage* ren_new_image(int width, int height) { 93 - assert(width > 0 && height > 0); 94 - RenImage *image = malloc(sizeof(RenImage) + width * height * sizeof(RenColor)); 95 - check_alloc(image); 96 - image->pixels = (void*) (image + 1); 97 - image->width = width; 98 - image->height = height; 99 - return image; 100 } 101 102 103 - void ren_free_image(RenImage *image) { 104 - free(image); 105 } 106 107 108 - static GlyphSet* load_glyphset(RenFont *font, int idx) { 109 - GlyphSet *set = check_alloc(calloc(1, sizeof(GlyphSet))); 110 111 - /* init image */ 112 - int width = 128; 113 - int height = 128; 114 retry: 115 - set->image = ren_new_image(width, height); 116 117 - /* load glyphs */ 118 - float s = 119 - stbtt_ScaleForMappingEmToPixels(&font->stbfont, 1) / 120 - stbtt_ScaleForPixelHeight(&font->stbfont, 1); 121 - int res = stbtt_BakeFontBitmap( 122 - font->data, 0, font->size * s, (void*) set->image->pixels, 123 - width, height, idx * 256, 256, set->glyphs); 124 125 - /* retry with a larger image buffer if the buffer wasn't large enough */ 126 - if (res < 0) { 127 - width *= 2; 128 - height *= 2; 129 - ren_free_image(set->image); 130 - goto retry; 131 - } 132 133 - /* adjust glyph yoffsets and xadvance */ 134 - int ascent, descent, linegap; 135 - stbtt_GetFontVMetrics(&font->stbfont, &ascent, &descent, &linegap); 136 - float scale = stbtt_ScaleForMappingEmToPixels(&font->stbfont, font->size); 137 - int scaled_ascent = ascent * scale + 0.5; 138 - for (int i = 0; i < 256; i++) { 139 - set->glyphs[i].yoff += scaled_ascent; 140 - set->glyphs[i].xadvance = floor(set->glyphs[i].xadvance); 141 - } 142 143 - /* convert 8bit data to 32bit */ 144 - for (int i = width * height - 1; i >= 0; i--) { 145 - uint8_t n = *((uint8_t*) set->image->pixels + i); 146 - set->image->pixels[i] = (RenColor) { .r = 255, .g = 255, .b = 255, .a = n }; 147 - } 148 149 - return set; 150 } 151 152 153 - static GlyphSet* get_glyphset(RenFont *font, int codepoint) { 154 - int idx = (codepoint >> 8) % MAX_GLYPHSET; 155 - if (!font->sets[idx]) { 156 - font->sets[idx] = load_glyphset(font, idx); 157 - } 158 - return font->sets[idx]; 159 } 160 161 162 - RenFont* ren_load_font(const char *filename, float size) { 163 - RenFont *font = NULL; 164 - FILE *fp = NULL; 165 166 - /* init font */ 167 - font = check_alloc(calloc(1, sizeof(RenFont))); 168 - font->size = size; 169 170 - /* load font into buffer */ 171 - fp = fopen(filename, "rb"); 172 - if (!fp) { return NULL; } 173 - /* get size */ 174 - fseek(fp, 0, SEEK_END); int buf_size = ftell(fp); fseek(fp, 0, SEEK_SET); 175 - /* load */ 176 - font->data = check_alloc(malloc(buf_size)); 177 - int _ = fread(font->data, 1, buf_size, fp); (void) _; 178 - fclose(fp); 179 - fp = NULL; 180 181 - /* init stbfont */ 182 - int ok = stbtt_InitFont(&font->stbfont, font->data, 0); 183 - if (!ok) { goto fail; } 184 185 - /* get height and scale */ 186 - int ascent, descent, linegap; 187 - stbtt_GetFontVMetrics(&font->stbfont, &ascent, &descent, &linegap); 188 - float scale = stbtt_ScaleForMappingEmToPixels(&font->stbfont, size); 189 - font->height = (ascent - descent + linegap) * scale + 0.5; 190 191 - /* make tab and newline glyphs invisible */ 192 - stbtt_bakedchar *g = get_glyphset(font, '\n')->glyphs; 193 - g['\t'].x1 = g['\t'].x0; 194 - g['\n'].x1 = g['\n'].x0; 195 196 - return font; 197 198 fail: 199 - if (fp) { fclose(fp); } 200 - if (font) { free(font->data); } 201 - free(font); 202 - return NULL; 203 } 204 205 206 - void ren_free_font(RenFont *font) { 207 - for (int i = 0; i < MAX_GLYPHSET; i++) { 208 - GlyphSet *set = font->sets[i]; 209 - if (set) { 210 - ren_free_image(set->image); 211 - free(set); 212 - } 213 - } 214 - free(font->data); 215 - free(font); 216 } 217 218 219 - void ren_set_font_tab_width(RenFont *font, int n) { 220 - GlyphSet *set = get_glyphset(font, '\t'); 221 - set->glyphs['\t'].xadvance = n; 222 } 223 224 225 - int ren_get_font_width(RenFont *font, const char *text) { 226 - int x = 0; 227 - const char *p = text; 228 - unsigned codepoint; 229 - while (*p) { 230 - p = utf8_to_codepoint(p, &codepoint); 231 - GlyphSet *set = get_glyphset(font, codepoint); 232 - stbtt_bakedchar *g = &set->glyphs[codepoint & 0xff]; 233 - x += g->xadvance; 234 - } 235 - return x; 236 } 237 238 239 - int ren_get_font_height(RenFont *font) { 240 - return font->height; 241 } 242 243 244 - static inline RenColor blend_pixel(RenColor dst, RenColor src) { 245 - int ia = 0xff - src.a; 246 - dst.r = ((src.r * src.a) + (dst.r * ia)) >> 8; 247 - dst.g = ((src.g * src.a) + (dst.g * ia)) >> 8; 248 - dst.b = ((src.b * src.a) + (dst.b * ia)) >> 8; 249 - return dst; 250 } 251 252 253 - static inline RenColor blend_pixel2(RenColor dst, RenColor src, RenColor color) { 254 - src.a = (src.a * color.a) >> 8; 255 - int ia = 0xff - src.a; 256 - dst.r = ((src.r * color.r * src.a) >> 16) + ((dst.r * ia) >> 8); 257 - dst.g = ((src.g * color.g * src.a) >> 16) + ((dst.g * ia) >> 8); 258 - dst.b = ((src.b * color.b * src.a) >> 16) + ((dst.b * ia) >> 8); 259 - return dst; 260 } 261 262 263 - #define rect_draw_loop(expr) \ 264 - for (int j = y1; j < y2; j++) { \ 265 - for (int i = x1; i < x2; i++) { \ 266 - *d = expr; \ 267 - d++; \ 268 - } \ 269 - d += dr; \ 270 - } 271 272 - void ren_draw_rect(RenRect rect, RenColor color) { 273 - if (color.a == 0) { return; } 274 275 - int x1 = rect.x < clip.left ? clip.left : rect.x; 276 - int y1 = rect.y < clip.top ? clip.top : rect.y; 277 - int x2 = rect.x + rect.width; 278 - int y2 = rect.y + rect.height; 279 - x2 = x2 > clip.right ? clip.right : x2; 280 - y2 = y2 > clip.bottom ? clip.bottom : y2; 281 282 - SDL_Surface *surf = SDL_GetWindowSurface(window); 283 - RenColor *d = (RenColor*) surf->pixels; 284 - d += x1 + y1 * surf->w; 285 - int dr = surf->w - (x2 - x1); 286 287 - if (color.a == 0xff) { 288 - rect_draw_loop(color); 289 - } else { 290 - rect_draw_loop(blend_pixel(*d, color)); 291 - } 292 } 293 294 295 - void ren_draw_image(RenImage *image, RenRect *sub, int x, int y, RenColor color) { 296 - if (color.a == 0) { return; } 297 298 - /* clip */ 299 - int n; 300 - if ((n = clip.left - x) > 0) { sub->width -= n; sub->x += n; x += n; } 301 - if ((n = clip.top - y) > 0) { sub->height -= n; sub->y += n; y += n; } 302 - if ((n = x + sub->width - clip.right ) > 0) { sub->width -= n; } 303 - if ((n = y + sub->height - clip.bottom) > 0) { sub->height -= n; } 304 305 - if (sub->width <= 0 || sub->height <= 0) { 306 - return; 307 - } 308 309 - /* draw */ 310 - SDL_Surface *surf = SDL_GetWindowSurface(window); 311 - RenColor *s = image->pixels; 312 - RenColor *d = (RenColor*) surf->pixels; 313 - s += sub->x + sub->y * image->width; 314 - d += x + y * surf->w; 315 - int sr = image->width - sub->width; 316 - int dr = surf->w - sub->width; 317 318 - for (int j = 0; j < sub->height; j++) { 319 - for (int i = 0; i < sub->width; i++) { 320 - *d = blend_pixel2(*d, *s, color); 321 - d++; 322 - s++; 323 - } 324 - d += dr; 325 - s += sr; 326 - } 327 } 328 329 330 - int ren_draw_text(RenFont *font, const char *text, int x, int y, RenColor color) { 331 - RenRect rect; 332 - const char *p = text; 333 - unsigned codepoint; 334 - while (*p) { 335 - p = utf8_to_codepoint(p, &codepoint); 336 - GlyphSet *set = get_glyphset(font, codepoint); 337 - stbtt_bakedchar *g = &set->glyphs[codepoint & 0xff]; 338 - rect.x = g->x0; 339 - rect.y = g->y0; 340 - rect.width = g->x1 - g->x0; 341 - rect.height = g->y1 - g->y0; 342 - ren_draw_image(set->image, &rect, x + g->xoff, y + g->yoff, color); 343 - x += g->xadvance; 344 - } 345 - return x; 346 }
··· 7 8 #define MAX_GLYPHSET 256 9 10 + struct RenImage 11 + { 12 + RenColor *pixels; 13 + int width, height; 14 }; 15 16 + typedef struct 17 + { 18 + RenImage *image; 19 + stbtt_bakedchar glyphs[256]; 20 } GlyphSet; 21 22 + struct RenFont 23 + { 24 + void *data; 25 + stbtt_fontinfo stbfont; 26 + GlyphSet *sets[MAX_GLYPHSET]; 27 + float size; 28 + int height; 29 }; 30 31 32 static SDL_Window *window; 33 + static struct 34 + { 35 + int left, top, right, bottom; 36 + } clip; 37 38 39 + static void* check_alloc(void *ptr) 40 + { 41 + if (!ptr) 42 + { 43 + fprintf(stderr, "Fatal error: memory allocation failed\n"); 44 + exit(EXIT_FAILURE); 45 + } 46 + return ptr; 47 } 48 49 50 + static const char* utf8_to_codepoint(const char *p, unsigned *dst) 51 + { 52 + unsigned res, n; 53 + switch (*p & 0xf0) 54 + { 55 + case 0xf0 : res = *p & 0x07; n = 3; break; 56 + case 0xe0 : res = *p & 0x0f; n = 2; break; 57 + case 0xd0 : 58 + case 0xc0 : res = *p & 0x1f; n = 1; break; 59 + default : res = *p; n = 0; break; 60 + } 61 + while (n--) 62 + res = (res << 6) | (*(++p) & 0x3f); 63 + *dst = res; 64 + return p + 1; 65 } 66 67 68 + void ren_init(SDL_Window *win) 69 + { 70 + assert(win); 71 + window = win; 72 + SDL_Surface *surf = SDL_GetWindowSurface(window); 73 + ren_set_clip_rect((RenRect){ 0, 0, surf->w, surf->h }); 74 } 75 76 77 + void ren_update_rects(RenRect *rects, int count) 78 + { 79 + SDL_UpdateWindowSurfaceRects(window, (SDL_Rect *)rects, count); 80 + static bool initial_frame = true; 81 + if (initial_frame) 82 + { 83 + SDL_ShowWindow(window); 84 + initial_frame = false; 85 + } 86 } 87 88 89 + void ren_set_clip_rect(RenRect rect) 90 + { 91 + clip.left = rect.x; 92 + clip.top = rect.y; 93 + clip.right = rect.x + rect.width; 94 + clip.bottom = rect.y + rect.height; 95 } 96 97 98 + void ren_get_size(int *x, int *y) 99 + { 100 + SDL_Surface *surf = SDL_GetWindowSurface(window); 101 + *x = surf->w; 102 + *y = surf->h; 103 } 104 105 106 + RenImage* ren_new_image(int width, int height) 107 + { 108 + assert(width > 0 && height > 0); 109 + RenImage *image = malloc(sizeof(RenImage) + width * height * sizeof(RenColor)); 110 + check_alloc(image); 111 + image->pixels = (void *)(image + 1); 112 + image->width = width; 113 + image->height = height; 114 + return image; 115 } 116 117 118 + void ren_free_image(RenImage *image) 119 + { 120 + free(image); 121 } 122 123 124 + static GlyphSet* load_glyphset(RenFont *font, int idx) 125 + { 126 + GlyphSet *set = check_alloc(calloc(1, sizeof(GlyphSet))); 127 128 + /* init image */ 129 + int width = 128; 130 + int height = 128; 131 retry: 132 + set->image = ren_new_image(width, height); 133 134 + /* load glyphs */ 135 + float s = 136 + stbtt_ScaleForMappingEmToPixels(&font->stbfont, 1) / 137 + stbtt_ScaleForPixelHeight(&font->stbfont, 1); 138 + int res = stbtt_BakeFontBitmap( 139 + font->data, 0, font->size * s, (void *)set->image->pixels, 140 + width, height, idx * 256, 256, set->glyphs); 141 142 + /* retry with a larger image buffer if the buffer wasn't large enough */ 143 + if (res < 0) 144 + { 145 + width *= 2; 146 + height *= 2; 147 + ren_free_image(set->image); 148 + goto retry; 149 + } 150 151 + /* adjust glyph yoffsets and xadvance */ 152 + int ascent, descent, linegap; 153 + stbtt_GetFontVMetrics(&font->stbfont, &ascent, &descent, &linegap); 154 + float scale = stbtt_ScaleForMappingEmToPixels(&font->stbfont, font->size); 155 + int scaled_ascent = ascent * scale + 0.5; 156 + for (int i = 0; i < 256; i++) 157 + { 158 + set->glyphs[i].yoff += scaled_ascent; 159 + set->glyphs[i].xadvance = floor(set->glyphs[i].xadvance); 160 + } 161 162 + /* convert 8bit data to 32bit */ 163 + for (int i = width * height - 1; i >= 0; i--) 164 + { 165 + uint8_t n = *((uint8_t *)set->image->pixels + i); 166 + set->image->pixels[i] = (RenColor) { .r = 255, .g = 255, .b = 255, .a = n }; 167 + } 168 169 + return set; 170 } 171 172 173 + static GlyphSet* get_glyphset(RenFont *font, int codepoint) 174 + { 175 + int idx = (codepoint >> 8) % MAX_GLYPHSET; 176 + if (!font->sets[idx]) 177 + font->sets[idx] = load_glyphset(font, idx); 178 + return font->sets[idx]; 179 } 180 181 182 + RenFont* ren_load_font(const char *filename, float size) 183 + { 184 + RenFont *font = NULL; 185 + FILE *fp = NULL; 186 187 + /* init font */ 188 + font = check_alloc(calloc(1, sizeof(RenFont))); 189 + font->size = size; 190 191 + /* load font into buffer */ 192 + fp = fopen(filename, "rb"); 193 + if (!fp) 194 + return NULL; 195 + /* get size */ 196 + fseek(fp, 0, SEEK_END); 197 + int buf_size = ftell(fp); 198 + fseek(fp, 0, SEEK_SET); 199 + /* load */ 200 + font->data = check_alloc(malloc(buf_size)); 201 + (void)fread(font->data, 1, buf_size, fp); 202 + fclose(fp); 203 + fp = NULL; 204 205 + /* init stbfont */ 206 + int ok = stbtt_InitFont(&font->stbfont, font->data, 0); 207 + if (!ok) 208 + goto fail; 209 210 + /* get height and scale */ 211 + int ascent, descent, linegap; 212 + stbtt_GetFontVMetrics(&font->stbfont, &ascent, &descent, &linegap); 213 + float scale = stbtt_ScaleForMappingEmToPixels(&font->stbfont, size); 214 + font->height = (ascent - descent + linegap) * scale + 0.5; 215 216 + /* make tab and newline glyphs invisible */ 217 + stbtt_bakedchar *g = get_glyphset(font, '\n')->glyphs; 218 + g['\t'].x1 = g['\t'].x0; 219 + g['\n'].x1 = g['\n'].x0; 220 221 + return font; 222 223 fail: 224 + if (fp) 225 + fclose(fp); 226 + if (font) 227 + free(font->data); 228 + free(font); 229 + return NULL; 230 + } 231 + 232 + 233 + void ren_free_font(RenFont *font) 234 + { 235 + for (int i = 0 ; i < MAX_GLYPHSET ; i++) 236 + { 237 + GlyphSet *set = font->sets[i]; 238 + if (set) 239 + { 240 + ren_free_image(set->image); 241 + free(set); 242 + } 243 + } 244 + free(font->data); 245 + free(font); 246 } 247 248 249 + void ren_set_font_tab_width(RenFont *font, int n) 250 + { 251 + GlyphSet *set = get_glyphset(font, '\t'); 252 + set->glyphs['\t'].xadvance = n; 253 } 254 255 256 + int ren_get_font_tab_width(RenFont *font) 257 + { 258 + GlyphSet *set = get_glyphset(font, '\t'); 259 + return set->glyphs['\t'].xadvance; 260 } 261 262 263 + int ren_get_font_width(RenFont *font, const char *text) 264 + { 265 + int x = 0; 266 + const char *p = text; 267 + unsigned codepoint; 268 + while (*p) 269 + { 270 + p = utf8_to_codepoint(p, &codepoint); 271 + GlyphSet *set = get_glyphset(font, codepoint); 272 + stbtt_bakedchar *g = &set->glyphs[codepoint & 0xff]; 273 + x += g->xadvance; 274 + } 275 + return x; 276 } 277 278 279 + int ren_get_font_height(RenFont *font) 280 + { 281 + return font->height; 282 } 283 284 285 + static inline RenColor blend_pixel(RenColor dst, RenColor src) 286 + { 287 + int ia = 0xff - src.a; 288 + dst.r = ((src.r * src.a) + (dst.r * ia)) >> 8; 289 + dst.g = ((src.g * src.a) + (dst.g * ia)) >> 8; 290 + dst.b = ((src.b * src.a) + (dst.b * ia)) >> 8; 291 + return dst; 292 } 293 294 295 + static inline RenColor blend_pixel2(RenColor dst, RenColor src, RenColor color) 296 + { 297 + src.a = (src.a * color.a) >> 8; 298 + int ia = 0xff - src.a; 299 + dst.r = ((src.r * color.r * src.a) >> 16) + ((dst.r * ia) >> 8); 300 + dst.g = ((src.g * color.g * src.a) >> 16) + ((dst.g * ia) >> 8); 301 + dst.b = ((src.b * color.b * src.a) >> 16) + ((dst.b * ia) >> 8); 302 + return dst; 303 } 304 305 306 + #define rect_draw_loop(expr) \ 307 + for (int j = y1 ; j < y2 ; j++) \ 308 + { \ 309 + for (int i = x1 ; i < x2 ; i++) \ 310 + { \ 311 + *d = expr; \ 312 + d++; \ 313 + } \ 314 + d += dr; \ 315 + } 316 317 + void ren_draw_rect(RenRect rect, RenColor color) 318 + { 319 + if (color.a == 0) 320 + return; 321 322 + int x1 = rect.x < clip.left ? clip.left : rect.x; 323 + int y1 = rect.y < clip.top ? clip.top : rect.y; 324 + int x2 = rect.x + rect.width; 325 + int y2 = rect.y + rect.height; 326 + x2 = x2 > clip.right ? clip.right : x2; 327 + y2 = y2 > clip.bottom ? clip.bottom : y2; 328 329 + SDL_Surface *surf = SDL_GetWindowSurface(window); 330 + RenColor *d = (RenColor *)surf->pixels; 331 + d += x1 + y1 * surf->w; 332 + int dr = surf->w - (x2 - x1); 333 334 + if (color.a == 0xff) 335 + { 336 + rect_draw_loop(color); 337 + } 338 + else 339 + { 340 + rect_draw_loop(blend_pixel(*d, color)); 341 + } 342 } 343 344 345 + void ren_draw_image(RenImage *image, RenRect *sub, int x, int y, RenColor color) 346 + { 347 + if (color.a == 0) 348 + return; 349 350 + /* clip */ 351 + int n; 352 + if ((n = clip.left - x) > 0) { sub->width -= n; sub->x += n; x += n; } 353 + if ((n = clip.top - y) > 0) { sub->height -= n; sub->y += n; y += n; } 354 + if ((n = x + sub->width - clip.right ) > 0) { sub->width -= n; } 355 + if ((n = y + sub->height - clip.bottom) > 0) { sub->height -= n; } 356 357 + if (sub->width <= 0 || sub->height <= 0) 358 + return; 359 360 + /* draw */ 361 + SDL_Surface *surf = SDL_GetWindowSurface(window); 362 + RenColor *s = image->pixels; 363 + RenColor *d = (RenColor *)surf->pixels; 364 + s += sub->x + sub->y * image->width; 365 + d += x + y * surf->w; 366 + int sr = image->width - sub->width; 367 + int dr = surf->w - sub->width; 368 369 + for (int j = 0; j < sub->height; j++) 370 + { 371 + for (int i = 0; i < sub->width; i++) 372 + { 373 + *d = blend_pixel2(*d, *s, color); 374 + d++; 375 + s++; 376 + } 377 + d += dr; 378 + s += sr; 379 + } 380 } 381 382 383 + int ren_draw_text(RenFont *font, const char *text, int x, int y, RenColor color) 384 + { 385 + RenRect rect; 386 + const char *p = text; 387 + unsigned codepoint; 388 + while (*p) 389 + { 390 + p = utf8_to_codepoint(p, &codepoint); 391 + GlyphSet *set = get_glyphset(font, codepoint); 392 + stbtt_bakedchar *g = &set->glyphs[codepoint & 0xff]; 393 + rect.x = g->x0; 394 + rect.y = g->y0; 395 + rect.width = g->x1 - g->x0; 396 + rect.height = g->y1 - g->y0; 397 + ren_draw_image(set->image, &rect, x + g->xoff, y + g->yoff, color); 398 + x += g->xadvance; 399 + } 400 + return x; 401 }
+12 -3
src/renderer.h
··· 1 #ifndef RENDERER_H 2 #define RENDERER_H 3 4 - #include <SDL2/SDL.h> 5 #include <stdint.h> 6 7 typedef struct RenImage RenImage; 8 typedef struct RenFont RenFont; 9 10 - typedef struct { uint8_t b, g, r, a; } RenColor; 11 - typedef struct { int x, y, width, height; } RenRect; 12 13 14 void ren_init(SDL_Window *win); ··· 22 RenFont* ren_load_font(const char *filename, float size); 23 void ren_free_font(RenFont *font); 24 void ren_set_font_tab_width(RenFont *font, int n); 25 int ren_get_font_width(RenFont *font, const char *text); 26 int ren_get_font_height(RenFont *font); 27
··· 1 #ifndef RENDERER_H 2 #define RENDERER_H 3 4 + #include <SDL3/SDL.h> 5 + #include <stdlib.h> 6 #include <stdint.h> 7 8 typedef struct RenImage RenImage; 9 typedef struct RenFont RenFont; 10 11 + typedef struct 12 + { 13 + uint8_t b, g, r, a; 14 + } RenColor; 15 + 16 + typedef struct 17 + { 18 + int x, y, width, height; 19 + } RenRect; 20 21 22 void ren_init(SDL_Window *win); ··· 30 RenFont* ren_load_font(const char *filename, float size); 31 void ren_free_font(RenFont *font); 32 void ren_set_font_tab_width(RenFont *font, int n); 33 + int ren_get_font_tab_width(RenFont *font); 34 int ren_get_font_width(RenFont *font, const char *text); 35 int ren_get_font_height(RenFont *font); 36