First implementation of scaling for retina display

Introduce a new approach that discriminate coordinates in
points and pixels. Now all the logic from the Lua side and in
rencache is to always use points. The coordinates are converted
to pixels only within the renderer, in the file renderer.c.
In this way the application logic does not need to care about the
scaling of the retina displays.

For non-retina display the scaling between points and pixels is
equal to one so nothing will change.

There is nevertheless a change that leak into the Lua side. The
subpixel coordinates are in sub-pixel, not sub-points so they are
scaled by the retina scaling factor. But no change in the code is
required because the subpixel scaling factor take into account the
retina scaling, when present.

Because the retina scaling factor is not know when the application
starts but only when a window is actually available we introduce a
mechanism to render the font with a given scaling factor only from
the renderer when they are needed. We use therefore FontDesc to
describe the font information but without actually rasterizing the
font at a given scale.
This commit is contained in:
Francesco Abbate 2021-04-24 10:21:34 +02:00
parent 57e6de978b
commit 46c3bdea67
10 changed files with 272 additions and 126 deletions

View File

@ -72,9 +72,9 @@ static int f_draw_rect(lua_State *L) {
} }
static int draw_text_subpixel_impl(lua_State *L, bool draw_subpixel) { static int draw_text_subpixel_impl(lua_State *L, bool draw_subpixel) {
RenFont **font = luaL_checkudata(L, 1, API_TYPE_FONT); FontDesc *font_desc = luaL_checkudata(L, 1, API_TYPE_FONT);
const char *text = luaL_checkstring(L, 2); const char *text = luaL_checkstring(L, 2);
/* The coordinate below will be in subpixels iff draw_subpixel is true. /* The coordinate below will be in subpixel iff draw_subpixel is true.
Otherwise it will be in pixels. */ Otherwise it will be in pixels. */
int x_subpixel = luaL_checknumber(L, 3); int x_subpixel = luaL_checknumber(L, 3);
int y = luaL_checknumber(L, 4); int y = luaL_checknumber(L, 4);
@ -90,7 +90,7 @@ static int draw_text_subpixel_impl(lua_State *L, bool draw_subpixel) {
replace_color = (RenColor) {0}; replace_color = (RenColor) {0};
} }
x_subpixel = rencache_draw_text(*font, text, x_subpixel, y, color, draw_subpixel, rep_table, replace_color); x_subpixel = rencache_draw_text(font_desc, text, x_subpixel, y, color, draw_subpixel, rep_table, replace_color);
lua_pushnumber(L, x_subpixel); lua_pushnumber(L, x_subpixel);
return 1; return 1;
} }
@ -114,7 +114,7 @@ static const luaL_Reg lib[] = {
{ "draw_rect", f_draw_rect }, { "draw_rect", f_draw_rect },
{ "draw_text", f_draw_text }, { "draw_text", f_draw_text },
{ "draw_text_subpixel", f_draw_text_subpixel }, { "draw_text_subpixel", f_draw_text_subpixel },
{ NULL, NULL } { NULL, NULL }
}; };

View File

@ -1,8 +1,8 @@
#include "api.h" #include "api.h"
#include "fontdesc.h"
#include "renderer.h" #include "renderer.h"
#include "rencache.h" #include "rencache.h"
static int f_load(lua_State *L) { static int f_load(lua_State *L) {
const char *filename = luaL_checkstring(L, 1); const char *filename = luaL_checkstring(L, 1);
float size = luaL_checknumber(L, 2); float size = luaL_checknumber(L, 2);
@ -40,57 +40,72 @@ static int f_load(lua_State *L) {
} }
lua_pop(L, 1); lua_pop(L, 1);
} }
RenFont **self = lua_newuserdata(L, sizeof(*self));
if (ren_verify_font(filename)) {
luaL_error(L, "failed to load font");
}
FontDesc *font_desc = lua_newuserdata(L, sizeof(FontDesc));
// FIXME: implement font_desc initialization in fontdesc.c
int filename_sz = strlen(filename) + 1;
font_desc->filename = malloc(filename_sz);
memcpy(font_desc->filename, filename, filename_sz);
font_desc->size = size;
font_desc->options = font_options;
font_desc->tab_size = 4;
font_desc->fonts_scale_length = 0;
font_desc->recent_font_scale_index = 0; /* No need to initialize. */
luaL_setmetatable(L, API_TYPE_FONT); luaL_setmetatable(L, API_TYPE_FONT);
*self = ren_load_font(filename, size, font_options);
if (!*self) { luaL_error(L, "failed to load font"); }
return 1; return 1;
} }
static int f_set_tab_size(lua_State *L) { static int f_set_tab_size(lua_State *L) {
RenFont **self = luaL_checkudata(L, 1, API_TYPE_FONT); FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT);
int n = luaL_checknumber(L, 2); int n = luaL_checknumber(L, 2);
ren_set_font_tab_size(*self, n); font_desc_set_tab_size(self, n);
return 0; return 0;
} }
static int f_gc(lua_State *L) { static int f_gc(lua_State *L) {
RenFont **self = luaL_checkudata(L, 1, API_TYPE_FONT); FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT);
if (*self) { rencache_free_font(*self); } rencache_free_font(self);
return 0; return 0;
} }
static int f_get_width(lua_State *L) { static int f_get_width(lua_State *L) {
RenFont **self = luaL_checkudata(L, 1, API_TYPE_FONT); FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT);
const char *text = luaL_checkstring(L, 2); const char *text = luaL_checkstring(L, 2);
int subpixel_scale; /* By calling ren_get_font_width with NULL as third arguments
int w = ren_get_font_width(*self, text, &subpixel_scale); we will obtain the width in points. */
lua_pushnumber(L, ren_font_subpixel_round(w, subpixel_scale, 0)); int w = ren_get_font_width(self, text, NULL);
lua_pushnumber(L, w);
return 1; return 1;
} }
static int f_subpixel_scale(lua_State *L) { static int f_subpixel_scale(lua_State *L) {
RenFont **self = luaL_checkudata(L, 1, API_TYPE_FONT); FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT);
lua_pushnumber(L, ren_get_font_subpixel_scale(*self)); lua_pushnumber(L, ren_get_font_subpixel_scale(self));
return 1; return 1;
} }
static int f_get_width_subpixel(lua_State *L) { static int f_get_width_subpixel(lua_State *L) {
RenFont **self = luaL_checkudata(L, 1, API_TYPE_FONT); FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT);
const char *text = luaL_checkstring(L, 2); const char *text = luaL_checkstring(L, 2);
lua_pushnumber(L, ren_get_font_width(*self, text, NULL)); 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; return 1;
} }
static int f_get_height(lua_State *L) { static int f_get_height(lua_State *L) {
RenFont **self = luaL_checkudata(L, 1, API_TYPE_FONT); FontDesc *self = luaL_checkudata(L, 1, API_TYPE_FONT);
lua_pushnumber(L, ren_get_font_height(*self) ); lua_pushnumber(L, ren_get_font_height(self) );
return 1; return 1;
} }

View File

@ -109,10 +109,9 @@ top:
case SDL_WINDOWEVENT: case SDL_WINDOWEVENT:
if (e.window.event == SDL_WINDOWEVENT_RESIZED) { if (e.window.event == SDL_WINDOWEVENT_RESIZED) {
ren_resize(); ren_setup_renderer();
lua_pushstring(L, "resized"); lua_pushstring(L, "resized");
/* The size below can be wrong on Retina display by a multiplicative factor /* The size below will be in points. */
but the size reported below is not currently used. */
lua_pushnumber(L, e.window.data1); lua_pushnumber(L, e.window.data1);
lua_pushnumber(L, e.window.data2); lua_pushnumber(L, e.window.data2);
return 3; return 3;
@ -319,7 +318,7 @@ static int f_set_window_size(lua_State *L) {
double y = luaL_checknumber(L, 4); double y = luaL_checknumber(L, 4);
SDL_SetWindowSize(window, w, h); SDL_SetWindowSize(window, w, h);
SDL_SetWindowPosition(window, x, y); SDL_SetWindowPosition(window, x, y);
ren_resize(); ren_setup_renderer();
return 0; return 0;
} }

53
src/fontdesc.c Normal file
View File

@ -0,0 +1,53 @@
#include "fontdesc.h"
#include "renderer.h"
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->fonts_scale_length; i++) {
ren_set_font_tab_size(font_desc->fonts_scale[i].font, tab_size);
}
}
int font_desc_get_tab_size(FontDesc *font_desc) {
return font_desc->tab_size;
}
void font_desc_free(FontDesc *font_desc) {
for (int i = 0; i < font_desc->fonts_scale_length; i++) {
ren_free_font(font_desc->fonts_scale[i].font);
}
font_desc->fonts_scale_length = 0;
free(font_desc->filename);
}
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);
font_desc->fonts_scale[index].font = font;
font_desc->fonts_scale[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->fonts_scale_length; i++) {
if (font_desc->fonts_scale[i].scale == scale) {
index = i;
break;
}
}
if (index < 0) {
index = font_desc->fonts_scale_length;
if (index < FONT_SCALE_ARRAY_MAX) {
load_scaled_font(font_desc, index, scale);
font_desc->fonts_scale_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->recent_font_scale_index == 0 ? 1 : 0);
ren_free_font(font_desc->fonts_scale[index].font);
load_scaled_font(font_desc, index, scale);
}
}
font_desc->recent_font_scale_index = index;
return font_desc->fonts_scale[index].font;
}

33
src/fontdesc.h Normal file
View File

@ -0,0 +1,33 @@
#ifndef FONT_DESC_H
#define FONT_DESC_H
typedef struct RenFont RenFont;
// FIXME: find a better name for the struct below
struct FontScaled {
RenFont *font;
short int scale;
};
typedef struct FontScaled FontScaled;
#define FONT_SCALE_ARRAY_MAX 2
struct FontDesc {
char *filename;
float size;
unsigned int options;
short int tab_size;
// FIXME: find a better name for the array below
FontScaled fonts_scale[FONT_SCALE_ARRAY_MAX];
short int fonts_scale_length;
short int recent_font_scale_index; /* More recently scale used. */
};
typedef struct FontDesc FontDesc;
int font_desc_get_tab_size(FontDesc *font_desc);
void font_desc_set_tab_size(FontDesc *font_desc, int tab_size);
void font_desc_free(FontDesc *font_desc);
RenFont *font_desc_get_font_at_scale(FontDesc *font_desc, int scale);
#endif

View File

@ -5,6 +5,7 @@ lite_sources = [
'api/renderer_font.c', 'api/renderer_font.c',
'api/system.c', 'api/system.c',
'renderer.c', 'renderer.c',
'fontdesc.c',
'rencache.c', 'rencache.c',
'main.c', 'main.c',
] ]

View File

@ -24,7 +24,7 @@ typedef struct {
int32_t size; int32_t size;
RenRect rect; RenRect rect;
RenColor color; RenColor color;
RenFont *font; FontDesc *font_desc;
CPReplaceTable *replacements; CPReplaceTable *replacements;
RenColor replace_color; RenColor replace_color;
char text[0]; char text[0];
@ -115,9 +115,9 @@ void rencache_show_debug(bool enable) {
} }
void rencache_free_font(RenFont *font) { void rencache_free_font(FontDesc *font_desc) {
Command *cmd = push_command(FREE_FONT, COMMAND_BARE_SIZE); Command *cmd = push_command(FREE_FONT, COMMAND_BARE_SIZE);
if (cmd) { cmd->font = font; } if (cmd) { cmd->font_desc = font_desc; }
} }
@ -136,17 +136,17 @@ void rencache_draw_rect(RenRect rect, RenColor color) {
} }
} }
int rencache_draw_text(RenFont *font, int rencache_draw_text(FontDesc *font_desc,
const char *text, int x, int y, RenColor color, bool draw_subpixel, const char *text, int x, int y, RenColor color, bool draw_subpixel,
CPReplaceTable *replacements, RenColor replace_color) CPReplaceTable *replacements, RenColor replace_color)
{ {
int subpixel_scale; int subpixel_scale;
int w_subpixel = ren_get_font_width(font, text, &subpixel_scale); int w_subpixel = ren_get_font_width(font_desc, text, &subpixel_scale);
RenRect rect; RenRect rect;
rect.x = (draw_subpixel ? ren_font_subpixel_round(x, subpixel_scale, -1) : x); rect.x = (draw_subpixel ? ren_font_subpixel_round(x, subpixel_scale, -1) : x);
rect.y = y; rect.y = y;
rect.width = ren_font_subpixel_round(w_subpixel, subpixel_scale, 0); rect.width = ren_font_subpixel_round(w_subpixel, subpixel_scale, 0);
rect.height = ren_get_font_height(font); rect.height = ren_get_font_height(font_desc);
if (rects_overlap(screen_rect, rect)) { if (rects_overlap(screen_rect, rect)) {
int sz = strlen(text) + 1; int sz = strlen(text) + 1;
@ -154,11 +154,11 @@ int rencache_draw_text(RenFont *font,
if (cmd) { if (cmd) {
memcpy(cmd->text, text, sz); memcpy(cmd->text, text, sz);
cmd->color = color; cmd->color = color;
cmd->font = font; cmd->font_desc = font_desc;
cmd->rect = rect; cmd->rect = rect;
cmd->subpixel_scale = (draw_subpixel ? subpixel_scale : 1); cmd->subpixel_scale = (draw_subpixel ? subpixel_scale : 1);
cmd->x_subpixel_offset = x - subpixel_scale * rect.x; cmd->x_subpixel_offset = x - subpixel_scale * rect.x;
cmd->tab_size = ren_get_font_tab_size(font); cmd->tab_size = font_desc_get_tab_size(font_desc);
cmd->replacements = replacements; cmd->replacements = replacements;
cmd->replace_color = replace_color; cmd->replace_color = replace_color;
} }
@ -272,13 +272,13 @@ void rencache_end_frame(void) {
ren_draw_rect(cmd->rect, cmd->color); ren_draw_rect(cmd->rect, cmd->color);
break; break;
case DRAW_TEXT: case DRAW_TEXT:
ren_set_font_tab_size(cmd->font, cmd->tab_size); font_desc_set_tab_size(cmd->font_desc, cmd->tab_size);
ren_draw_text(cmd->font, cmd->text, cmd->rect.x, cmd->rect.y, cmd->color, ren_draw_text(cmd->font_desc, cmd->text, cmd->rect.x, cmd->rect.y, cmd->color,
cmd->replacements, cmd->replace_color); cmd->replacements, cmd->replace_color);
break; break;
case DRAW_TEXT_SUBPIXEL: case DRAW_TEXT_SUBPIXEL:
ren_set_font_tab_size(cmd->font, cmd->tab_size); font_desc_set_tab_size(cmd->font_desc, cmd->tab_size);
ren_draw_text_subpixel(cmd->font, cmd->text, 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->subpixel_scale * cmd->rect.x + cmd->x_subpixel_offset, cmd->rect.y, cmd->color,
cmd->replacements, cmd->replace_color); cmd->replacements, cmd->replace_color);
break; break;
@ -301,7 +301,7 @@ void rencache_end_frame(void) {
cmd = NULL; cmd = NULL;
while (next_command(&cmd)) { while (next_command(&cmd)) {
if (cmd->type == FREE_FONT) { if (cmd->type == FREE_FONT) {
ren_free_font(cmd->font); font_desc_free(cmd->font_desc);
} }
} }
} }

View File

@ -5,10 +5,11 @@
#include "renderer.h" #include "renderer.h"
void rencache_show_debug(bool enable); void rencache_show_debug(bool enable);
void rencache_free_font(RenFont *font); void rencache_free_font(FontDesc *font_desc);
void rencache_set_clip_rect(RenRect rect); void rencache_set_clip_rect(RenRect rect);
void rencache_draw_rect(RenRect rect, RenColor color); void rencache_draw_rect(RenRect rect, RenColor color);
int rencache_draw_text(RenFont *font, const char *text, int x, int y, RenColor color, bool draw_subpixel, CPReplaceTable *replacements, RenColor replace_color); int rencache_draw_text(FontDesc *font_desc, const char *text, int x, int y, RenColor color,
bool draw_subpixel, CPReplaceTable *replacements, RenColor replace_color);
void rencache_invalidate(void); void rencache_invalidate(void);
void rencache_begin_frame(void); void rencache_begin_frame(void);
void rencache_end_frame(void); void rencache_end_frame(void);

View File

@ -32,13 +32,16 @@ struct RenFont {
}; };
static SDL_Window *window; struct Renderer {
static SDL_Renderer *window_renderer = NULL; SDL_Window *window;
static SDL_Texture *window_texture = NULL; SDL_Renderer *renderer;
static SDL_Surface *window_surface = NULL; SDL_Texture *texture;
static int window_w = -1, window_h = -1; SDL_Surface *surface;
FR_Clip_Area clip; /* Clipping rect in pixel coordinates. */
int surface_scale;
};
static FR_Clip_Area clip; static struct Renderer renderer = {0};
static void* check_alloc(void *ptr) { static void* check_alloc(void *ptr) {
if (!ptr) { if (!ptr) {
@ -66,18 +69,30 @@ static const char* utf8_to_codepoint(const char *p, unsigned *dst) {
} }
static void init_window_surface() { static int get_surface_scale() {
if (window_surface) { int w_pixels, h_pixels;
SDL_FreeSurface(window_surface); int w_points, h_points;
} SDL_GL_GetDrawableSize(renderer.window, &w_pixels, &h_pixels);
SDL_GL_GetDrawableSize(window, &window_w, &window_h); SDL_GetWindowSize(renderer.window, &w_points, &h_points);
window_surface = SDL_CreateRGBSurfaceWithFormat(0, window_w, window_h, 32, SDL_PIXELFORMAT_BGRA32); // FIXME: this assert is too harsh.
ren_set_clip_rect( (RenRect) { 0, 0, window_w, window_h } ); assert(w_pixels % w_points == 0 && h_pixels % h_points == 0 && w_pixels / w_points == h_pixels / h_points);
return w_pixels / w_points;
} }
static SDL_Surface *get_window_surface() { static FR_Clip_Area scaled_clip(const RenRect rect, const int scale) {
return window_surface; return (FR_Clip_Area) {rect.x * scale, rect.y * scale, (rect.x + rect.width) * scale, (rect.y + rect.height) * scale};
}
static void init_window_surface() {
if (renderer.surface) {
SDL_FreeSurface(renderer.surface);
}
int w, h;
SDL_GL_GetDrawableSize(renderer.window, &w, &h);
renderer.surface = SDL_CreateRGBSurfaceWithFormat(0, w, h, 32, SDL_PIXELFORMAT_BGRA32);
renderer.clip = scaled_clip((RenRect) { 0, 0, w, h }, 1);
} }
@ -112,37 +127,39 @@ void ren_cp_replace_add(CPReplaceTable *rep_table, const char *src, const char *
} }
void ren_free_window_resources() { void ren_free_window_resources() {
SDL_DestroyWindow(window); SDL_DestroyWindow(renderer.window);
SDL_DestroyRenderer(window_renderer); SDL_DestroyRenderer(renderer.renderer);
SDL_DestroyTexture(window_texture); SDL_DestroyTexture(renderer.texture);
window = NULL; renderer.window = NULL;
window_renderer = NULL; renderer.renderer = NULL;
} }
static void setup_renderer(int w, int h) { static void setup_renderer(int w, int h) {
/* Note that w and h here should always be in pixels and obtained from /* Note that w and h here should always be in pixels and obtained from
a call to SDL_GL_GetDrawableSize(). */ a call to SDL_GL_GetDrawableSize(). */
if (window_renderer) { if (renderer.renderer) {
SDL_DestroyRenderer(window_renderer); SDL_DestroyRenderer(renderer.renderer);
SDL_DestroyTexture(window_texture); SDL_DestroyTexture(renderer.texture);
} }
window_renderer = SDL_CreateRenderer(window, -1, 0); renderer.renderer = SDL_CreateRenderer(renderer.window, -1, 0);
// May be we could use: SDL_CreateTextureFromSurface(sdlRenderer, mySurface); // May be we could use: SDL_CreateTextureFromSurface(sdlRenderer, mySurface);
window_texture = SDL_CreateTexture(window_renderer, SDL_PIXELFORMAT_BGRA32, SDL_TEXTUREACCESS_STREAMING, w, h); renderer.texture = SDL_CreateTexture(renderer.renderer, SDL_PIXELFORMAT_BGRA32, SDL_TEXTUREACCESS_STREAMING, w, h);
renderer.surface_scale = get_surface_scale();
} }
void ren_init(SDL_Window *win) { void ren_init(SDL_Window *win) {
assert(win); assert(win);
window = win; renderer.window = win;
init_window_surface(); init_window_surface();
renderer.surface_scale = get_surface_scale();
} }
void ren_resize() { void ren_setup_renderer() {
int new_w, new_h; int new_w, new_h;
SDL_GL_GetDrawableSize(window, &new_w, &new_h); SDL_GL_GetDrawableSize(renderer.window, &new_w, &new_h);
/* Note that (w, h) may differ from (new_w, new_h) on retina displays. */ /* Note that (w, h) may differ from (new_w, new_h) on retina displays. */
if (new_w != window_h || new_h != window_h) { if (new_w != renderer.surface->w || new_h != renderer.surface->h) {
init_window_surface(); init_window_surface();
setup_renderer(new_w, new_h); setup_renderer(new_w, new_h);
} }
@ -153,31 +170,28 @@ void ren_update_rects(RenRect *rects, int count) {
static bool initial_frame = true; static bool initial_frame = true;
if (initial_frame) { if (initial_frame) {
int w, h; int w, h;
SDL_ShowWindow(window); SDL_ShowWindow(renderer.window);
SDL_GL_GetDrawableSize(window, &w, &h); SDL_GL_GetDrawableSize(renderer.window, &w, &h);
setup_renderer(w, h); setup_renderer(w, h);
initial_frame = false; initial_frame = false;
} }
// FIXME: we ignore the rects here. // FIXME: we ignore the rects here.
SDL_UpdateTexture(window_texture, NULL, window_surface->pixels, window_w * 4); SDL_UpdateTexture(renderer.texture, NULL, renderer.surface->pixels, renderer.surface->w * 4);
SDL_RenderCopy(window_renderer, window_texture, NULL, NULL); SDL_RenderCopy(renderer.renderer, renderer.texture, NULL, NULL);
SDL_RenderPresent(window_renderer); SDL_RenderPresent(renderer.renderer);
} }
void ren_set_clip_rect(RenRect rect) { void ren_set_clip_rect(RenRect rect) {
clip.left = rect.x; renderer.clip = scaled_clip(rect, renderer.surface_scale);
clip.top = rect.y;
clip.right = rect.x + rect.width;
clip.bottom = rect.y + rect.height;
} }
void ren_get_size(int *x, int *y) { void ren_get_size(int *x, int *y) {
SDL_Surface *surf = get_window_surface(); const int scale = renderer.surface_scale;
*x = surf->w; *x = renderer.surface->w / scale;
*y = surf->h; *y = renderer.surface->h / scale;
} }
@ -214,6 +228,17 @@ static GlyphSet* get_glyphset(RenFont *font, int codepoint) {
} }
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* ren_load_font(const char *filename, float size, unsigned int renderer_flags) {
RenFont *font = NULL; RenFont *font = NULL;
@ -274,25 +299,34 @@ int ren_get_font_tab_size(RenFont *font) {
} }
int ren_get_font_width(RenFont *font, const char *text, int *subpixel_scale) { /* 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; int x = 0;
const char *p = text; const char *p = text;
unsigned codepoint; unsigned codepoint;
const int surface_scale = renderer.surface_scale;
RenFont *font = font_desc_get_font_at_scale(font_desc, surface_scale);
while (*p) { while (*p) {
p = utf8_to_codepoint(p, &codepoint); p = utf8_to_codepoint(p, &codepoint);
GlyphSet *set = get_glyphset(font, codepoint); GlyphSet *set = get_glyphset(font, codepoint);
FR_Bitmap_Glyph_Metrics *g = &set->glyphs[codepoint & 0xff]; FR_Bitmap_Glyph_Metrics *g = &set->glyphs[codepoint & 0xff];
x += g->xadvance; 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) { if (subpixel_scale) {
*subpixel_scale = FR_Subpixel_Scale(font->renderer); *subpixel_scale = x_scale_to_points;
return x;
} }
return x; return (x + x_scale_to_points / 2) / x_scale_to_points;
} }
int ren_get_font_height(RenFont *font) { int ren_get_font_height(FontDesc *font_desc) {
return font->height; const int surface_scale = renderer.surface_scale;
RenFont *font = font_desc_get_font_at_scale(font_desc, surface_scale);
return (font->height + surface_scale / 2) / surface_scale;
} }
@ -305,16 +339,6 @@ static inline RenColor blend_pixel(RenColor dst, RenColor src) {
} }
static inline RenColor blend_pixel2(RenColor dst, RenColor src, RenColor color) {
src.a = (src.a * color.a) >> 8;
int ia = 0xff - src.a;
dst.r = ((src.r * color.r * src.a) >> 16) + ((dst.r * ia) >> 8);
dst.g = ((src.g * color.g * src.a) >> 16) + ((dst.g * ia) >> 8);
dst.b = ((src.b * color.b * src.a) >> 16) + ((dst.b * ia) >> 8);
return dst;
}
#define rect_draw_loop(expr) \ #define rect_draw_loop(expr) \
for (int j = y1; j < y2; j++) { \ for (int j = y1; j < y2; j++) { \
for (int i = x1; i < x2; i++) { \ for (int i = x1; i < x2; i++) { \
@ -327,17 +351,24 @@ static inline RenColor blend_pixel2(RenColor dst, RenColor src, RenColor color)
void ren_draw_rect(RenRect rect, RenColor color) { void ren_draw_rect(RenRect rect, RenColor color) {
if (color.a == 0) { return; } if (color.a == 0) { return; }
int x1 = rect.x < clip.left ? clip.left : rect.x; const int surface_scale = renderer.surface_scale;
int y1 = rect.y < clip.top ? clip.top : rect.y;
/* transforms coordinates in pixels. */
rect.x *= surface_scale;
rect.y *= surface_scale;
rect.width *= surface_scale;
rect.height *= surface_scale;
int x1 = rect.x < renderer.clip.left ? renderer.clip.left : rect.x;
int y1 = rect.y < renderer.clip.top ? renderer.clip.top : rect.y;
int x2 = rect.x + rect.width; int x2 = rect.x + rect.width;
int y2 = rect.y + rect.height; int y2 = rect.y + rect.height;
x2 = x2 > clip.right ? clip.right : x2; x2 = x2 > renderer.clip.right ? renderer.clip.right : x2;
y2 = y2 > clip.bottom ? clip.bottom : y2; y2 = y2 > renderer.clip.bottom ? renderer.clip.bottom : y2;
SDL_Surface *surf = get_window_surface(); RenColor *d = (RenColor*) renderer.surface->pixels;
RenColor *d = (RenColor*) surf->pixels; d += x1 + y1 * renderer.surface->w;
d += x1 + y1 * surf->w; int dr = renderer.surface->w - (x2 - x1);
int dr = surf->w - (x2 - x1);
if (color.a == 0xff) { if (color.a == 0xff) {
rect_draw_loop(color); rect_draw_loop(color);
@ -358,13 +389,12 @@ static int codepoint_replace(CPReplaceTable *rep_table, unsigned *codepoint) {
return 0; return 0;
} }
static void draw_text_impl(RenFont *font, const char *text, int x_subpixel, int y_pixel, RenColor color,
void ren_draw_text_subpixel(RenFont *font, const char *text, int x_subpixel, int y, RenColor color,
CPReplaceTable *replacements, RenColor replace_color) CPReplaceTable *replacements, RenColor replace_color)
{ {
SDL_Surface *surf = renderer.surface;
const char *p = text; const char *p = text;
unsigned codepoint; unsigned codepoint;
SDL_Surface *surf = get_window_surface();
const FR_Color color_fr = { .r = color.r, .g = color.g, .b = color.b }; const FR_Color color_fr = { .r = color.r, .g = color.g, .b = color.b };
while (*p) { while (*p) {
FR_Color color_rep; FR_Color color_rep;
@ -381,18 +411,29 @@ void ren_draw_text_subpixel(RenFont *font, const char *text, int x_subpixel, int
color_rep = color_fr; color_rep = color_fr;
} }
if (color.a != 0) { if (color.a != 0) {
FR_Blend_Glyph(font->renderer, &clip, FR_Blend_Glyph(font->renderer, &renderer.clip,
x_subpixel, y, (uint8_t *) surf->pixels, surf->w, set->image, g, color_rep); x_subpixel, y_pixel, (uint8_t *) surf->pixels, surf->w, set->image, g, color_rep);
} }
x_subpixel += xadvance_original_cp; x_subpixel += xadvance_original_cp;
} }
} }
void ren_draw_text(RenFont *font, const char *text, int x, int y, RenColor color,
void ren_draw_text_subpixel(FontDesc *font_desc, const char *text, int x_subpixel, int y, RenColor color,
CPReplaceTable *replacements, RenColor replace_color) CPReplaceTable *replacements, RenColor replace_color)
{ {
const int subpixel_scale = FR_Subpixel_Scale(font->renderer); const int surface_scale = renderer.surface_scale;
ren_draw_text_subpixel(font, text, subpixel_scale * x, y, color, replacements, replace_color); 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 = renderer.surface_scale;
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 // Could be declared as static inline
@ -409,7 +450,8 @@ int ren_font_subpixel_round(int width, int subpixel_scale, int orientation) {
} }
int ren_get_font_subpixel_scale(RenFont *font) { int ren_get_font_subpixel_scale(FontDesc *font_desc) {
return FR_Subpixel_Scale(font->renderer); const int surface_scale = renderer.surface_scale;
RenFont *font = font_desc_get_font_at_scale(font_desc, surface_scale);
return FR_Subpixel_Scale(font->renderer) * surface_scale;
} }

View File

@ -3,9 +3,9 @@
#include <SDL.h> #include <SDL.h>
#include <stdint.h> #include <stdint.h>
#include "fontdesc.h"
typedef struct RenImage RenImage; typedef struct RenImage RenImage;
typedef struct RenFont RenFont;
enum { enum {
RenFontAntialiasingMask = 1, RenFontAntialiasingMask = 1,
@ -36,27 +36,29 @@ typedef struct CPReplaceTable CPReplaceTable;
void ren_init(SDL_Window *win); void ren_init(SDL_Window *win);
void ren_resize(); void ren_setup_renderer();
void ren_update_rects(RenRect *rects, int count); void ren_update_rects(RenRect *rects, int count);
void ren_set_clip_rect(RenRect rect); void ren_set_clip_rect(RenRect rect);
void ren_get_size(int *x, int *y); void ren_get_size(int *x, int *y); /* Reports the size in points. */
void ren_free_window_resources(); void ren_free_window_resources();
RenImage* ren_new_image(int width, int height); RenImage* ren_new_image(int width, int height);
void ren_free_image(RenImage *image); void ren_free_image(RenImage *image);
RenFont* ren_load_font(const char *filename, float size, unsigned int renderer_flags); 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_free_font(RenFont *font);
void ren_set_font_tab_size(RenFont *font, int n); void ren_set_font_tab_size(RenFont *font, int n);
int ren_get_font_tab_size(RenFont *font); int ren_get_font_tab_size(RenFont *font);
int ren_get_font_width(RenFont *font, const char *text, int *subpixel_scale);
int ren_get_font_height(RenFont *font); int ren_get_font_width(FontDesc *font_desc, const char *text, int *subpixel_scale);
int ren_get_font_subpixel_scale(RenFont *font); 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); int ren_font_subpixel_round(int width, int subpixel_scale, int orientation);
void ren_draw_rect(RenRect rect, RenColor color); void ren_draw_rect(RenRect rect, RenColor color);
void ren_draw_text(RenFont *font, const char *text, int x, int y, RenColor color, CPReplaceTable *replacements, RenColor replace_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(RenFont *font, const char *text, int x_subpixel, 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_init(CPReplaceTable *rep_table);
void ren_cp_replace_free(CPReplaceTable *rep_table); void ren_cp_replace_free(CPReplaceTable *rep_table);