Merge branch 'master' into plugin_api_h_fix
This commit is contained in:
+5
-2
@@ -68,8 +68,11 @@ static int f_pcre_match(lua_State *L) {
|
||||
int rc = pcre2_match(re, (PCRE2_SPTR)str, len, offset - 1, opts, md, NULL);
|
||||
if (rc < 0) {
|
||||
pcre2_match_data_free(md);
|
||||
if (rc != PCRE2_ERROR_NOMATCH)
|
||||
luaL_error(L, "regex matching error %d", rc);
|
||||
if (rc != PCRE2_ERROR_NOMATCH) {
|
||||
PCRE2_UCHAR buffer[120];
|
||||
pcre2_get_error_message(rc, buffer, sizeof(buffer));
|
||||
luaL_error(L, "regex matching error %d: %s", rc, buffer);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
PCRE2_SIZE* ovector = pcre2_get_ovector_pointer(md);
|
||||
|
||||
+24
-15
@@ -6,16 +6,18 @@ static int f_font_load(lua_State *L) {
|
||||
const char *filename = luaL_checkstring(L, 1);
|
||||
float size = luaL_checknumber(L, 2);
|
||||
unsigned int font_hinting = FONT_HINTING_SLIGHT, font_style = 0;
|
||||
bool subpixel = true;
|
||||
ERenFontAntialiasing font_antialiasing = FONT_ANTIALIASING_SUBPIXEL;
|
||||
if (lua_gettop(L) > 2 && lua_istable(L, 3)) {
|
||||
lua_getfield(L, 3, "antialiasing");
|
||||
if (lua_isstring(L, -1)) {
|
||||
const char *antialiasing = lua_tostring(L, -1);
|
||||
if (antialiasing) {
|
||||
if (strcmp(antialiasing, "grayscale") == 0) {
|
||||
subpixel = false;
|
||||
if (strcmp(antialiasing, "none") == 0) {
|
||||
font_antialiasing = FONT_ANTIALIASING_NONE;
|
||||
} else if (strcmp(antialiasing, "grayscale") == 0) {
|
||||
font_antialiasing = FONT_ANTIALIASING_GRAYSCALE;
|
||||
} else if (strcmp(antialiasing, "subpixel") == 0) {
|
||||
subpixel = true;
|
||||
font_antialiasing = FONT_ANTIALIASING_SUBPIXEL;
|
||||
} else {
|
||||
return luaL_error(L, "error in renderer.font.load, unknown antialiasing option: \"%s\"", antialiasing);
|
||||
}
|
||||
@@ -48,7 +50,7 @@ static int f_font_load(lua_State *L) {
|
||||
lua_pop(L, 5);
|
||||
}
|
||||
RenFont** font = lua_newuserdata(L, sizeof(RenFont*));
|
||||
*font = ren_font_load(filename, size, subpixel, font_hinting, font_style);
|
||||
*font = ren_font_load(filename, size, font_antialiasing, font_hinting, font_style);
|
||||
if (!*font)
|
||||
return luaL_error(L, "failed to load font");
|
||||
luaL_setmetatable(L, API_TYPE_FONT);
|
||||
@@ -174,23 +176,30 @@ static int f_end_frame(lua_State *L) {
|
||||
}
|
||||
|
||||
|
||||
static RenRect rect_to_grid(lua_Number x, lua_Number y, lua_Number w, lua_Number h) {
|
||||
int x1 = (int) (x + 0.5), y1 = (int) (y + 0.5);
|
||||
int x2 = (int) (x + w + 0.5), y2 = (int) (y + h + 0.5);
|
||||
return (RenRect) {x1, y1, x2 - x1, y2 - y1};
|
||||
}
|
||||
|
||||
|
||||
static int f_set_clip_rect(lua_State *L) {
|
||||
RenRect rect;
|
||||
rect.x = luaL_checknumber(L, 1);
|
||||
rect.y = luaL_checknumber(L, 2);
|
||||
rect.width = luaL_checknumber(L, 3);
|
||||
rect.height = luaL_checknumber(L, 4);
|
||||
lua_Number x = luaL_checknumber(L, 1);
|
||||
lua_Number y = luaL_checknumber(L, 2);
|
||||
lua_Number w = luaL_checknumber(L, 3);
|
||||
lua_Number h = luaL_checknumber(L, 4);
|
||||
RenRect rect = rect_to_grid(x, y, w, h);
|
||||
rencache_set_clip_rect(rect);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int f_draw_rect(lua_State *L) {
|
||||
RenRect rect;
|
||||
rect.x = luaL_checknumber(L, 1);
|
||||
rect.y = luaL_checknumber(L, 2);
|
||||
rect.width = luaL_checknumber(L, 3);
|
||||
rect.height = luaL_checknumber(L, 4);
|
||||
lua_Number x = luaL_checknumber(L, 1);
|
||||
lua_Number y = luaL_checknumber(L, 2);
|
||||
lua_Number w = luaL_checknumber(L, 3);
|
||||
lua_Number h = luaL_checknumber(L, 4);
|
||||
RenRect rect = rect_to_grid(x, y, w, h);
|
||||
RenColor color = checkcolor(L, 5, 255);
|
||||
rencache_draw_rect(rect, color);
|
||||
return 0;
|
||||
|
||||
+177
-45
@@ -6,11 +6,14 @@
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
#include "api.h"
|
||||
#include "dirmonitor.h"
|
||||
#include "rencache.h"
|
||||
#ifdef _WIN32
|
||||
#include <direct.h>
|
||||
#include <windows.h>
|
||||
#include <fileapi.h>
|
||||
#elif __linux__
|
||||
#include <sys/vfs.h>
|
||||
#endif
|
||||
|
||||
extern SDL_Window *window;
|
||||
@@ -18,9 +21,11 @@ extern SDL_Window *window;
|
||||
|
||||
static const char* button_name(int button) {
|
||||
switch (button) {
|
||||
case 1 : return "left";
|
||||
case 2 : return "middle";
|
||||
case 3 : return "right";
|
||||
case SDL_BUTTON_LEFT : return "left";
|
||||
case SDL_BUTTON_MIDDLE : return "middle";
|
||||
case SDL_BUTTON_RIGHT : return "right";
|
||||
case SDL_BUTTON_X1 : return "x";
|
||||
case SDL_BUTTON_X2 : return "y";
|
||||
default : return "?";
|
||||
}
|
||||
}
|
||||
@@ -236,6 +241,26 @@ top:
|
||||
lua_pushnumber(L, e.wheel.y);
|
||||
return 2;
|
||||
|
||||
case SDL_USEREVENT:
|
||||
lua_pushstring(L, "dirchange");
|
||||
lua_pushnumber(L, e.user.code >> 16);
|
||||
switch (e.user.code & 0xffff) {
|
||||
case DMON_ACTION_DELETE:
|
||||
lua_pushstring(L, "delete");
|
||||
break;
|
||||
case DMON_ACTION_CREATE:
|
||||
lua_pushstring(L, "create");
|
||||
break;
|
||||
case DMON_ACTION_MODIFY:
|
||||
lua_pushstring(L, "modify");
|
||||
break;
|
||||
default:
|
||||
return luaL_error(L, "unknown dmon event action: %d", e.user.code & 0xffff);
|
||||
}
|
||||
lua_pushstring(L, e.user.data1);
|
||||
free(e.user.data1);
|
||||
return 4;
|
||||
|
||||
default:
|
||||
goto top;
|
||||
}
|
||||
@@ -524,6 +549,45 @@ static int f_get_file_info(lua_State *L) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if __linux__
|
||||
// https://man7.org/linux/man-pages/man2/statfs.2.html
|
||||
|
||||
struct f_type_names {
|
||||
uint32_t magic;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
static struct f_type_names fs_names[] = {
|
||||
{ 0xef53, "ext2/ext3" },
|
||||
{ 0x6969, "nfs" },
|
||||
{ 0x65735546, "fuse" },
|
||||
{ 0x517b, "smb" },
|
||||
{ 0xfe534d42, "smb2" },
|
||||
{ 0x52654973, "reiserfs" },
|
||||
{ 0x01021994, "tmpfs" },
|
||||
{ 0x858458f6, "ramfs" },
|
||||
{ 0x5346544e, "ntfs" },
|
||||
{ 0x0, NULL },
|
||||
};
|
||||
|
||||
static int f_get_fs_type(lua_State *L) {
|
||||
const char *path = luaL_checkstring(L, 1);
|
||||
struct statfs buf;
|
||||
int status = statfs(path, &buf);
|
||||
if (status != 0) {
|
||||
return luaL_error(L, "error calling statfs on %s", path);
|
||||
}
|
||||
for (int i = 0; fs_names[i].magic; i++) {
|
||||
if (fs_names[i].magic == buf.f_type) {
|
||||
lua_pushstring(L, fs_names[i].name);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
lua_pushstring(L, "unknown");
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static int f_mkdir(lua_State *L) {
|
||||
const char *path = luaL_checkstring(L, 1);
|
||||
@@ -591,56 +655,32 @@ static int f_exec(lua_State *L) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int f_fuzzy_match(lua_State *L) {
|
||||
size_t strLen, ptnLen;
|
||||
const char *str = luaL_checklstring(L, 1, &strLen);
|
||||
const char *ptn = luaL_checklstring(L, 2, &ptnLen);
|
||||
bool files = false;
|
||||
if (lua_gettop(L) > 2 && lua_isboolean(L,3))
|
||||
files = lua_toboolean(L, 3);
|
||||
|
||||
int score = 0;
|
||||
int run = 0;
|
||||
|
||||
// Match things *backwards*. This allows for better matching on filenames than the above
|
||||
// If true match things *backwards*. This allows for better matching on filenames than the above
|
||||
// function. For example, in the lite project, opening "renderer" has lib/font_render/build.sh
|
||||
// as the first result, rather than src/renderer.c. Clearly that's wrong.
|
||||
if (files) {
|
||||
const char* strEnd = str + strLen - 1;
|
||||
const char* ptnEnd = ptn + ptnLen - 1;
|
||||
while (strEnd >= str && ptnEnd >= ptn) {
|
||||
while (*strEnd == ' ') { strEnd--; }
|
||||
while (*ptnEnd == ' ') { ptnEnd--; }
|
||||
if (tolower(*strEnd) == tolower(*ptnEnd)) {
|
||||
score += run * 10 - (*strEnd != *ptnEnd);
|
||||
run++;
|
||||
ptnEnd--;
|
||||
} else {
|
||||
score -= 10;
|
||||
run = 0;
|
||||
}
|
||||
strEnd--;
|
||||
bool files = lua_gettop(L) > 2 && lua_isboolean(L,3) && lua_toboolean(L, 3);
|
||||
int score = 0, run = 0, increment = files ? -1 : 1;
|
||||
const char* strTarget = files ? str + strLen - 1 : str;
|
||||
const char* ptnTarget = files ? ptn + ptnLen - 1 : ptn;
|
||||
while (strTarget >= str && ptnTarget >= ptn && *strTarget && *ptnTarget) {
|
||||
while (strTarget >= str && *strTarget == ' ') { strTarget += increment; }
|
||||
while (ptnTarget >= ptn && *ptnTarget == ' ') { ptnTarget += increment; }
|
||||
if (tolower(*strTarget) == tolower(*ptnTarget)) {
|
||||
score += run * 10 - (*strTarget != *ptnTarget);
|
||||
run++;
|
||||
ptnTarget += increment;
|
||||
} else {
|
||||
score -= 10;
|
||||
run = 0;
|
||||
}
|
||||
if (ptnEnd >= ptn) { return 0; }
|
||||
} else {
|
||||
while (*str && *ptn) {
|
||||
while (*str == ' ') { str++; }
|
||||
while (*ptn == ' ') { ptn++; }
|
||||
if (tolower(*str) == tolower(*ptn)) {
|
||||
score += run * 10 - (*str != *ptn);
|
||||
run++;
|
||||
ptn++;
|
||||
} else {
|
||||
score -= 10;
|
||||
run = 0;
|
||||
}
|
||||
str++;
|
||||
}
|
||||
if (*ptn) { return 0; }
|
||||
strTarget += increment;
|
||||
}
|
||||
|
||||
lua_pushnumber(L, score - (int)strLen);
|
||||
if (ptnTarget >= ptn && *ptnTarget) { return 0; }
|
||||
lua_pushnumber(L, score - (int)strLen * 10);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -738,6 +778,91 @@ static int f_load_native_plugin(lua_State *L) {
|
||||
return result;
|
||||
}
|
||||
|
||||
static int f_watch_dir(lua_State *L) {
|
||||
const char *path = luaL_checkstring(L, 1);
|
||||
const int recursive = lua_toboolean(L, 2);
|
||||
uint32_t dmon_flags = (recursive ? DMON_WATCHFLAGS_RECURSIVE : 0);
|
||||
dmon_watch_id watch_id = dmon_watch(path, dirmonitor_watch_callback, dmon_flags, NULL);
|
||||
if (watch_id.id == 0) { luaL_error(L, "directory monitoring watch failed"); }
|
||||
lua_pushnumber(L, watch_id.id);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if __linux__
|
||||
static int f_watch_dir_add(lua_State *L) {
|
||||
dmon_watch_id watch_id;
|
||||
watch_id.id = luaL_checkinteger(L, 1);
|
||||
const char *subdir = luaL_checkstring(L, 2);
|
||||
lua_pushboolean(L, dmon_watch_add(watch_id, subdir));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int f_watch_dir_rm(lua_State *L) {
|
||||
dmon_watch_id watch_id;
|
||||
watch_id.id = luaL_checkinteger(L, 1);
|
||||
const char *subdir = luaL_checkstring(L, 2);
|
||||
lua_pushboolean(L, dmon_watch_rm(watch_id, subdir));
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef _WIN32
|
||||
#define PATHSEP '\\'
|
||||
#else
|
||||
#define PATHSEP '/'
|
||||
#endif
|
||||
|
||||
/* Special purpose filepath compare function. Corresponds to the
|
||||
order used in the TreeView view of the project's files. Returns true iff
|
||||
path1 < path2 in the TreeView order. */
|
||||
static int f_path_compare(lua_State *L) {
|
||||
const char *path1 = luaL_checkstring(L, 1);
|
||||
const char *type1_s = luaL_checkstring(L, 2);
|
||||
const char *path2 = luaL_checkstring(L, 3);
|
||||
const char *type2_s = luaL_checkstring(L, 4);
|
||||
const int len1 = strlen(path1), len2 = strlen(path2);
|
||||
int type1 = strcmp(type1_s, "dir") != 0;
|
||||
int type2 = strcmp(type2_s, "dir") != 0;
|
||||
/* Find the index of the common part of the path. */
|
||||
int offset = 0, i;
|
||||
for (i = 0; i < len1 && i < len2; i++) {
|
||||
if (path1[i] != path2[i]) break;
|
||||
if (path1[i] == PATHSEP) {
|
||||
offset = i + 1;
|
||||
}
|
||||
}
|
||||
/* If a path separator is present in the name after the common part we consider
|
||||
the entry like a directory. */
|
||||
if (strchr(path1 + offset, PATHSEP)) {
|
||||
type1 = 0;
|
||||
}
|
||||
if (strchr(path2 + offset, PATHSEP)) {
|
||||
type2 = 0;
|
||||
}
|
||||
/* If types are different "dir" types comes before "file" types. */
|
||||
if (type1 != type2) {
|
||||
lua_pushboolean(L, type1 < type2);
|
||||
return 1;
|
||||
}
|
||||
/* If types are the same compare the files' path alphabetically. */
|
||||
int cfr = 0;
|
||||
int len_min = (len1 < len2 ? len1 : len2);
|
||||
for (int j = offset; j <= len_min; j++) {
|
||||
if (path1[j] == path2[j]) continue;
|
||||
if (path1[j] == 0 || path2[j] == 0) {
|
||||
cfr = (path1[j] == 0);
|
||||
} else if (path1[j] == PATHSEP || path2[j] == PATHSEP) {
|
||||
/* For comparison we treat PATHSEP as if it was the string terminator. */
|
||||
cfr = (path1[j] == PATHSEP);
|
||||
} else {
|
||||
cfr = (path1[j] < path2[j]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
lua_pushboolean(L, cfr);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static const luaL_Reg lib[] = {
|
||||
{ "poll_event", f_poll_event },
|
||||
@@ -766,6 +891,13 @@ static const luaL_Reg lib[] = {
|
||||
{ "fuzzy_match", f_fuzzy_match },
|
||||
{ "set_window_opacity", f_set_window_opacity },
|
||||
{ "load_native_plugin", f_load_native_plugin },
|
||||
{ "watch_dir", f_watch_dir },
|
||||
{ "path_compare", f_path_compare },
|
||||
#if __linux__
|
||||
{ "watch_dir_add", f_watch_dir_add },
|
||||
{ "watch_dir_rm", f_watch_dir_rm },
|
||||
{ "get_fs_type", f_get_fs_type },
|
||||
#endif
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <SDL.h>
|
||||
|
||||
#define DMON_IMPL
|
||||
#include "dmon.h"
|
||||
#include "dmon_extra.h"
|
||||
|
||||
#include "dirmonitor.h"
|
||||
|
||||
static void send_sdl_event(dmon_watch_id watch_id, dmon_action action, const char *filepath) {
|
||||
SDL_Event ev;
|
||||
const int size = strlen(filepath) + 1;
|
||||
/* The string allocated below should be deallocated as soon as the event is
|
||||
treated in the SDL main loop. */
|
||||
char *new_filepath = malloc(size);
|
||||
if (!new_filepath) return;
|
||||
memcpy(new_filepath, filepath, size);
|
||||
#ifdef _WIN32
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (new_filepath[i] == '/') {
|
||||
new_filepath[i] = '\\';
|
||||
}
|
||||
}
|
||||
#endif
|
||||
SDL_zero(ev);
|
||||
ev.type = SDL_USEREVENT;
|
||||
ev.user.code = ((watch_id.id & 0xffff) << 16) | (action & 0xffff);
|
||||
ev.user.data1 = new_filepath;
|
||||
SDL_PushEvent(&ev);
|
||||
}
|
||||
|
||||
void dirmonitor_init() {
|
||||
dmon_init();
|
||||
/* In theory we should register our user event but since we
|
||||
have just one type of user event this is not really needed. */
|
||||
/* sdl_dmon_event_type = SDL_RegisterEvents(1); */
|
||||
}
|
||||
|
||||
void dirmonitor_deinit() {
|
||||
dmon_deinit();
|
||||
}
|
||||
|
||||
void dirmonitor_watch_callback(dmon_watch_id watch_id, dmon_action action, const char *rootdir,
|
||||
const char *filepath, const char *oldfilepath, void *user)
|
||||
{
|
||||
(void) rootdir;
|
||||
(void) user;
|
||||
switch (action) {
|
||||
case DMON_ACTION_MOVE:
|
||||
send_sdl_event(watch_id, DMON_ACTION_DELETE, oldfilepath);
|
||||
send_sdl_event(watch_id, DMON_ACTION_CREATE, filepath);
|
||||
break;
|
||||
default:
|
||||
send_sdl_event(watch_id, action, filepath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
#ifndef DIRMONITOR_H
|
||||
#define DIRMONITOR_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#include "dmon.h"
|
||||
#include "dmon_extra.h"
|
||||
|
||||
void dirmonitor_init();
|
||||
void dirmonitor_deinit();
|
||||
void dirmonitor_watch_callback(dmon_watch_id watch_id, dmon_action action, const char *rootdir,
|
||||
const char *filepath, const char *oldfilepath, void *user);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#include <mach-o/dyld.h>
|
||||
#endif
|
||||
|
||||
#include "dirmonitor.h"
|
||||
|
||||
|
||||
SDL_Window *window;
|
||||
|
||||
@@ -106,6 +108,8 @@ int main(int argc, char **argv) {
|
||||
SDL_DisplayMode dm;
|
||||
SDL_GetCurrentDisplayMode(0, &dm);
|
||||
|
||||
dirmonitor_init();
|
||||
|
||||
window = SDL_CreateWindow(
|
||||
"", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, dm.w * 0.8, dm.h * 0.8,
|
||||
SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN);
|
||||
@@ -188,6 +192,7 @@ init_lua:
|
||||
|
||||
lua_close(L);
|
||||
ren_free_window_resources();
|
||||
dirmonitor_deinit();
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
+3
-2
@@ -4,6 +4,7 @@ lite_sources = [
|
||||
'api/regex.c',
|
||||
'api/system.c',
|
||||
'api/process.c',
|
||||
'dirmonitor.c',
|
||||
'renderer.c',
|
||||
'renwindow.c',
|
||||
'rencache.c',
|
||||
@@ -18,11 +19,11 @@ elif host_machine.system() == 'darwin'
|
||||
lite_sources += 'bundle_open.m'
|
||||
endif
|
||||
|
||||
lite_include = include_directories('.')
|
||||
lite_includes += include_directories('.')
|
||||
|
||||
executable('lite-xl',
|
||||
lite_sources + lite_rc,
|
||||
include_directories: [lite_include],
|
||||
include_directories: lite_includes,
|
||||
dependencies: lite_deps,
|
||||
c_args: lite_cargs,
|
||||
objc_args: lite_cargs,
|
||||
|
||||
+3
-1
@@ -123,7 +123,9 @@ void rencache_set_clip_rect(RenRect rect) {
|
||||
|
||||
|
||||
void rencache_draw_rect(RenRect rect, RenColor color) {
|
||||
if (!rects_overlap(screen_rect, rect)) { return; }
|
||||
if (!rects_overlap(screen_rect, rect) || rect.width == 0 || rect.height == 0) {
|
||||
return;
|
||||
}
|
||||
Command *cmd = push_command(DRAW_RECT, COMMAND_BARE_SIZE);
|
||||
if (cmd) {
|
||||
cmd->rect = rect;
|
||||
|
||||
+36
-25
@@ -43,7 +43,7 @@ typedef struct RenFont {
|
||||
GlyphSet* sets[SUBPIXEL_BITMAPS_CACHED][MAX_LOADABLE_GLYPHSETS];
|
||||
float size, space_advance, tab_advance;
|
||||
short max_height;
|
||||
bool subpixel;
|
||||
ERenFontAntialiasing antialiasing;
|
||||
ERenFontHinting hinting;
|
||||
unsigned char style;
|
||||
char path[0];
|
||||
@@ -66,16 +66,16 @@ static const char* utf8_to_codepoint(const char *p, unsigned *dst) {
|
||||
}
|
||||
|
||||
static int font_set_load_options(RenFont* font) {
|
||||
switch (font->hinting) {
|
||||
case FONT_HINTING_SLIGHT: return FT_LOAD_TARGET_LIGHT | FT_LOAD_FORCE_AUTOHINT;
|
||||
case FONT_HINTING_FULL: return FT_LOAD_TARGET_NORMAL | FT_LOAD_FORCE_AUTOHINT;
|
||||
case FONT_HINTING_NONE: return FT_LOAD_TARGET_NORMAL | FT_LOAD_NO_HINTING;
|
||||
}
|
||||
return FT_LOAD_TARGET_NORMAL | FT_LOAD_NO_HINTING;
|
||||
int load_target = font->antialiasing == FONT_ANTIALIASING_NONE ? FT_LOAD_TARGET_MONO
|
||||
: (font->hinting == FONT_HINTING_SLIGHT ? FT_LOAD_TARGET_LIGHT : FT_LOAD_TARGET_NORMAL);
|
||||
int hinting = font->hinting == FONT_HINTING_NONE ? FT_LOAD_NO_HINTING : FT_LOAD_FORCE_AUTOHINT;
|
||||
return load_target | hinting;
|
||||
}
|
||||
|
||||
static int font_set_render_options(RenFont* font) {
|
||||
if (font->subpixel) {
|
||||
if (font->antialiasing == FONT_ANTIALIASING_NONE)
|
||||
return FT_RENDER_MODE_MONO;
|
||||
if (font->antialiasing == FONT_ANTIALIASING_SUBPIXEL) {
|
||||
unsigned char weights[] = { 0x10, 0x40, 0x70, 0x40, 0x10 } ;
|
||||
switch (font->hinting) {
|
||||
case FONT_HINTING_NONE: FT_Library_SetLcdFilter(library, FT_LCD_FILTER_NONE); break;
|
||||
@@ -106,8 +106,8 @@ static int font_set_style(FT_Outline* outline, int x_translation, unsigned char
|
||||
|
||||
static void font_load_glyphset(RenFont* font, int idx) {
|
||||
unsigned int render_option = font_set_render_options(font), load_option = font_set_load_options(font);
|
||||
int bitmaps_cached = font->subpixel ? SUBPIXEL_BITMAPS_CACHED : 1;
|
||||
unsigned int byte_width = font->subpixel ? 3 : 1;
|
||||
int bitmaps_cached = font->antialiasing == FONT_ANTIALIASING_SUBPIXEL ? SUBPIXEL_BITMAPS_CACHED : 1;
|
||||
unsigned int byte_width = font->antialiasing == FONT_ANTIALIASING_SUBPIXEL ? 3 : 1;
|
||||
for (int j = 0, pen_x = 0; j < bitmaps_cached; ++j) {
|
||||
GlyphSet* set = check_alloc(calloc(1, sizeof(GlyphSet)));
|
||||
font->sets[j][idx] = set;
|
||||
@@ -117,13 +117,15 @@ static void font_load_glyphset(RenFont* font, int idx) {
|
||||
continue;
|
||||
FT_GlyphSlot slot = font->face->glyph;
|
||||
int glyph_width = slot->bitmap.width / byte_width;
|
||||
if (font->antialiasing == FONT_ANTIALIASING_NONE)
|
||||
glyph_width *= 8;
|
||||
set->metrics[i] = (GlyphMetric){ pen_x, pen_x + glyph_width, 0, slot->bitmap.rows, true, slot->bitmap_left, slot->bitmap_top, (slot->advance.x + slot->lsb_delta - slot->rsb_delta) / 64.0f};
|
||||
pen_x += glyph_width;
|
||||
font->max_height = slot->bitmap.rows > font->max_height ? slot->bitmap.rows : font->max_height;
|
||||
}
|
||||
if (pen_x == 0)
|
||||
continue;
|
||||
set->surface = check_alloc(SDL_CreateRGBSurface(0, pen_x, font->max_height, font->subpixel ? 24 : 8, 0, 0, 0, 0));
|
||||
set->surface = check_alloc(SDL_CreateRGBSurface(0, pen_x, font->max_height, font->antialiasing == FONT_ANTIALIASING_SUBPIXEL ? 24 : 8, 0, 0, 0, 0));
|
||||
unsigned char* pixels = set->surface->pixels;
|
||||
for (int i = 0; i < MAX_GLYPHSET; ++i) {
|
||||
int glyph_index = FT_Get_Char_Index(font->face, i + idx * MAX_GLYPHSET);
|
||||
@@ -136,7 +138,14 @@ static void font_load_glyphset(RenFont* font, int idx) {
|
||||
for (int line = 0; line < slot->bitmap.rows; ++line) {
|
||||
int target_offset = set->surface->pitch * line + set->metrics[i].x0 * byte_width;
|
||||
int source_offset = line * slot->bitmap.pitch;
|
||||
memcpy(&pixels[target_offset], &slot->bitmap.buffer[source_offset], slot->bitmap.width);
|
||||
if (font->antialiasing == FONT_ANTIALIASING_NONE) {
|
||||
for (int column = 0; column < slot->bitmap.width; ++column) {
|
||||
int current_source_offset = source_offset + (column / 8);
|
||||
int source_pixel = slot->bitmap.buffer[current_source_offset];
|
||||
pixels[++target_offset] = ((source_pixel >> (7 - (column % 8))) & 0x1) << 7;
|
||||
}
|
||||
} else
|
||||
memcpy(&pixels[target_offset], &slot->bitmap.buffer[source_offset], slot->bitmap.width);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -144,9 +153,9 @@ static void font_load_glyphset(RenFont* font, int idx) {
|
||||
|
||||
static GlyphSet* font_get_glyphset(RenFont* font, unsigned int codepoint, int subpixel_idx) {
|
||||
int idx = (codepoint >> 8) % MAX_LOADABLE_GLYPHSETS;
|
||||
if (!font->sets[font->subpixel ? subpixel_idx : 0][idx])
|
||||
if (!font->sets[font->antialiasing == FONT_ANTIALIASING_SUBPIXEL ? subpixel_idx : 0][idx])
|
||||
font_load_glyphset(font, idx);
|
||||
return font->sets[font->subpixel ? subpixel_idx : 0][idx];
|
||||
return font->sets[font->antialiasing == FONT_ANTIALIASING_SUBPIXEL ? subpixel_idx : 0][idx];
|
||||
}
|
||||
|
||||
static RenFont* font_group_get_glyph(GlyphSet** set, GlyphMetric** metric, RenFont** fonts, unsigned int codepoint, int bitmap_index) {
|
||||
@@ -163,7 +172,7 @@ static RenFont* font_group_get_glyph(GlyphSet** set, GlyphMetric** metric, RenFo
|
||||
return fonts[0];
|
||||
}
|
||||
|
||||
RenFont* ren_font_load(const char* path, float size, bool subpixel, unsigned char hinting, unsigned char style) {
|
||||
RenFont* ren_font_load(const char* path, float size, ERenFontAntialiasing antialiasing, ERenFontHinting hinting, unsigned char style) {
|
||||
FT_Face face;
|
||||
if (FT_New_Face( library, path, 0, &face))
|
||||
return NULL;
|
||||
@@ -175,7 +184,7 @@ RenFont* ren_font_load(const char* path, float size, bool subpixel, unsigned cha
|
||||
strcpy(font->path, path);
|
||||
font->face = face;
|
||||
font->size = size;
|
||||
font->subpixel = subpixel;
|
||||
font->antialiasing = antialiasing;
|
||||
font->hinting = hinting;
|
||||
font->style = style;
|
||||
font->space_advance = (int)font_get_glyphset(font, ' ', 0)->metrics[' '].xadvance;
|
||||
@@ -187,7 +196,7 @@ RenFont* ren_font_load(const char* path, float size, bool subpixel, unsigned cha
|
||||
}
|
||||
|
||||
RenFont* ren_font_copy(RenFont* font, float size) {
|
||||
return ren_font_load(font->path, size, font->subpixel, font->hinting, font->style);
|
||||
return ren_font_load(font->path, size, font->antialiasing, font->hinting, font->style);
|
||||
}
|
||||
|
||||
void ren_font_free(RenFont* font) {
|
||||
@@ -206,7 +215,7 @@ void ren_font_free(RenFont* font) {
|
||||
|
||||
void ren_font_group_set_tab_size(RenFont **fonts, int n) {
|
||||
for (int j = 0; j < FONT_FALLBACK_MAX && fonts[j]; ++j) {
|
||||
for (int i = 0; i < (fonts[j]->subpixel ? SUBPIXEL_BITMAPS_CACHED : 1); ++i)
|
||||
for (int i = 0; i < (fonts[j]->antialiasing == FONT_ANTIALIASING_SUBPIXEL ? SUBPIXEL_BITMAPS_CACHED : 1); ++i)
|
||||
font_get_glyphset(fonts[j], '\t', i)->metrics['\t'].xadvance = fonts[j]->space_advance * n;
|
||||
}
|
||||
}
|
||||
@@ -247,6 +256,7 @@ float ren_draw_text(RenFont **fonts, const char *text, float x, int y, RenColor
|
||||
const char* end = text + strlen(text);
|
||||
unsigned char* destination_pixels = surface->pixels;
|
||||
int clip_end_x = clip.x + clip.width, clip_end_y = clip.y + clip.height;
|
||||
|
||||
while (text < end) {
|
||||
unsigned int codepoint, r, g, b;
|
||||
text = utf8_to_codepoint(text, &codepoint);
|
||||
@@ -273,15 +283,15 @@ float ren_draw_text(RenFont **fonts, const char *text, float x, int y, RenColor
|
||||
glyph_start += offset;
|
||||
}
|
||||
unsigned int* destination_pixel = (unsigned int*)&destination_pixels[surface->pitch * target_y + start_x * bytes_per_pixel];
|
||||
unsigned char* source_pixel = &source_pixels[line * set->surface->pitch + glyph_start * (font->subpixel ? 3 : 1)];
|
||||
unsigned char* source_pixel = &source_pixels[line * set->surface->pitch + glyph_start * (font->antialiasing == FONT_ANTIALIASING_SUBPIXEL ? 3 : 1)];
|
||||
for (int x = glyph_start; x < glyph_end; ++x) {
|
||||
unsigned int destination_color = *destination_pixel;
|
||||
SDL_Color dst = { (destination_color >> 16) & 0xFF, (destination_color >> 8) & 0xFF, (destination_color >> 0) & 0xFF, (destination_color >> 24) & 0xFF };
|
||||
SDL_Color src = { *(font->subpixel ? source_pixel++ : source_pixel), *(font->subpixel ? source_pixel++ : source_pixel), *source_pixel++ };
|
||||
SDL_Color dst = { (destination_color & surface->format->Rmask) >> surface->format->Rshift, (destination_color & surface->format->Gmask) >> surface->format->Gshift, (destination_color & surface->format->Bmask) >> surface->format->Bshift, (destination_color & surface->format->Amask) >> surface->format->Ashift };
|
||||
SDL_Color src = { *(font->antialiasing == FONT_ANTIALIASING_SUBPIXEL ? source_pixel++ : source_pixel), *(font->antialiasing == FONT_ANTIALIASING_SUBPIXEL ? source_pixel++ : source_pixel), *source_pixel++ };
|
||||
r = (color.r * src.r * color.a + dst.r * (65025 - src.r * color.a) + 32767) / 65025;
|
||||
g = (color.g * src.g * color.a + dst.g * (65025 - src.g * color.a) + 32767) / 65025;
|
||||
b = (color.b * src.b * color.a + dst.b * (65025 - src.b * color.a) + 32767) / 65025;
|
||||
*destination_pixel++ = dst.a << 24 | r << 16 | g << 8 | b;
|
||||
*destination_pixel++ = dst.a << surface->format->Ashift | r << surface->format->Rshift | g << surface->format->Gshift | b << surface->format->Bshift;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -324,14 +334,15 @@ void ren_draw_rect(RenRect rect, RenColor color) {
|
||||
RenColor *d = (RenColor*) surface->pixels;
|
||||
d += x1 + y1 * surface->w;
|
||||
int dr = surface->w - (x2 - x1);
|
||||
|
||||
unsigned int translated = SDL_MapRGB(surface->format, color.r, color.g, color.b);
|
||||
if (color.a == 0xff) {
|
||||
SDL_Rect rect = { x1, y1, x2 - x1, y2 - y1 };
|
||||
SDL_FillRect(surface, &rect, SDL_MapRGBA(surface->format, color.r, color.g, color.b, color.a));
|
||||
SDL_FillRect(surface, &rect, translated);
|
||||
} else {
|
||||
RenColor translated_color = (RenColor){ translated & 0xFF, (translated >> 8) & 0xFF, (translated >> 16) & 0xFF, color.a };
|
||||
for (int j = y1; j < y2; j++) {
|
||||
for (int i = x1; i < x2; i++, d++)
|
||||
*d = blend_pixel(*d, color);
|
||||
*d = blend_pixel(*d, translated_color);
|
||||
d += dr;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -8,11 +8,12 @@
|
||||
#define FONT_FALLBACK_MAX 4
|
||||
typedef struct RenFont RenFont;
|
||||
typedef enum { FONT_HINTING_NONE, FONT_HINTING_SLIGHT, FONT_HINTING_FULL } ERenFontHinting;
|
||||
typedef enum { FONT_ANTIALIASING_NONE, FONT_ANTIALIASING_GRAYSCALE, FONT_ANTIALIASING_SUBPIXEL } ERenFontAntialiasing;
|
||||
typedef enum { FONT_STYLE_BOLD = 1, FONT_STYLE_ITALIC = 2, FONT_STYLE_UNDERLINE = 4 } ERenFontStyle;
|
||||
typedef struct { uint8_t b, g, r, a; } RenColor;
|
||||
typedef struct { int x, y, width, height; } RenRect;
|
||||
|
||||
RenFont* ren_font_load(const char *filename, float size, bool subpixel, unsigned char hinting, unsigned char style);
|
||||
RenFont* ren_font_load(const char *filename, float size, ERenFontAntialiasing antialiasing, ERenFontHinting hinting, unsigned char style);
|
||||
RenFont* ren_font_copy(RenFont* font, float size);
|
||||
void ren_font_free(RenFont *font);
|
||||
int ren_font_group_get_tab_size(RenFont **font);
|
||||
|
||||
Reference in New Issue
Block a user