+16
.editorconfig
+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
-3
.github/FUNDING.yml
-19
LICENSE
-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
-40
README.md
···
1
-
# lite
2
-

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
+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
-1
build.sh
+3
-1
data/core/commands/doc.lua
+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
-1
data/plugins/language_python.lua
+4
-1
data/user/init.lua
+4
-1
data/user/init.lua
+41
doc/original-readme.md
+41
doc/original-readme.md
···
···
1
+
# lite
2
+

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
+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
+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
+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
+
{
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
+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
+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
+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
+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
+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
+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
+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
+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