Merge pull request #521 from adamharrison/remove-font-renderer
Remove Font Renderer + CP Replace + libagg
This commit is contained in:
+1
-3
@@ -6,7 +6,6 @@ int luaopen_renderer(lua_State *L);
|
||||
int luaopen_regex(lua_State *L);
|
||||
int luaopen_process(lua_State *L);
|
||||
|
||||
|
||||
static const luaL_Reg libs[] = {
|
||||
{ "system", luaopen_system },
|
||||
{ "renderer", luaopen_renderer },
|
||||
@@ -16,7 +15,6 @@ static const luaL_Reg libs[] = {
|
||||
};
|
||||
|
||||
void api_load_libs(lua_State *L) {
|
||||
for (int i = 0; libs[i].name; i++) {
|
||||
for (int i = 0; libs[i].name; i++)
|
||||
luaL_requiref(L, libs[i].name, libs[i].func, 1);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-4
@@ -1,12 +1,11 @@
|
||||
#ifndef API_H
|
||||
#define API_H
|
||||
|
||||
#include "lua.h"
|
||||
#include "lauxlib.h"
|
||||
#include "lualib.h"
|
||||
#include <lua.h>
|
||||
#include <lauxlib.h>
|
||||
#include <lualib.h>
|
||||
|
||||
#define API_TYPE_FONT "Font"
|
||||
#define API_TYPE_REPLACE "Replace"
|
||||
#define API_TYPE_PROCESS "Process"
|
||||
|
||||
void api_load_libs(lua_State *L);
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
#include "api.h"
|
||||
#include "renderer.h"
|
||||
|
||||
|
||||
static int f_new(lua_State *L) {
|
||||
CPReplaceTable *rep_table = lua_newuserdata(L, sizeof(CPReplaceTable));
|
||||
luaL_setmetatable(L, API_TYPE_REPLACE);
|
||||
ren_cp_replace_init(rep_table);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int f_gc(lua_State *L) {
|
||||
CPReplaceTable *rep_table = luaL_checkudata(L, 1, API_TYPE_REPLACE);
|
||||
ren_cp_replace_free(rep_table);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int f_add(lua_State *L) {
|
||||
CPReplaceTable *rep_table = luaL_checkudata(L, 1, API_TYPE_REPLACE);
|
||||
const char *src = luaL_checkstring(L, 2);
|
||||
const char *dst = luaL_checkstring(L, 3);
|
||||
ren_cp_replace_add(rep_table, src, dst);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const luaL_Reg lib[] = {
|
||||
{ "__gc", f_gc },
|
||||
{ "new", f_new },
|
||||
{ "add", f_add },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
int luaopen_renderer_replacements(lua_State *L) {
|
||||
luaL_newmetatable(L, API_TYPE_REPLACE);
|
||||
luaL_setfuncs(L, lib, 0);
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setfield(L, -2, "__index");
|
||||
return 1;
|
||||
}
|
||||
+113
-35
@@ -2,6 +2,100 @@
|
||||
#include "renderer.h"
|
||||
#include "rencache.h"
|
||||
|
||||
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;
|
||||
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;
|
||||
} else if (strcmp(antialiasing, "subpixel") == 0) {
|
||||
subpixel = true;
|
||||
} else {
|
||||
return luaL_error(L, "error in renderer.font.load, unknown antialiasing option: \"%s\"", antialiasing);
|
||||
}
|
||||
}
|
||||
}
|
||||
lua_getfield(L, 3, "hinting");
|
||||
if (lua_isstring(L, -1)) {
|
||||
const char *hinting = lua_tostring(L, -1);
|
||||
if (hinting) {
|
||||
if (strcmp(hinting, "slight") == 0) {
|
||||
font_hinting = FONT_HINTING_SLIGHT;
|
||||
} else if (strcmp(hinting, "none") == 0) {
|
||||
font_hinting = FONT_HINTING_NONE;
|
||||
} else if (strcmp(hinting, "full") == 0) {
|
||||
font_hinting = FONT_HINTING_FULL;
|
||||
} else {
|
||||
return luaL_error(L, "error in renderer.font.load, unknown hinting option: \"%s\"", hinting);
|
||||
}
|
||||
}
|
||||
}
|
||||
lua_getfield(L, 3, "italic");
|
||||
if (lua_toboolean(L, -1))
|
||||
font_style |= FONT_STYLE_ITALIC;
|
||||
lua_getfield(L, 3, "bold");
|
||||
if (lua_toboolean(L, -1))
|
||||
font_style |= FONT_STYLE_BOLD;
|
||||
lua_getfield(L, 3, "underline");
|
||||
if (lua_toboolean(L, -1))
|
||||
font_style |= FONT_STYLE_UNDERLINE;
|
||||
lua_pop(L, 5);
|
||||
}
|
||||
RenFont** font = lua_newuserdata(L, sizeof(RenFont*));
|
||||
*font = ren_font_load(filename, size, subpixel, font_hinting, font_style);
|
||||
if (!*font)
|
||||
return luaL_error(L, "failed to load font");
|
||||
luaL_setmetatable(L, API_TYPE_FONT);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int f_font_copy(lua_State *L) {
|
||||
RenFont** self = luaL_checkudata(L, 1, API_TYPE_FONT);
|
||||
float size = lua_gettop(L) >= 2 ? luaL_checknumber(L, 2) : ren_font_get_height(*self);
|
||||
RenFont** font = lua_newuserdata(L, sizeof(RenFont*));
|
||||
*font = ren_font_copy(*self, size);
|
||||
if (!*font)
|
||||
return luaL_error(L, "failed to copy font");
|
||||
luaL_setmetatable(L, API_TYPE_FONT);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int f_font_set_tab_size(lua_State *L) {
|
||||
RenFont** self = luaL_checkudata(L, 1, API_TYPE_FONT);
|
||||
int n = luaL_checknumber(L, 2);
|
||||
ren_font_set_tab_size(*self, n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int f_font_gc(lua_State *L) {
|
||||
RenFont** self = luaL_checkudata(L, 1, API_TYPE_FONT);
|
||||
ren_font_free(*self);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int f_font_get_width(lua_State *L) {
|
||||
RenFont** self = luaL_checkudata(L, 1, API_TYPE_FONT);
|
||||
lua_pushnumber(L, ren_font_get_width(*self, luaL_checkstring(L, 2)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int f_font_get_height(lua_State *L) {
|
||||
RenFont** self = luaL_checkudata(L, 1, API_TYPE_FONT);
|
||||
lua_pushnumber(L, ren_font_get_height(*self));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int f_font_get_size(lua_State *L) {
|
||||
RenFont** self = luaL_checkudata(L, 1, API_TYPE_FONT);
|
||||
lua_pushnumber(L, ren_font_get_size(*self));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static RenColor checkcolor(lua_State *L, int idx, int def) {
|
||||
RenColor color;
|
||||
@@ -71,40 +165,17 @@ static int f_draw_rect(lua_State *L) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int draw_text_subpixel_impl(lua_State *L, bool draw_subpixel) {
|
||||
FontDesc *font_desc = luaL_checkudata(L, 1, API_TYPE_FONT);
|
||||
static int f_draw_text(lua_State *L) {
|
||||
RenFont** font = luaL_checkudata(L, 1, API_TYPE_FONT);
|
||||
const char *text = luaL_checkstring(L, 2);
|
||||
/* The coordinate below will be in subpixel iff draw_subpixel is true.
|
||||
Otherwise it will be in pixels. */
|
||||
int x_subpixel = luaL_checknumber(L, 3);
|
||||
float x = luaL_checknumber(L, 3);
|
||||
int y = luaL_checknumber(L, 4);
|
||||
RenColor color = checkcolor(L, 5, 255);
|
||||
|
||||
CPReplaceTable *rep_table;
|
||||
RenColor replace_color;
|
||||
if (lua_gettop(L) >= 7) {
|
||||
rep_table = luaL_checkudata(L, 6, API_TYPE_REPLACE);
|
||||
replace_color = checkcolor(L, 7, 255);
|
||||
} else {
|
||||
rep_table = NULL;
|
||||
replace_color = (RenColor) {0};
|
||||
}
|
||||
|
||||
x_subpixel = rencache_draw_text(L, font_desc, 1, text, x_subpixel, y, color, draw_subpixel, rep_table, replace_color);
|
||||
lua_pushnumber(L, x_subpixel);
|
||||
x = rencache_draw_text(L, *font, text, x, y, color);
|
||||
lua_pushnumber(L, x);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int f_draw_text(lua_State *L) {
|
||||
return draw_text_subpixel_impl(L, false);
|
||||
}
|
||||
|
||||
|
||||
static int f_draw_text_subpixel(lua_State *L) {
|
||||
return draw_text_subpixel_impl(L, true);
|
||||
}
|
||||
|
||||
|
||||
static const luaL_Reg lib[] = {
|
||||
{ "show_debug", f_show_debug },
|
||||
{ "get_size", f_get_size },
|
||||
@@ -113,19 +184,26 @@ static const luaL_Reg lib[] = {
|
||||
{ "set_clip_rect", f_set_clip_rect },
|
||||
{ "draw_rect", f_draw_rect },
|
||||
{ "draw_text", f_draw_text },
|
||||
{ "draw_text_subpixel", f_draw_text_subpixel },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
|
||||
int luaopen_renderer_font(lua_State *L);
|
||||
int luaopen_renderer_replacements(lua_State *L);
|
||||
static const luaL_Reg fontLib[] = {
|
||||
{ "__gc", f_font_gc },
|
||||
{ "load", f_font_load },
|
||||
{ "copy", f_font_copy },
|
||||
{ "set_tab_size", f_font_set_tab_size },
|
||||
{ "get_width", f_font_get_width },
|
||||
{ "get_height", f_font_get_height },
|
||||
{ "get_size", f_font_get_size },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
int luaopen_renderer(lua_State *L) {
|
||||
luaL_newlib(L, lib);
|
||||
luaopen_renderer_font(L);
|
||||
luaL_newmetatable(L, API_TYPE_FONT);
|
||||
luaL_setfuncs(L, fontLib, 0);
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setfield(L, -2, "__index");
|
||||
lua_setfield(L, -2, "font");
|
||||
luaopen_renderer_replacements(L);
|
||||
lua_setfield(L, -2, "replacements");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -1,158 +0,0 @@
|
||||
#include <lua.h>
|
||||
#include <lauxlib.h>
|
||||
|
||||
#include "api.h"
|
||||
#include "fontdesc.h"
|
||||
#include "renderer.h"
|
||||
#include "rencache.h"
|
||||
|
||||
static int f_load(lua_State *L) {
|
||||
const char *filename = luaL_checkstring(L, 1);
|
||||
float size = luaL_checknumber(L, 2);
|
||||
unsigned int font_options = 0;
|
||||
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) {
|
||||
font_options |= RenFontGrayscale;
|
||||
} else if (strcmp(antialiasing, "subpixel") == 0) {
|
||||
font_options |= RenFontSubpixel;
|
||||
} else {
|
||||
return luaL_error(L, "error in renderer.font.load, unknown antialiasing option: \"%s\"", antialiasing);
|
||||
}
|
||||
}
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, 3, "hinting");
|
||||
if (lua_isstring(L, -1)) {
|
||||
const char *hinting = lua_tostring(L, -1);
|
||||
if (hinting) {
|
||||
if (strcmp(hinting, "slight") == 0) {
|
||||
font_options |= RenFontHintingSlight;
|
||||
} else if (strcmp(hinting, "none") == 0) {
|
||||
font_options |= RenFontHintingNone;
|
||||
} else if (strcmp(hinting, "full") == 0) {
|
||||
font_options |= RenFontHintingFull;
|
||||
} else {
|
||||
return luaL_error(L, "error in renderer.font.load, unknown hinting option: \"%s\"", hinting);
|
||||
}
|
||||
}
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
if (ren_verify_font(filename)) {
|
||||
luaL_error(L, "failed to load font");
|
||||
}
|
||||
|
||||
FontDesc *font_desc = lua_newuserdata(L, font_desc_alloc_size(filename));
|
||||
font_desc_init(font_desc, filename, size, font_options);
|
||||
luaL_setmetatable(L, API_TYPE_FONT);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int f_copy(lua_State *L) {
|
||||
FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT);
|
||||
float size;
|
||||
if (lua_gettop(L) >= 2) {
|
||||
size = luaL_checknumber(L, 2);
|
||||
} else {
|
||||
size = self->size;
|
||||
}
|
||||
FontDesc *new_font_desc = lua_newuserdata(L, font_desc_alloc_size(self->filename));
|
||||
font_desc_init(new_font_desc, self->filename, size, self->options);
|
||||
luaL_setmetatable(L, API_TYPE_FONT);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int f_set_tab_size(lua_State *L) {
|
||||
FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT);
|
||||
int n = luaL_checknumber(L, 2);
|
||||
font_desc_set_tab_size(self, n);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int f_gc(lua_State *L) {
|
||||
FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT);
|
||||
font_desc_clear(self);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int f_get_width(lua_State *L) {
|
||||
FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT);
|
||||
const char *text = luaL_checkstring(L, 2);
|
||||
/* By calling ren_get_font_width with NULL as third arguments
|
||||
we will obtain the width in points. */
|
||||
int w = ren_get_font_width(self, text, NULL);
|
||||
lua_pushnumber(L, w);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int f_subpixel_scale(lua_State *L) {
|
||||
FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT);
|
||||
lua_pushnumber(L, ren_get_font_subpixel_scale(self));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int f_get_width_subpixel(lua_State *L) {
|
||||
FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT);
|
||||
const char *text = luaL_checkstring(L, 2);
|
||||
int subpixel_scale;
|
||||
/* We need to pass a non-null subpixel_scale pointer to force
|
||||
subpixel width calculation. */
|
||||
lua_pushnumber(L, ren_get_font_width(self, text, &subpixel_scale));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int f_get_height(lua_State *L) {
|
||||
FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT);
|
||||
lua_pushnumber(L, ren_get_font_height(self) );
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int f_get_size(lua_State *L) {
|
||||
FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT);
|
||||
lua_pushnumber(L, self->size);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int f_set_size(lua_State *L) {
|
||||
FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT);
|
||||
float new_size = luaL_checknumber(L, 2);
|
||||
font_desc_clear(self);
|
||||
self->size = new_size;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static const luaL_Reg lib[] = {
|
||||
{ "__gc", f_gc },
|
||||
{ "load", f_load },
|
||||
{ "copy", f_copy },
|
||||
{ "set_tab_size", f_set_tab_size },
|
||||
{ "get_width", f_get_width },
|
||||
{ "get_width_subpixel", f_get_width_subpixel },
|
||||
{ "get_height", f_get_height },
|
||||
{ "subpixel_scale", f_subpixel_scale },
|
||||
{ "get_size", f_get_size },
|
||||
{ "set_size", f_set_size },
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
int luaopen_renderer_font(lua_State *L) {
|
||||
luaL_newmetatable(L, API_TYPE_FONT);
|
||||
luaL_setfuncs(L, lib, 0);
|
||||
lua_pushvalue(L, -1);
|
||||
lua_setfield(L, -2, "__index");
|
||||
return 1;
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "fontdesc.h"
|
||||
#include "renderer.h"
|
||||
|
||||
|
||||
int font_desc_alloc_size(const char *filename) {
|
||||
return offsetof(FontDesc, filename) + strlen(filename) + 1;
|
||||
}
|
||||
|
||||
void font_desc_init(FontDesc *font_desc, const char *filename, float size, unsigned int font_options) {
|
||||
memcpy(font_desc->filename, filename, strlen(filename) + 1);
|
||||
font_desc->size = size;
|
||||
font_desc->options = font_options;
|
||||
font_desc->tab_size = 4;
|
||||
font_desc->cache_length = 0;
|
||||
font_desc->cache_last_index = 0; /* Normally no need to initialize. */
|
||||
}
|
||||
|
||||
void font_desc_clear(FontDesc *font_desc) {
|
||||
for (int i = 0; i < font_desc->cache_length; i++) {
|
||||
ren_free_font(font_desc->cache[i].font);
|
||||
}
|
||||
font_desc->cache_length = 0;
|
||||
font_desc->cache_last_index = 0;
|
||||
}
|
||||
|
||||
void font_desc_set_tab_size(FontDesc *font_desc, int tab_size) {
|
||||
font_desc->tab_size = tab_size;
|
||||
for (int i = 0; i < font_desc->cache_length; i++) {
|
||||
ren_set_font_tab_size(font_desc->cache[i].font, tab_size);
|
||||
}
|
||||
}
|
||||
|
||||
int font_desc_get_tab_size(FontDesc *font_desc) {
|
||||
return font_desc->tab_size;
|
||||
}
|
||||
|
||||
static void load_scaled_font(FontDesc *font_desc, int index, int scale) {
|
||||
RenFont *font = ren_load_font(font_desc->filename, scale * font_desc->size, font_desc->options);
|
||||
if (!font) {
|
||||
/* The font was able to load when initially loaded using renderer.load.font.
|
||||
If now is no longer available we just abort the application. */
|
||||
fprintf(stderr, "Fatal error: unable to load font %s. Application will abort.\n",
|
||||
font_desc->filename);
|
||||
exit(1);
|
||||
}
|
||||
font_desc->cache[index].font = font;
|
||||
font_desc->cache[index].scale = scale;
|
||||
}
|
||||
|
||||
RenFont *font_desc_get_font_at_scale(FontDesc *font_desc, int scale) {
|
||||
int index = -1;
|
||||
for (int i = 0; i < font_desc->cache_length; i++) {
|
||||
if (font_desc->cache[i].scale == scale) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (index < 0) {
|
||||
index = font_desc->cache_length;
|
||||
if (index < FONT_CACHE_ARRAY_MAX) {
|
||||
load_scaled_font(font_desc, index, scale);
|
||||
font_desc->cache_length = index + 1;
|
||||
} else {
|
||||
// FIXME: should not print into the stderr or stdout.
|
||||
fprintf(stderr, "Warning: max array of font scale reached.\n");
|
||||
index = (font_desc->cache_last_index == 0 ? 1 : 0);
|
||||
ren_free_font(font_desc->cache[index].font);
|
||||
load_scaled_font(font_desc, index, scale);
|
||||
}
|
||||
}
|
||||
font_desc->cache_last_index = index;
|
||||
return font_desc->cache[index].font;
|
||||
}
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
#ifndef FONT_DESC_H
|
||||
#define FONT_DESC_H
|
||||
|
||||
typedef struct RenFont RenFont;
|
||||
|
||||
struct FontInstance {
|
||||
RenFont *font;
|
||||
short int scale;
|
||||
};
|
||||
typedef struct FontInstance FontInstance;
|
||||
|
||||
#define FONT_CACHE_ARRAY_MAX 2
|
||||
|
||||
struct FontDesc {
|
||||
float size;
|
||||
unsigned int options;
|
||||
short int tab_size;
|
||||
FontInstance cache[FONT_CACHE_ARRAY_MAX];
|
||||
short int cache_length;
|
||||
short int cache_last_index; /* More recently used instance. */
|
||||
char filename[0];
|
||||
};
|
||||
typedef struct FontDesc FontDesc;
|
||||
|
||||
void font_desc_init(FontDesc *font_desc, const char *filename, float size, unsigned int font_options);
|
||||
int font_desc_alloc_size(const char *filename);
|
||||
int font_desc_get_tab_size(FontDesc *font_desc);
|
||||
void font_desc_set_tab_size(FontDesc *font_desc, int tab_size);
|
||||
void font_desc_clear(FontDesc *font_desc);
|
||||
RenFont *font_desc_get_font_at_scale(FontDesc *font_desc, int scale);
|
||||
|
||||
#endif
|
||||
|
||||
+1
-5
@@ -1,14 +1,11 @@
|
||||
lite_sources = [
|
||||
'api/api.c',
|
||||
'api/cp_replace.c',
|
||||
'api/renderer.c',
|
||||
'api/renderer_font.c',
|
||||
'api/regex.c',
|
||||
'api/system.c',
|
||||
'api/process.c',
|
||||
'renderer.c',
|
||||
'renwindow.c',
|
||||
'fontdesc.c',
|
||||
'rencache.c',
|
||||
'main.c',
|
||||
]
|
||||
@@ -25,11 +22,10 @@ lite_include = include_directories('.')
|
||||
|
||||
executable('lite-xl',
|
||||
lite_sources + lite_rc,
|
||||
include_directories: [lite_include, font_renderer_include],
|
||||
include_directories: [lite_include],
|
||||
dependencies: lite_deps,
|
||||
c_args: lite_cargs,
|
||||
objc_args: lite_cargs,
|
||||
link_with: libfontrenderer,
|
||||
link_args: lite_link_args,
|
||||
install_dir: lite_bindir,
|
||||
install: true,
|
||||
|
||||
+14
-74
@@ -16,32 +16,19 @@
|
||||
#define COMMAND_BUF_SIZE (1024 * 512)
|
||||
#define COMMAND_BARE_SIZE offsetof(Command, text)
|
||||
|
||||
enum { SET_CLIP, DRAW_TEXT, DRAW_RECT, DRAW_TEXT_SUBPIXEL };
|
||||
enum { SET_CLIP, DRAW_TEXT, DRAW_RECT };
|
||||
|
||||
typedef struct {
|
||||
int8_t type;
|
||||
int8_t tab_size;
|
||||
int8_t subpixel_scale;
|
||||
int8_t x_subpixel_offset;
|
||||
int32_t size;
|
||||
RenRect rect;
|
||||
RenColor color;
|
||||
FontDesc *font_desc;
|
||||
CPReplaceTable *replacements;
|
||||
RenColor replace_color;
|
||||
RenFont *font;
|
||||
float text_x;
|
||||
char text[0];
|
||||
} Command;
|
||||
|
||||
#define FONT_REFS_MAX 12
|
||||
struct FontRef {
|
||||
FontDesc *font_desc;
|
||||
int index;
|
||||
};
|
||||
typedef struct FontRef FontRef;
|
||||
FontRef font_refs[FONT_REFS_MAX];
|
||||
int font_refs_len = 0;
|
||||
|
||||
|
||||
static unsigned cells_buf1[CELLS_X * CELLS_Y];
|
||||
static unsigned cells_buf2[CELLS_X * CELLS_Y];
|
||||
static unsigned *cells_prev = cells_buf1;
|
||||
@@ -52,36 +39,9 @@ static int command_buf_idx;
|
||||
static RenRect screen_rect;
|
||||
static bool show_debug;
|
||||
|
||||
|
||||
static inline int min(int a, int b) { return a < b ? a : b; }
|
||||
static inline int max(int a, int b) { return a > b ? a : b; }
|
||||
|
||||
static int font_refs_add(lua_State *L, FontDesc *font_desc, int index) {
|
||||
for (int i = 0; i < font_refs_len; i++) {
|
||||
if (font_refs[i].font_desc == font_desc) {
|
||||
return font_refs[i].index;
|
||||
}
|
||||
}
|
||||
|
||||
if (font_refs_len >= FONT_REFS_MAX) {
|
||||
fprintf(stderr, "Warning: (" __FILE__ "): exhausted font reference buffer\n");
|
||||
return LUA_NOREF;
|
||||
}
|
||||
|
||||
lua_pushvalue(L, index);
|
||||
int ref = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
|
||||
font_refs[font_refs_len++] = (FontRef) { font_desc, ref };
|
||||
return ref;
|
||||
}
|
||||
|
||||
|
||||
static void font_refs_clear(lua_State *L) {
|
||||
for (int i = 0; i < font_refs_len; i++) {
|
||||
luaL_unref(L, LUA_REGISTRYINDEX, font_refs[i].index);
|
||||
}
|
||||
font_refs_len = 0;
|
||||
}
|
||||
|
||||
/* 32bit fnv-1a hash */
|
||||
#define HASH_INITIAL 2166136261
|
||||
@@ -168,35 +128,23 @@ void rencache_draw_rect(RenRect rect, RenColor color) {
|
||||
}
|
||||
}
|
||||
|
||||
int rencache_draw_text(lua_State *L, FontDesc *font_desc, int font_index,
|
||||
const char *text, int x, int y, RenColor color, bool draw_subpixel,
|
||||
CPReplaceTable *replacements, RenColor replace_color)
|
||||
float rencache_draw_text(lua_State *L, RenFont *font, const char *text, float x, int y, RenColor color)
|
||||
{
|
||||
int subpixel_scale;
|
||||
int w_subpixel = ren_get_font_width(font_desc, text, &subpixel_scale);
|
||||
RenRect rect;
|
||||
rect.x = (draw_subpixel ? ren_font_subpixel_round(x, subpixel_scale, -1) : x);
|
||||
rect.y = y;
|
||||
rect.width = ren_font_subpixel_round(w_subpixel, subpixel_scale, 0);
|
||||
rect.height = ren_get_font_height(font_desc);
|
||||
|
||||
if (rects_overlap(screen_rect, rect) && font_refs_add(L, font_desc, font_index) >= 0) {
|
||||
float width = ren_font_get_width(font, text);
|
||||
RenRect rect = { x, y, (int)width, ren_font_get_height(font) };
|
||||
if (rects_overlap(screen_rect, rect)) {
|
||||
int sz = strlen(text) + 1;
|
||||
Command *cmd = push_command(draw_subpixel ? DRAW_TEXT_SUBPIXEL : DRAW_TEXT, COMMAND_BARE_SIZE + sz);
|
||||
Command *cmd = push_command(DRAW_TEXT, COMMAND_BARE_SIZE + sz);
|
||||
if (cmd) {
|
||||
memcpy(cmd->text, text, sz);
|
||||
cmd->color = color;
|
||||
cmd->font_desc = font_desc;
|
||||
cmd->font = font;
|
||||
cmd->rect = rect;
|
||||
cmd->subpixel_scale = (draw_subpixel ? subpixel_scale : 1);
|
||||
cmd->x_subpixel_offset = x - subpixel_scale * rect.x;
|
||||
cmd->tab_size = font_desc_get_tab_size(font_desc);
|
||||
cmd->replacements = replacements;
|
||||
cmd->replace_color = replace_color;
|
||||
cmd->text_x = x;
|
||||
cmd->tab_size = ren_font_get_tab_size(font);
|
||||
}
|
||||
}
|
||||
|
||||
return x + (draw_subpixel ? w_subpixel : rect.width);
|
||||
return x + width;
|
||||
}
|
||||
|
||||
|
||||
@@ -214,7 +162,6 @@ void rencache_begin_frame(lua_State *L) {
|
||||
screen_rect.height = h;
|
||||
rencache_invalidate();
|
||||
}
|
||||
font_refs_clear(L);
|
||||
}
|
||||
|
||||
|
||||
@@ -301,15 +248,8 @@ void rencache_end_frame(lua_State *L) {
|
||||
ren_draw_rect(cmd->rect, cmd->color);
|
||||
break;
|
||||
case DRAW_TEXT:
|
||||
font_desc_set_tab_size(cmd->font_desc, cmd->tab_size);
|
||||
ren_draw_text(cmd->font_desc, cmd->text, cmd->rect.x, cmd->rect.y, cmd->color,
|
||||
cmd->replacements, cmd->replace_color);
|
||||
break;
|
||||
case DRAW_TEXT_SUBPIXEL:
|
||||
font_desc_set_tab_size(cmd->font_desc, cmd->tab_size);
|
||||
ren_draw_text_subpixel(cmd->font_desc, cmd->text,
|
||||
cmd->subpixel_scale * cmd->rect.x + cmd->x_subpixel_offset, cmd->rect.y, cmd->color,
|
||||
cmd->replacements, cmd->replace_color);
|
||||
ren_font_set_tab_size(cmd->font, cmd->tab_size);
|
||||
ren_draw_text(cmd->font, cmd->text, cmd->text_x, cmd->rect.y, cmd->color);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
+8
-8
@@ -5,13 +5,13 @@
|
||||
#include <lua.h>
|
||||
#include "renderer.h"
|
||||
|
||||
void rencache_show_debug(bool enable);
|
||||
void rencache_set_clip_rect(RenRect rect);
|
||||
void rencache_draw_rect(RenRect rect, RenColor color);
|
||||
int rencache_draw_text(lua_State *L, FontDesc *font_desc, int font_index, const char *text, int x, int y, RenColor color,
|
||||
bool draw_subpixel, CPReplaceTable *replacements, RenColor replace_color);
|
||||
void rencache_invalidate(void);
|
||||
void rencache_begin_frame(lua_State *L);
|
||||
void rencache_end_frame(lua_State *L);
|
||||
void rencache_show_debug(bool enable);
|
||||
void rencache_set_clip_rect(RenRect rect);
|
||||
void rencache_draw_rect(RenRect rect, RenColor color);
|
||||
float rencache_draw_text(lua_State *L, RenFont *font,
|
||||
const char *text, float x, int y, RenColor color);
|
||||
void rencache_invalidate(void);
|
||||
void rencache_begin_frame(lua_State *L);
|
||||
void rencache_end_frame(lua_State *L);
|
||||
|
||||
#endif
|
||||
|
||||
+297
-324
@@ -2,37 +2,21 @@
|
||||
#include <stdbool.h>
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include "font_renderer.h"
|
||||
#include <ft2build.h>
|
||||
#include <freetype/ftlcdfil.h>
|
||||
#include <freetype/ftoutln.h>
|
||||
#include FT_FREETYPE_H
|
||||
|
||||
#include "renderer.h"
|
||||
#include "renwindow.h"
|
||||
|
||||
#define DIVIDE_BY_255_SIGNED(x, sign_val) (((x) + (sign_val) + ((x)>>8)) >> 8)
|
||||
#define DIVIDE_BY_255(x) DIVIDE_BY_255_SIGNED(x, 1)
|
||||
#define MAX_GLYPHSET 256
|
||||
#define REPLACEMENT_CHUNK_SIZE 8
|
||||
|
||||
struct RenImage {
|
||||
RenColor *pixels;
|
||||
int width, height;
|
||||
};
|
||||
|
||||
struct GlyphSet {
|
||||
FR_Bitmap *image;
|
||||
FR_Bitmap_Glyph_Metrics glyphs[256];
|
||||
};
|
||||
typedef struct GlyphSet GlyphSet;
|
||||
|
||||
/* The field "padding" below must be there just before GlyphSet *sets[MAX_GLYPHSET]
|
||||
because the field "sets" can be indexed and writted with an index -1. For this
|
||||
reason the "padding" field must be there but is never explicitly used. */
|
||||
struct RenFont {
|
||||
GlyphSet *padding;
|
||||
GlyphSet *sets[MAX_GLYPHSET];
|
||||
float size;
|
||||
int height;
|
||||
int space_advance;
|
||||
FR_Renderer *renderer;
|
||||
};
|
||||
#define SUBPIXEL_BITMAPS_CACHED 3
|
||||
|
||||
static RenWindow window_renderer = {0};
|
||||
static FT_Library library;
|
||||
|
||||
static void* check_alloc(void *ptr) {
|
||||
if (!ptr) {
|
||||
@@ -42,6 +26,29 @@ static void* check_alloc(void *ptr) {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
/************************* Fonts *************************/
|
||||
|
||||
typedef struct {
|
||||
unsigned short x0, x1, y0, y1;
|
||||
short bitmap_left, bitmap_top;
|
||||
float xadvance;
|
||||
} GlyphMetric;
|
||||
|
||||
typedef struct {
|
||||
SDL_Surface* surface;
|
||||
GlyphMetric metrics[256];
|
||||
} GlyphSet;
|
||||
|
||||
typedef struct RenFont {
|
||||
FT_Face face;
|
||||
GlyphSet* sets[SUBPIXEL_BITMAPS_CACHED][MAX_GLYPHSET];
|
||||
float size, space_advance, tab_advance;
|
||||
short max_height;
|
||||
bool subpixel;
|
||||
ERenFontHinting hinting;
|
||||
unsigned char style;
|
||||
char path[0];
|
||||
} RenFont;
|
||||
|
||||
static const char* utf8_to_codepoint(const char *p, unsigned *dst) {
|
||||
unsigned res, n;
|
||||
@@ -59,43 +66,280 @@ static const char* utf8_to_codepoint(const char *p, unsigned *dst) {
|
||||
return p + 1;
|
||||
}
|
||||
|
||||
|
||||
void ren_cp_replace_init(CPReplaceTable *rep_table) {
|
||||
rep_table->size = 0;
|
||||
rep_table->replacements = NULL;
|
||||
}
|
||||
|
||||
|
||||
void ren_cp_replace_free(CPReplaceTable *rep_table) {
|
||||
free(rep_table->replacements);
|
||||
}
|
||||
|
||||
|
||||
void ren_cp_replace_add(CPReplaceTable *rep_table, const char *src, const char *dst) {
|
||||
int table_size = rep_table->size;
|
||||
if (table_size % REPLACEMENT_CHUNK_SIZE == 0) {
|
||||
CPReplace *old_replacements = rep_table->replacements;
|
||||
const int new_size = (table_size / REPLACEMENT_CHUNK_SIZE + 1) * REPLACEMENT_CHUNK_SIZE;
|
||||
rep_table->replacements = malloc(new_size * sizeof(CPReplace));
|
||||
if (!rep_table->replacements) {
|
||||
rep_table->replacements = old_replacements;
|
||||
return;
|
||||
}
|
||||
memcpy(rep_table->replacements, old_replacements, table_size * sizeof(CPReplace));
|
||||
free(old_replacements);
|
||||
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;
|
||||
}
|
||||
CPReplace *rep = &rep_table->replacements[table_size];
|
||||
utf8_to_codepoint(src, &rep->codepoint_src);
|
||||
utf8_to_codepoint(dst, &rep->codepoint_dst);
|
||||
rep_table->size = table_size + 1;
|
||||
return FT_LOAD_TARGET_NORMAL | FT_LOAD_NO_HINTING;
|
||||
}
|
||||
|
||||
static int font_set_render_options(RenFont* font) {
|
||||
if (font->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;
|
||||
case FONT_HINTING_SLIGHT:
|
||||
case FONT_HINTING_FULL: FT_Library_SetLcdFilterWeights(library, weights); break;
|
||||
}
|
||||
return FT_RENDER_MODE_LCD;
|
||||
} else {
|
||||
switch (font->hinting) {
|
||||
case FONT_HINTING_NONE: return FT_RENDER_MODE_NORMAL; break;
|
||||
case FONT_HINTING_SLIGHT: return FT_RENDER_MODE_LIGHT; break;
|
||||
case FONT_HINTING_FULL: return FT_RENDER_MODE_LIGHT; break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int font_set_style(FT_Outline* outline, int x_translation, unsigned char style) {
|
||||
FT_Outline_Translate(outline, x_translation, 0 );
|
||||
if (style & FONT_STYLE_BOLD)
|
||||
FT_Outline_EmboldenXY(outline, 1 << 5, 0);
|
||||
if (style & FONT_STYLE_ITALIC) {
|
||||
FT_Matrix matrix = { 1 << 16, 1 << 14, 0, 1 << 16 };
|
||||
FT_Outline_Transform(outline, &matrix);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
for (int j = 0, pen_x = 0; j < bitmaps_cached; ++j) {
|
||||
GlyphSet* set = check_alloc(calloc(1, sizeof(GlyphSet)));
|
||||
font->sets[j][idx] = set;
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
int glyph_index = FT_Get_Char_Index( font->face, i + idx * MAX_GLYPHSET);
|
||||
if (!glyph_index || FT_Load_Glyph(font->face, glyph_index, load_option | FT_LOAD_BITMAP_METRICS_ONLY) || font_set_style(&font->face->glyph->outline, j * (64 / SUBPIXEL_BITMAPS_CACHED), font->style) || FT_Render_Glyph(font->face->glyph, render_option))
|
||||
continue;
|
||||
FT_GlyphSlot slot = font->face->glyph;
|
||||
int glyph_width = slot->bitmap.width / byte_width;
|
||||
set->metrics[i] = (GlyphMetric){ pen_x, pen_x + glyph_width, 0, slot->bitmap.rows, 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));
|
||||
unsigned char* pixels = set->surface->pixels;
|
||||
for (int i = 0; i < 256; ++i) {
|
||||
int glyph_index = FT_Get_Char_Index(font->face, i + idx * MAX_GLYPHSET);
|
||||
if (!glyph_index || FT_Load_Glyph(font->face, glyph_index, load_option))
|
||||
continue;
|
||||
FT_GlyphSlot slot = font->face->glyph;
|
||||
font_set_style(&slot->outline, (64 / bitmaps_cached) * j, font->style);
|
||||
if (FT_Render_Glyph(slot, render_option))
|
||||
continue;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static GlyphSet* font_get_glyphset(RenFont* font, unsigned int codepoint, int subpixel_idx) {
|
||||
int idx = (codepoint >> 8) % MAX_GLYPHSET;
|
||||
if (!font->sets[subpixel_idx][idx])
|
||||
font_load_glyphset(font, idx);
|
||||
return font->sets[subpixel_idx][idx];
|
||||
}
|
||||
|
||||
RenFont* ren_font_load(const char* path, float size, bool subpixel, unsigned char hinting, unsigned char style) {
|
||||
FT_Face face;
|
||||
if (FT_New_Face( library, path, 0, &face))
|
||||
return NULL;
|
||||
|
||||
const int surface_scale = renwin_surface_scale(&window_renderer);
|
||||
if (FT_Set_Pixel_Sizes(face, 0, (int)(size*surface_scale)))
|
||||
goto failure;
|
||||
int len = strlen(path);
|
||||
RenFont* font = check_alloc(calloc(1, sizeof(RenFont) + len + 1));
|
||||
strcpy(font->path, path);
|
||||
font->face = face;
|
||||
font->size = size;
|
||||
font->subpixel = subpixel;
|
||||
font->hinting = hinting;
|
||||
font->style = style;
|
||||
font->space_advance = (int)font_get_glyphset(font, ' ', 0)->metrics[' '].xadvance;
|
||||
font->tab_advance = font->space_advance * 2;
|
||||
return font;
|
||||
failure:
|
||||
FT_Done_Face(face);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
RenFont* ren_font_copy(RenFont* font, float size) {
|
||||
return ren_font_load(font->path, size, font->subpixel, font->hinting, font->style);
|
||||
}
|
||||
|
||||
void ren_font_free(RenFont* font) {
|
||||
for (int i = 0; i < SUBPIXEL_BITMAPS_CACHED; ++i) {
|
||||
for (int j = 0; j < MAX_GLYPHSET; ++j) {
|
||||
if (font->sets[i][j]) {
|
||||
if (font->sets[i][j]->surface)
|
||||
SDL_FreeSurface(font->sets[i][j]->surface);
|
||||
free(font->sets[i][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
FT_Done_Face(font->face);
|
||||
free(font);
|
||||
}
|
||||
|
||||
void ren_font_set_tab_size(RenFont *font, int n) {
|
||||
for (int i = 0; i < (font->subpixel ? SUBPIXEL_BITMAPS_CACHED : 1); ++i)
|
||||
font_get_glyphset(font, '\t', i)->metrics['\t'].xadvance = font->space_advance * n;
|
||||
}
|
||||
|
||||
int ren_font_get_tab_size(RenFont *font) {
|
||||
return font_get_glyphset(font, '\t', 0)->metrics['\t'].xadvance / font->space_advance;
|
||||
}
|
||||
|
||||
float ren_font_get_width(RenFont *font, const char *text) {
|
||||
float width = 0;
|
||||
const char* end = text + strlen(text);
|
||||
while (text < end) {
|
||||
unsigned int codepoint;
|
||||
text = utf8_to_codepoint(text, &codepoint);
|
||||
GlyphMetric* metric = &font_get_glyphset(font, codepoint, 0)->metrics[codepoint % 256];
|
||||
width += metric->xadvance ? metric->xadvance : font->space_advance;
|
||||
}
|
||||
const int surface_scale = renwin_surface_scale(&window_renderer);
|
||||
return width / surface_scale;
|
||||
}
|
||||
|
||||
float ren_font_get_size(RenFont *font) {
|
||||
return font->size;
|
||||
}
|
||||
int ren_font_get_height(RenFont *font) {
|
||||
return font->size + 3;
|
||||
}
|
||||
|
||||
float ren_draw_text(RenFont *font, const char *text, float x, int y, RenColor color) {
|
||||
SDL_Surface *surface = renwin_get_surface(&window_renderer);
|
||||
const RenRect clip = window_renderer.clip;
|
||||
|
||||
const int surface_scale = renwin_surface_scale(&window_renderer);
|
||||
float pen_x = x * surface_scale;
|
||||
y *= surface_scale;
|
||||
int bytes_per_pixel = surface->format->BytesPerPixel;
|
||||
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);
|
||||
int bitmap_index = font->subpixel ? (int)(fmod(pen_x, 1.0) * SUBPIXEL_BITMAPS_CACHED) : 0;
|
||||
GlyphSet* set = font_get_glyphset(font, codepoint, bitmap_index);
|
||||
GlyphMetric* metric = &set->metrics[codepoint % 256];
|
||||
int start_x = floor(pen_x) + metric->bitmap_left, end_x = metric->x1 - metric->x0 + pen_x;
|
||||
int glyph_end = metric->x1, glyph_start = metric->x0;
|
||||
if (set->surface && color.a > 0 && end_x >= clip.x && start_x < clip_end_x) {
|
||||
unsigned char* source_pixels = set->surface->pixels;
|
||||
for (int line = metric->y0; line < metric->y1; ++line) {
|
||||
int target_y = line + y - metric->y0 - metric->bitmap_top + font->size * surface_scale;
|
||||
if (target_y < clip.y)
|
||||
continue;
|
||||
if (target_y >= clip_end_y)
|
||||
break;
|
||||
if (start_x + (glyph_end - glyph_start) >= clip_end_x)
|
||||
glyph_end = glyph_start + (clip_end_x - start_x);
|
||||
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 + metric->x0 * (font->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 };
|
||||
if (font->subpixel) {
|
||||
SDL_Color src = { *source_pixel++, *source_pixel++, *source_pixel++ };
|
||||
r = color.r * src.r + dst.r * (255 - src.r) + 127;
|
||||
r = DIVIDE_BY_255(r);
|
||||
g = color.g * src.g + dst.g * (255 - src.g) + 127;
|
||||
g = DIVIDE_BY_255(g);
|
||||
b = color.b * src.b + dst.b * (255 - src.b) + 127;
|
||||
b = DIVIDE_BY_255(b);
|
||||
} else {
|
||||
unsigned char intensity = *source_pixel++;
|
||||
r = color.r * intensity + dst.r * (255 - intensity) + 127;
|
||||
r = DIVIDE_BY_255(r);
|
||||
g = color.g * intensity + dst.g * (255 - intensity) + 127;
|
||||
g = DIVIDE_BY_255(g);
|
||||
b = color.b * intensity + dst.b * (255 - intensity) + 127;
|
||||
b = DIVIDE_BY_255(b);
|
||||
}
|
||||
*destination_pixel++ = dst.a << 24 | r << 16 | g << 8 | b;
|
||||
}
|
||||
}
|
||||
}
|
||||
pen_x += metric->xadvance ? metric->xadvance : font->space_advance;
|
||||
}
|
||||
if (font->style & FONT_STYLE_UNDERLINE)
|
||||
ren_draw_rect((RenRect){ x, y / surface_scale + ren_font_get_height(font) - 1, (pen_x - x) / surface_scale, 1 }, color);
|
||||
return pen_x / surface_scale;
|
||||
}
|
||||
|
||||
/******************* Rectangles **********************/
|
||||
static inline RenColor blend_pixel(RenColor dst, RenColor src) {
|
||||
int ia = 0xff - src.a;
|
||||
dst.r = ((src.r * src.a) + (dst.r * ia)) >> 8;
|
||||
dst.g = ((src.g * src.a) + (dst.g * ia)) >> 8;
|
||||
dst.b = ((src.b * src.a) + (dst.b * ia)) >> 8;
|
||||
return dst;
|
||||
}
|
||||
|
||||
void ren_draw_rect(RenRect rect, RenColor color) {
|
||||
if (color.a == 0) { return; }
|
||||
|
||||
const int surface_scale = renwin_surface_scale(&window_renderer);
|
||||
|
||||
/* transforms coordinates in pixels. */
|
||||
rect.x *= surface_scale;
|
||||
rect.y *= surface_scale;
|
||||
rect.width *= surface_scale;
|
||||
rect.height *= surface_scale;
|
||||
|
||||
const RenRect clip = window_renderer.clip;
|
||||
int x1 = rect.x < clip.x ? clip.x : rect.x;
|
||||
int y1 = rect.y < clip.y ? clip.y : rect.y;
|
||||
int x2 = rect.x + rect.width;
|
||||
int y2 = rect.y + rect.height;
|
||||
x2 = x2 > clip.x + clip.width ? clip.x + clip.width : x2;
|
||||
y2 = y2 > clip.y + clip.height ? clip.y + clip.height : y2;
|
||||
|
||||
SDL_Surface *surface = renwin_get_surface(&window_renderer);
|
||||
RenColor *d = (RenColor*) surface->pixels;
|
||||
d += x1 + y1 * surface->w;
|
||||
int dr = surface->w - (x2 - x1);
|
||||
|
||||
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));
|
||||
} else {
|
||||
for (int j = y1; j < y2; j++) {
|
||||
for (int i = x1; i < x2; i++, d++)
|
||||
*d = blend_pixel(*d, color);
|
||||
d += dr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************** Window Management ****************/
|
||||
void ren_free_window_resources() {
|
||||
renwin_free(&window_renderer);
|
||||
}
|
||||
|
||||
void ren_init(SDL_Window *win) {
|
||||
assert(win);
|
||||
int error = FT_Init_FreeType( &library );
|
||||
if ( error ) {
|
||||
fprintf(stderr, "internal font error when starting the application\n");
|
||||
return;
|
||||
}
|
||||
window_renderer.window = win;
|
||||
renwin_init_surface(&window_renderer);
|
||||
renwin_clip_to_surface(&window_renderer);
|
||||
@@ -130,274 +374,3 @@ void ren_get_size(int *x, int *y) {
|
||||
*y = surface->h / scale;
|
||||
}
|
||||
|
||||
|
||||
RenImage* ren_new_image(int width, int height) {
|
||||
assert(width > 0 && height > 0);
|
||||
RenImage *image = malloc(sizeof(RenImage) + width * height * sizeof(RenColor));
|
||||
check_alloc(image);
|
||||
image->pixels = (void*) (image + 1);
|
||||
image->width = width;
|
||||
image->height = height;
|
||||
return image;
|
||||
}
|
||||
|
||||
void ren_free_image(RenImage *image) {
|
||||
free(image);
|
||||
}
|
||||
|
||||
static GlyphSet* load_glyphset(RenFont *font, int idx) {
|
||||
GlyphSet *set = check_alloc(calloc(1, sizeof(GlyphSet)));
|
||||
|
||||
set->image = FR_Bake_Font_Bitmap(font->renderer, font->height, idx << 8, 256, set->glyphs);
|
||||
check_alloc(set->image);
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
|
||||
static GlyphSet* get_glyphset(RenFont *font, int codepoint) {
|
||||
int idx = (codepoint >> 8) % MAX_GLYPHSET;
|
||||
if (!font->sets[idx]) {
|
||||
font->sets[idx] = load_glyphset(font, idx);
|
||||
}
|
||||
return font->sets[idx];
|
||||
}
|
||||
|
||||
|
||||
int ren_verify_font(const char *filename) {
|
||||
RenFont font[1];
|
||||
font->renderer = FR_Renderer_New(0);
|
||||
if (FR_Load_Font(font->renderer, filename)) {
|
||||
return 1;
|
||||
}
|
||||
FR_Renderer_Free(font->renderer);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
RenFont* ren_load_font(const char *filename, float size, unsigned int renderer_flags) {
|
||||
RenFont *font = NULL;
|
||||
|
||||
/* init font */
|
||||
font = check_alloc(calloc(1, sizeof(RenFont)));
|
||||
font->size = size;
|
||||
|
||||
unsigned int fr_renderer_flags = 0;
|
||||
if ((renderer_flags & RenFontAntialiasingMask) == RenFontSubpixel) {
|
||||
fr_renderer_flags |= FR_SUBPIXEL;
|
||||
}
|
||||
if ((renderer_flags & RenFontHintingMask) == RenFontHintingSlight) {
|
||||
fr_renderer_flags |= (FR_HINTING | FR_PRESCALE_X);
|
||||
} else if ((renderer_flags & RenFontHintingMask) == RenFontHintingFull) {
|
||||
fr_renderer_flags |= FR_HINTING;
|
||||
}
|
||||
font->renderer = FR_Renderer_New(fr_renderer_flags);
|
||||
if (FR_Load_Font(font->renderer, filename)) {
|
||||
free(font);
|
||||
return NULL;
|
||||
}
|
||||
font->height = FR_Get_Font_Height(font->renderer, size);
|
||||
|
||||
FR_Bitmap_Glyph_Metrics *gs = get_glyphset(font, ' ')->glyphs;
|
||||
font->space_advance = gs[' '].xadvance;
|
||||
|
||||
/* make tab and newline glyphs invisible */
|
||||
FR_Bitmap_Glyph_Metrics *g = get_glyphset(font, '\n')->glyphs;
|
||||
g['\t'].x1 = g['\t'].x0;
|
||||
g['\n'].x1 = g['\n'].x0;
|
||||
|
||||
return font;
|
||||
}
|
||||
|
||||
|
||||
void ren_free_font(RenFont *font) {
|
||||
for (int i = 0; i < MAX_GLYPHSET; i++) {
|
||||
GlyphSet *set = font->sets[i];
|
||||
if (set) {
|
||||
FR_Bitmap_Free(set->image);
|
||||
free(set);
|
||||
}
|
||||
}
|
||||
FR_Renderer_Free(font->renderer);
|
||||
free(font);
|
||||
}
|
||||
|
||||
|
||||
void ren_set_font_tab_size(RenFont *font, int n) {
|
||||
GlyphSet *set = get_glyphset(font, '\t');
|
||||
set->glyphs['\t'].xadvance = font->space_advance * n;
|
||||
}
|
||||
|
||||
|
||||
int ren_get_font_tab_size(RenFont *font) {
|
||||
GlyphSet *set = get_glyphset(font, '\t');
|
||||
return set->glyphs['\t'].xadvance / font->space_advance;
|
||||
}
|
||||
|
||||
|
||||
/* Important: if subpixel_scale is NULL we will return width in points. Otherwise we will
|
||||
return width in subpixels. */
|
||||
int ren_get_font_width(FontDesc *font_desc, const char *text, int *subpixel_scale) {
|
||||
int x = 0;
|
||||
const char *p = text;
|
||||
unsigned codepoint;
|
||||
const int surface_scale = renwin_surface_scale(&window_renderer);
|
||||
RenFont *font = font_desc_get_font_at_scale(font_desc, surface_scale);
|
||||
while (*p) {
|
||||
p = utf8_to_codepoint(p, &codepoint);
|
||||
GlyphSet *set = get_glyphset(font, codepoint);
|
||||
FR_Bitmap_Glyph_Metrics *g = &set->glyphs[codepoint & 0xff];
|
||||
x += g->xadvance;
|
||||
}
|
||||
/* At this point here x is in subpixel units */
|
||||
const int x_scale_to_points = FR_Subpixel_Scale(font->renderer) * surface_scale;
|
||||
if (subpixel_scale) {
|
||||
*subpixel_scale = x_scale_to_points;
|
||||
return x;
|
||||
}
|
||||
return (x + x_scale_to_points / 2) / x_scale_to_points;
|
||||
}
|
||||
|
||||
|
||||
int ren_get_font_height(FontDesc *font_desc) {
|
||||
const int surface_scale = renwin_surface_scale(&window_renderer);
|
||||
RenFont *font = font_desc_get_font_at_scale(font_desc, surface_scale);
|
||||
return (font->height + surface_scale / 2) / surface_scale;
|
||||
}
|
||||
|
||||
|
||||
static inline RenColor blend_pixel(RenColor dst, RenColor src) {
|
||||
int ia = 0xff - src.a;
|
||||
dst.r = ((src.r * src.a) + (dst.r * ia)) >> 8;
|
||||
dst.g = ((src.g * src.a) + (dst.g * ia)) >> 8;
|
||||
dst.b = ((src.b * src.a) + (dst.b * ia)) >> 8;
|
||||
return dst;
|
||||
}
|
||||
|
||||
|
||||
#define rect_draw_loop(expr) \
|
||||
for (int j = y1; j < y2; j++) { \
|
||||
for (int i = x1; i < x2; i++) { \
|
||||
*d = expr; \
|
||||
d++; \
|
||||
} \
|
||||
d += dr; \
|
||||
}
|
||||
|
||||
void ren_draw_rect(RenRect rect, RenColor color) {
|
||||
if (color.a == 0) { return; }
|
||||
|
||||
const int surface_scale = renwin_surface_scale(&window_renderer);
|
||||
|
||||
/* transforms coordinates in pixels. */
|
||||
rect.x *= surface_scale;
|
||||
rect.y *= surface_scale;
|
||||
rect.width *= surface_scale;
|
||||
rect.height *= surface_scale;
|
||||
|
||||
const RenRect clip = window_renderer.clip;
|
||||
int x1 = rect.x < clip.x ? clip.x : rect.x;
|
||||
int y1 = rect.y < clip.y ? clip.y : rect.y;
|
||||
int x2 = rect.x + rect.width;
|
||||
int y2 = rect.y + rect.height;
|
||||
x2 = x2 > clip.x + clip.width ? clip.x + clip.width : x2;
|
||||
y2 = y2 > clip.y + clip.height ? clip.y + clip.height : y2;
|
||||
|
||||
SDL_Surface *surface = renwin_get_surface(&window_renderer);
|
||||
RenColor *d = (RenColor*) surface->pixels;
|
||||
d += x1 + y1 * surface->w;
|
||||
int dr = surface->w - (x2 - x1);
|
||||
|
||||
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));
|
||||
} else {
|
||||
rect_draw_loop(blend_pixel(*d, color));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int codepoint_replace(CPReplaceTable *rep_table, unsigned *codepoint) {
|
||||
for (int i = 0; i < rep_table->size; i++) {
|
||||
const CPReplace *rep = &rep_table->replacements[i];
|
||||
if (*codepoint == rep->codepoint_src) {
|
||||
*codepoint = rep->codepoint_dst;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static FR_Clip_Area clip_area_from_rect(const RenRect r) {
|
||||
return (FR_Clip_Area) {r.x, r.y, r.x + r.width, r.y + r.height};
|
||||
}
|
||||
|
||||
|
||||
static void draw_text_impl(RenFont *font, const char *text, int x_subpixel, int y_pixel, RenColor color,
|
||||
CPReplaceTable *replacements, RenColor replace_color)
|
||||
{
|
||||
SDL_Surface *surf = renwin_get_surface(&window_renderer);
|
||||
FR_Clip_Area clip = clip_area_from_rect(window_renderer.clip);
|
||||
const char *p = text;
|
||||
unsigned codepoint;
|
||||
const FR_Color color_fr = { .r = color.r, .g = color.g, .b = color.b };
|
||||
while (*p) {
|
||||
FR_Color color_rep;
|
||||
p = utf8_to_codepoint(p, &codepoint);
|
||||
GlyphSet *set = get_glyphset(font, codepoint);
|
||||
FR_Bitmap_Glyph_Metrics *g = &set->glyphs[codepoint & 0xff];
|
||||
const int xadvance_original_cp = g->xadvance;
|
||||
const int replaced = replacements ? codepoint_replace(replacements, &codepoint) : 0;
|
||||
if (replaced) {
|
||||
set = get_glyphset(font, codepoint);
|
||||
g = &set->glyphs[codepoint & 0xff];
|
||||
color_rep = (FR_Color) { .r = replace_color.r, .g = replace_color.g, .b = replace_color.b};
|
||||
} else {
|
||||
color_rep = color_fr;
|
||||
}
|
||||
if (color.a != 0) {
|
||||
FR_Blend_Glyph(font->renderer, &clip,
|
||||
x_subpixel, y_pixel, (uint8_t *) surf->pixels, surf->w, set->image, g, color_rep);
|
||||
}
|
||||
x_subpixel += xadvance_original_cp;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ren_draw_text_subpixel(FontDesc *font_desc, const char *text, int x_subpixel, int y, RenColor color,
|
||||
CPReplaceTable *replacements, RenColor replace_color)
|
||||
{
|
||||
const int surface_scale = renwin_surface_scale(&window_renderer);
|
||||
RenFont *font = font_desc_get_font_at_scale(font_desc, surface_scale);
|
||||
draw_text_impl(font, text, x_subpixel, surface_scale * y, color, replacements, replace_color);
|
||||
}
|
||||
|
||||
void ren_draw_text(FontDesc *font_desc, const char *text, int x, int y, RenColor color,
|
||||
CPReplaceTable *replacements, RenColor replace_color)
|
||||
{
|
||||
const int surface_scale = renwin_surface_scale(&window_renderer);
|
||||
RenFont *font = font_desc_get_font_at_scale(font_desc, surface_scale);
|
||||
const int subpixel_scale = surface_scale * FR_Subpixel_Scale(font->renderer);
|
||||
draw_text_impl(font, text, subpixel_scale * x, surface_scale * y, color, replacements, replace_color);
|
||||
}
|
||||
|
||||
// Could be declared as static inline
|
||||
int ren_font_subpixel_round(int width, int subpixel_scale, int orientation) {
|
||||
int w_mult;
|
||||
if (orientation < 0) {
|
||||
w_mult = width;
|
||||
} else if (orientation == 0) {
|
||||
w_mult = width + subpixel_scale / 2;
|
||||
} else {
|
||||
w_mult = width + subpixel_scale - 1;
|
||||
}
|
||||
return w_mult / subpixel_scale;
|
||||
}
|
||||
|
||||
|
||||
int ren_get_font_subpixel_scale(FontDesc *font_desc) {
|
||||
const int surface_scale = renwin_surface_scale(&window_renderer);
|
||||
RenFont *font = font_desc_get_font_at_scale(font_desc, surface_scale);
|
||||
return FR_Subpixel_Scale(font->renderer) * surface_scale;
|
||||
}
|
||||
|
||||
+14
-48
@@ -3,37 +3,25 @@
|
||||
|
||||
#include <SDL.h>
|
||||
#include <stdint.h>
|
||||
#include "fontdesc.h"
|
||||
|
||||
typedef struct RenImage RenImage;
|
||||
|
||||
enum {
|
||||
RenFontAntialiasingMask = 1,
|
||||
RenFontGrayscale = 1,
|
||||
RenFontSubpixel = 0,
|
||||
|
||||
RenFontHintingMask = 3 << 1,
|
||||
RenFontHintingSlight = 0 << 1,
|
||||
RenFontHintingNone = 1 << 1,
|
||||
RenFontHintingFull = 2 << 1,
|
||||
};
|
||||
#include <stdbool.h>
|
||||
|
||||
typedef struct RenFont RenFont;
|
||||
typedef enum { FONT_HINTING_NONE, FONT_HINTING_SLIGHT, FONT_HINTING_FULL } ERenFontHinting;
|
||||
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;
|
||||
|
||||
struct CPReplace {
|
||||
unsigned codepoint_src;
|
||||
unsigned codepoint_dst;
|
||||
};
|
||||
typedef struct CPReplace CPReplace;
|
||||
|
||||
|
||||
struct CPReplaceTable {
|
||||
int size;
|
||||
CPReplace *replacements;
|
||||
};
|
||||
typedef struct CPReplaceTable CPReplaceTable;
|
||||
RenFont* ren_font_load(const char *filename, float size, bool subpixel, unsigned char hinting, unsigned char style);
|
||||
RenFont* ren_font_copy(RenFont* font, float size);
|
||||
void ren_font_free(RenFont *font);
|
||||
void ren_font_set_tab_size(RenFont *font, int n);
|
||||
int ren_font_get_tab_size(RenFont *font);
|
||||
float ren_font_get_width(RenFont *font, const char *text);
|
||||
int ren_font_get_height(RenFont *font);
|
||||
float ren_font_get_size(RenFont *font);
|
||||
float ren_draw_text(RenFont *font, const char *text, float x, int y, RenColor color);
|
||||
|
||||
void ren_draw_rect(RenRect rect, RenColor color);
|
||||
|
||||
void ren_init(SDL_Window *win);
|
||||
void ren_resize_window();
|
||||
@@ -42,27 +30,5 @@ void ren_set_clip_rect(RenRect rect);
|
||||
void ren_get_size(int *x, int *y); /* Reports the size in points. */
|
||||
void ren_free_window_resources();
|
||||
|
||||
RenImage* ren_new_image(int width, int height);
|
||||
void ren_free_image(RenImage *image);
|
||||
|
||||
RenFont* ren_load_font(const char *filename, float size, unsigned int renderer_flags);
|
||||
int ren_verify_font(const char *filename);
|
||||
void ren_free_font(RenFont *font);
|
||||
void ren_set_font_tab_size(RenFont *font, int n);
|
||||
int ren_get_font_tab_size(RenFont *font);
|
||||
|
||||
int ren_get_font_width(FontDesc *font_desc, const char *text, int *subpixel_scale);
|
||||
int ren_get_font_height(FontDesc *font_desc);
|
||||
int ren_get_font_subpixel_scale(FontDesc *font_desc);
|
||||
int ren_font_subpixel_round(int width, int subpixel_scale, int orientation);
|
||||
|
||||
void ren_draw_rect(RenRect rect, RenColor color);
|
||||
void ren_draw_text(FontDesc *font_desc, const char *text, int x, int y, RenColor color, CPReplaceTable *replacements, RenColor replace_color);
|
||||
void ren_draw_text_subpixel(FontDesc *font_desc, const char *text, int x_subpixel, int y, RenColor color, CPReplaceTable *replacements, RenColor replace_color);
|
||||
|
||||
void ren_cp_replace_init(CPReplaceTable *rep_table);
|
||||
void ren_cp_replace_free(CPReplaceTable *rep_table);
|
||||
void ren_cp_replace_add(CPReplaceTable *rep_table, const char *src, const char *dst);
|
||||
void ren_cp_replace_clear(CPReplaceTable *rep_table);
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user