Merge branch with support for retina display

This commit is contained in:
Francesco Abbate 2021-04-29 14:15:58 +02:00
commit a72431ace7
17 changed files with 520 additions and 135 deletions

View File

@ -85,10 +85,10 @@ lite_build_package_windows () {
echo "created package $package_name" echo "created package $package_name"
} }
lite_build_package_macosx () { lite_build_package_macos () {
local build="$1" local build="$1"
local arch="$2" local arch="$2"
local os="macosx" local os="macos"
local appdir=".package-build/lite-xl.app" local appdir=".package-build/lite-xl.app"
local bindir="$appdir/Contents/MacOS" local bindir="$appdir/Contents/MacOS"
@ -110,10 +110,7 @@ lite_build_package_macosx () {
mv "$package_name" .. mv "$package_name" ..
popd popd
rm -fr ".package-build" rm -fr ".package-build"
local dmg_name="lite-xl-$os-$arch.dmg"
rm -f "$dmg_name" && hdiutil create -volname lite-xl -srcfolder lite-xl.app -ov -format UDBZ "$dmg_name"
echo "created package $package_name" echo "created package $package_name"
echo "created disk image $dmg_name"
} }
lite_build_package_linux () { lite_build_package_linux () {
@ -161,7 +158,7 @@ lite_build_package () {
if [[ "$OSTYPE" == msys || "$OSTYPE" == win32 ]]; then if [[ "$OSTYPE" == msys || "$OSTYPE" == win32 ]]; then
lite_build_package_windows "$@" lite_build_package_windows "$@"
elif [[ "$OSTYPE" == "darwin"* ]]; then elif [[ "$OSTYPE" == "darwin"* ]]; then
lite_build_package_macosx "$@" lite_build_package_macos "$@"
elif [[ "$OSTYPE" == "linux"* || "$OSTYPE" == "freebsd"* ]]; then elif [[ "$OSTYPE" == "linux"* || "$OSTYPE" == "freebsd"* ]]; then
lite_build_package_linux "$@" lite_build_package_linux "$@"
else else

View File

@ -0,0 +1,97 @@
## Window creation for Retina displays
The file info.plist sets NSHighResolutionCapable to true. This is fine for High-DPI
and retina displays.
The `SDL_CreateWindow` is called with the flag `SDL_WINDOW_ALLOW_HIGHDPI`.
On Mac OS it means that, in source file `video/cocoa/SDL_cocoawindow.m`, from
function `Cocoa_CreateWindow`, SDL calls:
```objc
/* highdpi will be TRUE below */
BOOL highdpi = (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) != 0;
[contentView setWantsBestResolutionOpenGLSurface:highdpi]
```
Documentation for `setWantsBestResolutionOpenGLSurface`:
https://developer.apple.com/documentation/appkit/nsview/1414938-wantsbestresolutionopenglsurface
with more details in "OpenGL Programming Guide for Mac", chapter
"Optimizing OpenGL for High Resolution":
https://developer.apple.com/library/archive/documentation/GraphicsImaging/Conceptual/OpenGL-MacProgGuide/EnablingOpenGLforHighResolution/EnablingOpenGLforHighResolution.html#//apple_ref/doc/uid/TP40001987-CH1001-SW4
Citation from the official documentation:
You can opt in to high resolution by calling the method
`setWantsBestResolutionOpenGLSurface:` when you initialize the view, and
supplying YES as an argument:
```objc
[self setWantsBestResolutionOpenGLSurface:YES];
```
If you dont opt in, the system magnifies the rendered results.
The wantsBestResolutionOpenGLSurface property is relevant only for views to
which an NSOpenGLContext object is bound. Its value does not affect the behavior
of other views. For compatibility, wantsBestResolutionOpenGLSurface defaults to
NO, providing a 1-pixel-per-point framebuffer regardless of the backing scale
factor for the display the view occupies. Setting this property to YES for a
given view causes AppKit to allocate a higher-resolution framebuffer when
appropriate for the backing scale factor and target display.
To function correctly with wantsBestResolutionOpenGLSurface set to YES, a view
must perform correct conversions between view units (points) and pixel units as
needed. For example, the common practice of passing the width and height of
[self bounds] to glViewport() will yield incorrect results at high resolution,
because the parameters passed to the glViewport() function must be in pixels. As
a result, youll get only partial instead of complete coverage of the render
surface. Instead, use the backing store bounds:
```objc
[self convertRectToBacking:[self bounds]];
```
## Coordinates
The SDL function `SDL_GL_GetDrawableSize` will provide the size of the underlying drawable
in pixels. From the `SDL_video.h` header.
```c
/**
* \brief Get the size of a window's underlying drawable in pixels (for use
* with glViewport).
*
* \param window Window from which the drawable size should be queried
* \param w Pointer to variable for storing the width in pixels, may be NULL
* \param h Pointer to variable for storing the height in pixels, may be NULL
*
* This may differ from SDL_GetWindowSize() if we're rendering to a high-DPI
* drawable, i.e. the window was created with SDL_WINDOW_ALLOW_HIGHDPI on a
* platform with high-DPI support (Apple calls this "Retina"), and not disabled
* by the SDL_HINT_VIDEO_HIGHDPI_DISABLED hint.
*
* \sa SDL_GetWindowSize()
* \sa SDL_CreateWindow()
*/
extern DECLSPEC void SDLCALL SDL_GL_GetDrawableSize(SDL_Window * window, int *w,
int *h);
```
In turns it calls `Cocoa_GL_GetDrawableSize` from source file
`video/cocoa/SDL_cocoaopengl.m`. The function use the method
`[contentView convertRectToBacking:viewport]`:
```objc
if (window->flags & SDL_WINDOW_ALLOW_HIGHDPI) {
/* This gives us the correct viewport for a Retina-enabled view, only
* supported on 10.7+. */
if ([contentView respondsToSelector:@selector(convertRectToBacking:)]) {
viewport = [contentView convertRectToBacking:viewport];
}
}
```
to give back the sizes in pixels.

View File

@ -40,6 +40,11 @@ if host_machine.system() == 'windows'
lite_rc += windows.compile_resources('res.rc') lite_rc += windows.compile_resources('res.rc')
endif endif
# On macos we need to use the SDL renderer to support retina displays
if get_option('renderer') or host_machine.system() == 'darwin'
lite_cargs += '-DLITE_USE_SDL_RENDERER'
endif
subdir('lib/font_renderer') subdir('lib/font_renderer')
subdir('src') subdir('src')

View File

@ -1,2 +1,3 @@
option('portable', type : 'boolean', value : false, description: 'Portable install') option('portable', type : 'boolean', value : false, description: 'Portable install')
option('renderer', type : 'boolean', value : false, description: 'Use SDL renderer')

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;
} }

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,63 @@ 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, font_desc_alloc_size(filename));
font_desc_init(font_desc, filename, size, font_options);
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,7 +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_window();
lua_pushstring(L, "resized"); lua_pushstring(L, "resized");
/* The size below will be in points. */
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;
@ -316,6 +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_window();
return 0; return 0;
} }

76
src/fontdesc.c Normal file
View File

@ -0,0 +1,76 @@
#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_free(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;
}
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;
}

33
src/fontdesc.h Normal file
View File

@ -0,0 +1,33 @@
#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_free(FontDesc *font_desc);
RenFont *font_desc_get_font_at_scale(FontDesc *font_desc, int scale);
#endif

View File

@ -200,7 +200,7 @@ init_lua:
} }
lua_close(L); lua_close(L);
SDL_DestroyWindow(window); ren_free_window_resources();
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

View File

@ -5,6 +5,8 @@ lite_sources = [
'api/renderer_font.c', 'api/renderer_font.c',
'api/system.c', 'api/system.c',
'renderer.c', 'renderer.c',
'renwindow.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

@ -2,8 +2,9 @@
#include <stdbool.h> #include <stdbool.h>
#include <assert.h> #include <assert.h>
#include <math.h> #include <math.h>
#include "renderer.h"
#include "font_renderer.h" #include "font_renderer.h"
#include "renderer.h"
#include "renwindow.h"
#define MAX_GLYPHSET 256 #define MAX_GLYPHSET 256
#define REPLACEMENT_CHUNK_SIZE 8 #define REPLACEMENT_CHUNK_SIZE 8
@ -31,9 +32,7 @@ struct RenFont {
FR_Renderer *renderer; FR_Renderer *renderer;
}; };
static RenWindow window_renderer = {0};
static SDL_Window *window;
static FR_Clip_Area clip;
static void* check_alloc(void *ptr) { static void* check_alloc(void *ptr) {
if (!ptr) { if (!ptr) {
@ -91,37 +90,44 @@ void ren_cp_replace_add(CPReplaceTable *rep_table, const char *src, const char *
rep_table->size = table_size + 1; rep_table->size = table_size + 1;
} }
void ren_free_window_resources() {
renwin_free(&window_renderer);
}
void ren_init(SDL_Window *win) { void ren_init(SDL_Window *win) {
assert(win); assert(win);
window = win; window_renderer.window = win;
SDL_Surface *surf = SDL_GetWindowSurface(window); renwin_init_surface(&window_renderer);
ren_set_clip_rect( (RenRect) { 0, 0, surf->w, surf->h } ); renwin_clip_to_surface(&window_renderer);
}
void ren_resize_window() {
renwin_resize_surface(&window_renderer);
} }
void ren_update_rects(RenRect *rects, int count) { void ren_update_rects(RenRect *rects, int count) {
SDL_UpdateWindowSurfaceRects(window, (SDL_Rect*) rects, count);
static bool initial_frame = true; static bool initial_frame = true;
if (initial_frame) { if (initial_frame) {
SDL_ShowWindow(window); renwin_show_window(&window_renderer);
initial_frame = false; initial_frame = false;
} }
renwin_update_rects(&window_renderer, rects, count);
} }
void ren_set_clip_rect(RenRect rect) { void ren_set_clip_rect(RenRect rect) {
clip.left = rect.x; renwin_set_clip_rect(&window_renderer, rect);
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 = SDL_GetWindowSurface(window); RenWindow *ren = &window_renderer;
*x = surf->w; const int scale = renwin_surface_scale(ren);
*y = surf->h; SDL_Surface *surface = renwin_get_surface(ren);
*x = surface->w / scale;
*y = surface->h / scale;
} }
@ -158,6 +164,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;
@ -218,25 +235,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 = renwin_surface_scale(&window_renderer);
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 = 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;
} }
@ -249,16 +275,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++) { \
@ -271,17 +287,26 @@ 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 = renwin_surface_scale(&window_renderer);
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;
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 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 > clip.x + clip.width ? clip.x + clip.width : x2;
y2 = y2 > clip.bottom ? clip.bottom : y2; y2 = y2 > clip.y + clip.height ? clip.y + clip.height : y2;
SDL_Surface *surf = SDL_GetWindowSurface(window); SDL_Surface *surface = renwin_get_surface(&window_renderer);
RenColor *d = (RenColor*) surf->pixels; RenColor *d = (RenColor*) surface->pixels;
d += x1 + y1 * surf->w; d += x1 + y1 * surface->w;
int dr = surf->w - (x2 - x1); int dr = surface->w - (x2 - x1);
if (color.a == 0xff) { if (color.a == 0xff) {
rect_draw_loop(color); rect_draw_loop(color);
@ -290,38 +315,6 @@ void ren_draw_rect(RenRect rect, RenColor color) {
} }
} }
void ren_draw_image(RenImage *image, RenRect *sub, int x, int y, RenColor color) {
if (color.a == 0) { return; }
int n;
if ((n = clip.left - x) > 0) { sub->width -= n; sub->x += n; x += n; }
if ((n = clip.top - y) > 0) { sub->height -= n; sub->y += n; x += n; }
if ((n = x + sub->width - clip.right ) > 0) { sub->width -= n; }
if ((n = y + sub->height - clip.bottom) > 0) { sub->height -= n; }
if (sub->width <= 0 || sub->height <= 0) {
return;
}
/* draw */
SDL_Surface *surf = SDL_GetWindowSurface(window);
RenColor *s = image->pixels;
RenColor *d = (RenColor*) surf->pixels;
s += sub->x + sub->y * image->width;
d += x + y * surf->w;
int sr = image->width - sub->width;
int dr = surf->w - sub->width;
for (int j = 0; j < sub->height; j++) {
for (int i = 0; i < sub->width; i++) {
*d = blend_pixel2(*d, *s, color);
d++;
s++;
}
d += dr;
s += sr;
}
}
static int codepoint_replace(CPReplaceTable *rep_table, unsigned *codepoint) { static int codepoint_replace(CPReplaceTable *rep_table, unsigned *codepoint) {
for (int i = 0; i < rep_table->size; i++) { for (int i = 0; i < rep_table->size; i++) {
@ -335,12 +328,18 @@ static int codepoint_replace(CPReplaceTable *rep_table, unsigned *codepoint) {
} }
void ren_draw_text_subpixel(RenFont *font, const char *text, int x_subpixel, int y, RenColor color, 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) 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; const char *p = text;
unsigned codepoint; unsigned codepoint;
SDL_Surface *surf = SDL_GetWindowSurface(window);
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;
@ -358,17 +357,28 @@ void ren_draw_text_subpixel(RenFont *font, const char *text, int x_subpixel, int
} }
if (color.a != 0) { if (color.a != 0) {
FR_Blend_Glyph(font->renderer, &clip, FR_Blend_Glyph(font->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 = renwin_surface_scale(&window_renderer);
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 = 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 // Could be declared as static inline
@ -385,7 +395,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 = 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;
} }

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,26 +36,29 @@ typedef struct CPReplaceTable CPReplaceTable;
void ren_init(SDL_Window *win); void ren_init(SDL_Window *win);
void ren_resize_window();
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();
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_image(RenImage *image, RenRect *sub, int x, int y, 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(RenFont *font, 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_draw_text_subpixel(RenFont *font, 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);

125
src/renwindow.c Normal file
View File

@ -0,0 +1,125 @@
#include <assert.h>
#include "renwindow.h"
#ifdef LITE_USE_SDL_RENDERER
static int query_surface_scale(RenWindow *ren) {
int w_pixels, h_pixels;
int w_points, h_points;
SDL_GL_GetDrawableSize(ren->window, &w_pixels, &h_pixels);
SDL_GetWindowSize(ren->window, &w_points, &h_points);
/* We consider that the ratio pixel/point will always be an integer and
it is the same along the x and the y axis. */
assert(w_pixels % w_points == 0 && h_pixels % h_points == 0 && w_pixels / w_points == h_pixels / h_points);
return w_pixels / w_points;
}
#endif
void renwin_init_surface(RenWindow *ren) {
#ifdef LITE_USE_SDL_RENDERER
if (ren->surface) {
SDL_FreeSurface(ren->surface);
}
int w, h;
SDL_GL_GetDrawableSize(ren->window, &w, &h);
ren->surface = SDL_CreateRGBSurfaceWithFormat(0, w, h, 32, SDL_PIXELFORMAT_BGRA32);
ren->surface_scale = query_surface_scale(ren);
#endif
}
int renwin_surface_scale(RenWindow *ren) {
#ifdef LITE_USE_SDL_RENDERER
return ren->surface_scale;
#else
return 1;
#endif
}
static RenRect scaled_rect(const RenRect rect, const int scale) {
return (RenRect) {rect.x * scale, rect.y * scale, rect.width * scale, rect.height * scale};
}
void renwin_clip_to_surface(RenWindow *ren) {
SDL_Surface *surface = renwin_get_surface(ren);
ren->clip = (RenRect) {0, 0, surface->w, surface->h};
}
void renwin_set_clip_rect(RenWindow *ren, RenRect rect) {
ren->clip = scaled_rect(rect, renwin_surface_scale(ren));
}
SDL_Surface *renwin_get_surface(RenWindow *ren) {
#ifdef LITE_USE_SDL_RENDERER
return ren->surface;
#else
return SDL_GetWindowSurface(ren->window);
#endif
}
#ifdef LITE_USE_SDL_RENDERER
static void setup_renderer(RenWindow *ren, int w, int h) {
/* Note that w and h here should always be in pixels and obtained from
a call to SDL_GL_GetDrawableSize(). */
if (ren->renderer) {
SDL_DestroyRenderer(ren->renderer);
SDL_DestroyTexture(ren->texture);
}
ren->renderer = SDL_CreateRenderer(ren->window, -1, 0);
ren->texture = SDL_CreateTexture(ren->renderer, SDL_PIXELFORMAT_BGRA32, SDL_TEXTUREACCESS_STREAMING, w, h);
ren->surface_scale = query_surface_scale(ren);
}
#endif
void renwin_resize_surface(RenWindow *ren) {
#ifdef LITE_USE_SDL_RENDERER
int new_w, new_h;
SDL_GL_GetDrawableSize(ren->window, &new_w, &new_h);
/* Note that (w, h) may differ from (new_w, new_h) on retina displays. */
if (new_w != ren->surface->w || new_h != ren->surface->h) {
renwin_init_surface(ren);
renwin_clip_to_surface(ren);
setup_renderer(ren, new_w, new_h);
}
#endif
}
void renwin_show_window(RenWindow *ren) {
SDL_ShowWindow(ren->window);
#ifdef LITE_USE_SDL_RENDERER
int w, h;
SDL_GL_GetDrawableSize(ren->window, &w, &h);
setup_renderer(ren, w, h);
#endif
}
void renwin_update_rects(RenWindow *ren, RenRect *rects, int count) {
#ifdef LITE_USE_SDL_RENDERER
const int scale = ren->surface_scale;
for (int i = 0; i < count; i++) {
const RenRect *r = &rects[i];
const int x = scale * r->x, y = scale * r->y;
const int w = scale * r->width, h = scale * r->height;
const SDL_Rect sr = {.x = x, .y = y, .w = w, .h = h};
int32_t *pixels = ((int32_t *) ren->surface->pixels) + x + ren->surface->w * y;
SDL_UpdateTexture(ren->texture, &sr, pixels, ren->surface->w * 4);
}
SDL_RenderCopy(ren->renderer, ren->texture, NULL, NULL);
SDL_RenderPresent(ren->renderer);
#else
SDL_UpdateWindowSurfaceRects(ren->window, (SDL_Rect*) rects, count);
#endif
}
void renwin_free(RenWindow *ren) {
SDL_DestroyWindow(ren->window);
ren->window = NULL;
#ifdef LITE_USE_SDL_RENDERER
SDL_DestroyRenderer(ren->renderer);
SDL_DestroyTexture(ren->texture);
SDL_FreeSurface(ren->surface);
#endif
}

25
src/renwindow.h Normal file
View File

@ -0,0 +1,25 @@
#include <SDL.h>
#include "renderer.h"
struct RenWindow {
SDL_Window *window;
RenRect clip; /* Clipping rect in pixel coordinates. */
#ifdef LITE_USE_SDL_RENDERER
SDL_Renderer *renderer;
SDL_Texture *texture;
SDL_Surface *surface;
int surface_scale;
#endif
};
typedef struct RenWindow RenWindow;
void renwin_init_surface(RenWindow *ren);
int renwin_surface_scale(RenWindow *ren);
void renwin_clip_to_surface(RenWindow *ren);
void renwin_set_clip_rect(RenWindow *ren, RenRect rect);
void renwin_resize_surface(RenWindow *ren);
void renwin_show_window(RenWindow *ren);
void renwin_update_rects(RenWindow *ren, RenRect *rects, int count);
void renwin_free(RenWindow *ren);
SDL_Surface *renwin_get_surface(RenWindow *ren);