Compare commits
5 Commits
amiga2.1
...
rencache-t
Author | SHA1 | Date |
---|---|---|
Francesco Abbate | 1b39a4cb37 | |
Francesco Abbate | 4e9208f768 | |
Francesco Abbate | 23c6b182f6 | |
Francesco Abbate | ed3acbc29b | |
Francesco Abbate | 2b6867b4bc |
|
@ -7,6 +7,7 @@
|
|||
|
||||
#define API_TYPE_FONT "Font"
|
||||
#define API_TYPE_REPLACE "Replace"
|
||||
#define API_TYPE_RENCACHE "RenCache"
|
||||
|
||||
void api_load_libs(lua_State *L);
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "renderer.h"
|
||||
#include "rencache.h"
|
||||
|
||||
extern RenCache *window_rencache;
|
||||
|
||||
static RenColor checkcolor(lua_State *L, int idx, int def) {
|
||||
RenColor color;
|
||||
|
@ -21,16 +22,27 @@ static RenColor checkcolor(lua_State *L, int idx, int def) {
|
|||
}
|
||||
|
||||
|
||||
static RenCache *opt_rencache_arg(lua_State *L, int *index) {
|
||||
RenCache *rencache;
|
||||
if (lua_touserdata(L, 1)) {
|
||||
return luaL_checkudata(L, *(index++), API_TYPE_RENCACHE);
|
||||
}
|
||||
return window_rencache;
|
||||
}
|
||||
|
||||
|
||||
static int f_show_debug(lua_State *L) {
|
||||
luaL_checkany(L, 1);
|
||||
rencache_show_debug(lua_toboolean(L, 1));
|
||||
int index = 1;
|
||||
RenCache *rencache = opt_rencache_arg(L, &index);
|
||||
luaL_checkany(L, index);
|
||||
rencache_show_debug(rencache, lua_toboolean(L, index));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int f_get_size(lua_State *L) {
|
||||
int w, h;
|
||||
ren_get_size(&w, &h);
|
||||
ren_get_size(window_ren_surface, &w, &h);
|
||||
lua_pushnumber(L, w);
|
||||
lua_pushnumber(L, h);
|
||||
return 2;
|
||||
|
@ -38,59 +50,69 @@ static int f_get_size(lua_State *L) {
|
|||
|
||||
|
||||
static int f_begin_frame(lua_State *L) {
|
||||
rencache_begin_frame(L);
|
||||
int index = 1;
|
||||
RenCache *rencache = opt_rencache_arg(L, &index);
|
||||
rencache_begin_frame(rencache, window_ren_surface, L);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int f_end_frame(lua_State *L) {
|
||||
rencache_end_frame(L);
|
||||
int index = 1;
|
||||
RenCache *rencache = opt_rencache_arg(L, &index);
|
||||
rencache_end_frame(rencache, L);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int f_set_clip_rect(lua_State *L) {
|
||||
RenRect rect;
|
||||
rect.x = luaL_checknumber(L, 1);
|
||||
rect.y = luaL_checknumber(L, 2);
|
||||
rect.width = luaL_checknumber(L, 3);
|
||||
rect.height = luaL_checknumber(L, 4);
|
||||
rencache_set_clip_rect(rect);
|
||||
int index = 1;
|
||||
RenCache *rencache = opt_rencache_arg(L, &index);
|
||||
rect.x = luaL_checknumber(L, index);
|
||||
rect.y = luaL_checknumber(L, index + 1);
|
||||
rect.width = luaL_checknumber(L, index + 2);
|
||||
rect.height = luaL_checknumber(L, index + 3);
|
||||
rencache_set_clip_rect(rencache, rect);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int f_draw_rect(lua_State *L) {
|
||||
RenRect rect;
|
||||
rect.x = luaL_checknumber(L, 1);
|
||||
rect.y = luaL_checknumber(L, 2);
|
||||
rect.width = luaL_checknumber(L, 3);
|
||||
rect.height = luaL_checknumber(L, 4);
|
||||
RenColor color = checkcolor(L, 5, 255);
|
||||
rencache_draw_rect(rect, color);
|
||||
int index = 1;
|
||||
RenCache *rencache = opt_rencache_arg(L, &index);
|
||||
rect.x = luaL_checknumber(L, index);
|
||||
rect.y = luaL_checknumber(L, index + 1);
|
||||
rect.width = luaL_checknumber(L, index + 2);
|
||||
rect.height = luaL_checknumber(L, index + 3);
|
||||
RenColor color = checkcolor(L, index + 4, 255);
|
||||
rencache_draw_rect(rencache, rect, color);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int draw_text_subpixel_impl(lua_State *L, bool draw_subpixel) {
|
||||
FontDesc *font_desc = luaL_checkudata(L, 1, API_TYPE_FONT);
|
||||
const char *text = luaL_checkstring(L, 2);
|
||||
int index = 1;
|
||||
RenCache *rencache = opt_rencache_arg(L, &index);
|
||||
FontDesc *font_desc = luaL_checkudata(L, index, API_TYPE_FONT);
|
||||
const char *text = luaL_checkstring(L, index + 1);
|
||||
/* 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);
|
||||
int y = luaL_checknumber(L, 4);
|
||||
RenColor color = checkcolor(L, 5, 255);
|
||||
int x_subpixel = luaL_checknumber(L, index + 2);
|
||||
int y = luaL_checknumber(L, index + 3);
|
||||
RenColor color = checkcolor(L, index + 4, 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);
|
||||
if (lua_gettop(L) >= index + 6) {
|
||||
rep_table = luaL_checkudata(L, index + 5, API_TYPE_REPLACE);
|
||||
replace_color = checkcolor(L, index + 6, 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);
|
||||
x_subpixel = rencache_draw_text(rencache, L, font_desc, 1, text, x_subpixel, y, color, draw_subpixel, rep_table, replace_color);
|
||||
lua_pushnumber(L, x_subpixel);
|
||||
return 1;
|
||||
}
|
||||
|
@ -123,6 +145,14 @@ int luaopen_renderer_replacements(lua_State *L);
|
|||
|
||||
int luaopen_renderer(lua_State *L) {
|
||||
luaL_newlib(L, lib);
|
||||
|
||||
window_rencache = lua_newuserdata(L, sizeof(RenCache));
|
||||
rencache_init(window_rencache);
|
||||
luaL_setmetatable(L, API_TYPE_RENCACHE);
|
||||
lua_pushvalue(L, -1);
|
||||
luaL_ref(L, -1);
|
||||
lua_setfield(L, -2, "window");
|
||||
|
||||
luaopen_renderer_font(L);
|
||||
lua_setfield(L, -2, "font");
|
||||
luaopen_renderer_replacements(L);
|
||||
|
|
|
@ -74,7 +74,7 @@ static int f_get_width(lua_State *L) {
|
|||
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);
|
||||
int w = ren_get_font_width(window_ren_surface, self, text, NULL);
|
||||
lua_pushnumber(L, w);
|
||||
return 1;
|
||||
}
|
||||
|
@ -82,7 +82,7 @@ static int f_get_width(lua_State *L) {
|
|||
|
||||
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));
|
||||
lua_pushnumber(L, ren_get_font_subpixel_scale(window_ren_surface, self));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -92,14 +92,14 @@ static int f_get_width_subpixel(lua_State *L) {
|
|||
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));
|
||||
lua_pushnumber(L, ren_get_font_width(window_ren_surface, 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) );
|
||||
lua_pushnumber(L, ren_get_font_height(window_ren_surface, self) );
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
#endif
|
||||
|
||||
extern SDL_Window *window;
|
||||
|
||||
extern RenCache *window_rencache;
|
||||
|
||||
static const char* button_name(int button) {
|
||||
switch (button) {
|
||||
|
@ -116,7 +116,7 @@ top:
|
|||
lua_pushnumber(L, e.window.data2);
|
||||
return 3;
|
||||
} else if (e.window.event == SDL_WINDOWEVENT_EXPOSED) {
|
||||
rencache_invalidate();
|
||||
rencache_invalidate(&rencache);
|
||||
lua_pushstring(L, "exposed");
|
||||
return 1;
|
||||
} else if (e.window.event == SDL_WINDOWEVENT_MINIMIZED) {
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
|
||||
SDL_Window *window;
|
||||
RenCache *window_rencache;
|
||||
|
||||
static double get_scale(void) {
|
||||
#ifdef _WIN32
|
||||
|
|
166
src/rencache.c
166
src/rencache.c
|
@ -5,18 +5,10 @@
|
|||
#include <lauxlib.h>
|
||||
#include "rencache.h"
|
||||
|
||||
/* a cache over the software renderer -- all drawing operations are stored as
|
||||
** commands when issued. At the end of the frame we write the commands to a grid
|
||||
** of hash values, take the cells that have changed since the previous frame,
|
||||
** merge them into dirty rectangles and redraw only those regions */
|
||||
|
||||
#define CELLS_X 80
|
||||
#define CELLS_Y 50
|
||||
#define CELL_SIZE 96
|
||||
#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, DRAW_TEXT_SUBPIXEL, DRAW_IMAGE };
|
||||
|
||||
typedef struct {
|
||||
int8_t type;
|
||||
|
@ -29,6 +21,7 @@ typedef struct {
|
|||
FontDesc *font_desc;
|
||||
CPReplaceTable *replacements;
|
||||
RenColor replace_color;
|
||||
uint32_t image_id; /* FIXME: do not add a new field but use unions or alias another field. */
|
||||
char text[0];
|
||||
} Command;
|
||||
|
||||
|
@ -41,18 +34,6 @@ 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;
|
||||
static unsigned *cells = cells_buf2;
|
||||
static RenRect rect_buf[CELLS_X * CELLS_Y / 2];
|
||||
static char command_buf[COMMAND_BUF_SIZE];
|
||||
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; }
|
||||
|
||||
|
@ -123,14 +104,14 @@ static RenRect merge_rects(RenRect a, RenRect b) {
|
|||
}
|
||||
|
||||
|
||||
static Command* push_command(int type, int size) {
|
||||
Command *cmd = (Command*) (command_buf + command_buf_idx);
|
||||
int n = command_buf_idx + size;
|
||||
static Command* push_command(RenCache *rc, int type, int size) {
|
||||
Command *cmd = (Command*) (rc->command_buf + rc->command_buf_idx);
|
||||
int n = rc->command_buf_idx + size;
|
||||
if (n > COMMAND_BUF_SIZE) {
|
||||
fprintf(stderr, "Warning: (" __FILE__ "): exhausted command buffer\n");
|
||||
return NULL;
|
||||
}
|
||||
command_buf_idx = n;
|
||||
rc->command_buf_idx = n;
|
||||
memset(cmd, 0, COMMAND_BARE_SIZE);
|
||||
cmd->type = type;
|
||||
cmd->size = size;
|
||||
|
@ -138,51 +119,70 @@ static Command* push_command(int type, int size) {
|
|||
}
|
||||
|
||||
|
||||
static bool next_command(Command **prev) {
|
||||
static bool next_command(RenCache *rc, Command **prev) {
|
||||
if (*prev == NULL) {
|
||||
*prev = (Command*) command_buf;
|
||||
*prev = (Command*) rc->command_buf;
|
||||
} else {
|
||||
*prev = (Command*) (((char*) *prev) + (*prev)->size);
|
||||
}
|
||||
return *prev != ((Command*) (command_buf + command_buf_idx));
|
||||
return *prev != ((Command*) (rc->command_buf + rc->command_buf_idx));
|
||||
}
|
||||
|
||||
void rencache_init(RenCache *rc) {
|
||||
rc->cells_prev = rc->cells_buf1;
|
||||
rc->cells = rc->cells_buf2;
|
||||
}
|
||||
|
||||
|
||||
void rencache_show_debug(bool enable) {
|
||||
show_debug = enable;
|
||||
void rencache_show_debug(RenCache *rc, bool enable) {
|
||||
rc->show_debug = enable;
|
||||
}
|
||||
|
||||
|
||||
void rencache_set_clip_rect(RenRect rect) {
|
||||
Command *cmd = push_command(SET_CLIP, COMMAND_BARE_SIZE);
|
||||
if (cmd) { cmd->rect = intersect_rects(rect, screen_rect); }
|
||||
void rencache_set_clip_rect(RenCache *rc, RenRect rect) {
|
||||
Command *cmd = push_command(rc, SET_CLIP, COMMAND_BARE_SIZE);
|
||||
if (cmd) { cmd->rect = intersect_rects(rect, rc->screen_rect); }
|
||||
}
|
||||
|
||||
|
||||
void rencache_draw_rect(RenRect rect, RenColor color) {
|
||||
if (!rects_overlap(screen_rect, rect)) { return; }
|
||||
Command *cmd = push_command(DRAW_RECT, COMMAND_BARE_SIZE);
|
||||
void rencache_draw_rect(RenCache *rc, RenRect rect, RenColor color) {
|
||||
if (!rects_overlap(rc->screen_rect, rect)) { return; }
|
||||
Command *cmd = push_command(rc, DRAW_RECT, COMMAND_BARE_SIZE);
|
||||
if (cmd) {
|
||||
cmd->rect = rect;
|
||||
cmd->color = color;
|
||||
}
|
||||
}
|
||||
|
||||
int rencache_draw_text(lua_State *L, FontDesc *font_desc, int font_index,
|
||||
|
||||
void rencache_draw_image(RenCache *rc, int image_id, int x, int y, RenRect image_rect) {
|
||||
RenRect rect = {x, y, image_rect.width, image_rect.height};
|
||||
if (!rects_overlap(rc->screen_rect, rect)) { return; }
|
||||
Command *cmd = push_command(rc, DRAW_IMAGE, COMMAND_BARE_SIZE + 2 * sizeof(int));
|
||||
int *img_coord = (int *) cmd->text;
|
||||
if (cmd) {
|
||||
cmd->rect = rect;
|
||||
cmd->image_id = image_id;
|
||||
img_coord[0] = image_rect.x;
|
||||
img_coord[1] = image_rect.y;
|
||||
}
|
||||
}
|
||||
|
||||
int rencache_draw_text(RenCache *rc, 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)
|
||||
{
|
||||
int subpixel_scale;
|
||||
int w_subpixel = ren_get_font_width(font_desc, text, &subpixel_scale);
|
||||
int w_subpixel = ren_get_font_width(rc->ren_surface, 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);
|
||||
rect.height = ren_get_font_height(rc->ren_surface, font_desc);
|
||||
|
||||
if (rects_overlap(screen_rect, rect) && font_refs_add(L, font_desc, font_index) >= 0) {
|
||||
if (rects_overlap(rc->screen_rect, rect) && font_refs_add(L, font_desc, font_index) >= 0) {
|
||||
int sz = strlen(text) + 1;
|
||||
Command *cmd = push_command(draw_subpixel ? DRAW_TEXT_SUBPIXEL : DRAW_TEXT, COMMAND_BARE_SIZE + sz);
|
||||
Command *cmd = push_command(rc, draw_subpixel ? DRAW_TEXT_SUBPIXEL : DRAW_TEXT, COMMAND_BARE_SIZE + sz);
|
||||
if (cmd) {
|
||||
memcpy(cmd->text, text, sz);
|
||||
cmd->color = color;
|
||||
|
@ -200,25 +200,26 @@ int rencache_draw_text(lua_State *L, FontDesc *font_desc, int font_index,
|
|||
}
|
||||
|
||||
|
||||
void rencache_invalidate(void) {
|
||||
memset(cells_prev, 0xff, sizeof(cells_buf1));
|
||||
void rencache_invalidate(RenCache *rc) {
|
||||
memset(rc->cells_prev, 0xff, sizeof(rc->cells_buf1));
|
||||
}
|
||||
|
||||
|
||||
void rencache_begin_frame(lua_State *L) {
|
||||
void rencache_begin_frame(RenCache *rc, RenSurface *ren, lua_State *L) {
|
||||
/* reset all cells if the screen width/height has changed */
|
||||
int w, h;
|
||||
ren_get_size(&w, &h);
|
||||
if (screen_rect.width != w || h != screen_rect.height) {
|
||||
screen_rect.width = w;
|
||||
screen_rect.height = h;
|
||||
rencache_invalidate();
|
||||
ren_get_size(ren, &w, &h);
|
||||
if (rc->screen_rect.width != w || h != rc->screen_rect.height) {
|
||||
rc->screen_rect.width = w;
|
||||
rc->screen_rect.height = h;
|
||||
rencache_invalidate(rc);
|
||||
}
|
||||
rc->ren_surface = ren;
|
||||
font_refs_clear(L);
|
||||
}
|
||||
|
||||
|
||||
static void update_overlapping_cells(RenRect r, unsigned h) {
|
||||
static void update_overlapping_cells(RenCache *rc, RenRect r, unsigned h) {
|
||||
int x1 = r.x / CELL_SIZE;
|
||||
int y1 = r.y / CELL_SIZE;
|
||||
int x2 = (r.x + r.width) / CELL_SIZE;
|
||||
|
@ -227,108 +228,115 @@ static void update_overlapping_cells(RenRect r, unsigned h) {
|
|||
for (int y = y1; y <= y2; y++) {
|
||||
for (int x = x1; x <= x2; x++) {
|
||||
int idx = cell_idx(x, y);
|
||||
hash(&cells[idx], &h, sizeof(h));
|
||||
hash(&rc->cells[idx], &h, sizeof(h));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void push_rect(RenRect r, int *count) {
|
||||
static void push_rect(RenCache *rc, RenRect r, int *count) {
|
||||
/* try to merge with existing rectangle */
|
||||
for (int i = *count - 1; i >= 0; i--) {
|
||||
RenRect *rp = &rect_buf[i];
|
||||
RenRect *rp = &rc->rect_buf[i];
|
||||
if (rects_overlap(*rp, r)) {
|
||||
*rp = merge_rects(*rp, r);
|
||||
return;
|
||||
}
|
||||
}
|
||||
/* couldn't merge with previous rectangle: push */
|
||||
rect_buf[(*count)++] = r;
|
||||
rc->rect_buf[(*count)++] = r;
|
||||
}
|
||||
|
||||
|
||||
void rencache_end_frame(lua_State *L) {
|
||||
void rencache_end_frame(RenCache *rc, lua_State *L) {
|
||||
/* update cells from commands */
|
||||
Command *cmd = NULL;
|
||||
RenRect cr = screen_rect;
|
||||
while (next_command(&cmd)) {
|
||||
RenRect cr = rc->screen_rect;
|
||||
while (next_command(rc, &cmd)) {
|
||||
if (cmd->type == SET_CLIP) { cr = cmd->rect; }
|
||||
RenRect r = intersect_rects(cmd->rect, cr);
|
||||
if (r.width == 0 || r.height == 0) { continue; }
|
||||
unsigned h = HASH_INITIAL;
|
||||
hash(&h, cmd, cmd->size);
|
||||
update_overlapping_cells(r, h);
|
||||
update_overlapping_cells(rc, r, h);
|
||||
}
|
||||
|
||||
/* push rects for all cells changed from last frame, reset cells */
|
||||
int rect_count = 0;
|
||||
int max_x = screen_rect.width / CELL_SIZE + 1;
|
||||
int max_y = screen_rect.height / CELL_SIZE + 1;
|
||||
int max_x = rc->screen_rect.width / CELL_SIZE + 1;
|
||||
int max_y = rc->screen_rect.height / CELL_SIZE + 1;
|
||||
for (int y = 0; y < max_y; y++) {
|
||||
for (int x = 0; x < max_x; x++) {
|
||||
/* compare previous and current cell for change */
|
||||
int idx = cell_idx(x, y);
|
||||
if (cells[idx] != cells_prev[idx]) {
|
||||
push_rect((RenRect) { x, y, 1, 1 }, &rect_count);
|
||||
if (rc->cells[idx] != rc->cells_prev[idx]) {
|
||||
push_rect(rc, (RenRect) { x, y, 1, 1 }, &rect_count);
|
||||
}
|
||||
cells_prev[idx] = HASH_INITIAL;
|
||||
rc->cells_prev[idx] = HASH_INITIAL;
|
||||
}
|
||||
}
|
||||
|
||||
/* expand rects from cells to pixels */
|
||||
for (int i = 0; i < rect_count; i++) {
|
||||
RenRect *r = &rect_buf[i];
|
||||
RenRect *r = &rc->rect_buf[i];
|
||||
r->x *= CELL_SIZE;
|
||||
r->y *= CELL_SIZE;
|
||||
r->width *= CELL_SIZE;
|
||||
r->height *= CELL_SIZE;
|
||||
*r = intersect_rects(*r, screen_rect);
|
||||
*r = intersect_rects(*r, rc->screen_rect);
|
||||
}
|
||||
|
||||
/* redraw updated regions */
|
||||
for (int i = 0; i < rect_count; i++) {
|
||||
const int *image_coord;
|
||||
/* draw */
|
||||
RenRect r = rect_buf[i];
|
||||
ren_set_clip_rect(r);
|
||||
RenRect r = rc->rect_buf[i];
|
||||
ren_set_clip_rect(rc->ren_surface, r);
|
||||
|
||||
cmd = NULL;
|
||||
while (next_command(&cmd)) {
|
||||
while (next_command(rc, &cmd)) {
|
||||
switch (cmd->type) {
|
||||
case SET_CLIP:
|
||||
ren_set_clip_rect(intersect_rects(cmd->rect, r));
|
||||
ren_set_clip_rect(rc->ren_surface, intersect_rects(cmd->rect, r));
|
||||
break;
|
||||
case DRAW_RECT:
|
||||
ren_draw_rect(cmd->rect, cmd->color);
|
||||
ren_draw_rect(rc->ren_surface, 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,
|
||||
ren_draw_text(rc->ren_surface, 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,
|
||||
ren_draw_text_subpixel(rc->ren_surface, 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);
|
||||
break;
|
||||
case DRAW_IMAGE:
|
||||
image_coord = (const int *) cmd->text;
|
||||
ren_draw_image(rc->ren_surface, cmd->image_id, image_coord[0], image_coord[1], cmd->rect);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (show_debug) {
|
||||
if (rc->show_debug) {
|
||||
RenColor color = { rand(), rand(), rand(), 50 };
|
||||
ren_draw_rect(r, color);
|
||||
ren_draw_rect(rc->ren_surface, r, color);
|
||||
}
|
||||
}
|
||||
|
||||
/* update dirty rects */
|
||||
if (rect_count > 0) {
|
||||
ren_update_rects(rect_buf, rect_count);
|
||||
ren_update_rects(rc->ren_surface, rc->rect_buf, rect_count);
|
||||
}
|
||||
|
||||
/* swap cell buffer and reset */
|
||||
unsigned *tmp = cells;
|
||||
cells = cells_prev;
|
||||
cells_prev = tmp;
|
||||
command_buf_idx = 0;
|
||||
unsigned *tmp = rc->cells;
|
||||
rc->cells = rc->cells_prev;
|
||||
rc->cells_prev = tmp;
|
||||
rc->command_buf_idx = 0;
|
||||
|
||||
rc->ren_surface = NULL;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,13 +5,37 @@
|
|||
#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,
|
||||
/* a cache over the software renderer -- all drawing operations are stored as
|
||||
** commands when issued. At the end of the frame we write the commands to a grid
|
||||
** of hash values, take the cells that have changed since the previous frame,
|
||||
** merge them into dirty rectangles and redraw only those regions */
|
||||
|
||||
#define CELLS_X 80
|
||||
#define CELLS_Y 50
|
||||
#define COMMAND_BUF_SIZE (1024 * 512)
|
||||
|
||||
struct RenCache {
|
||||
unsigned cells_buf1[CELLS_X * CELLS_Y];
|
||||
unsigned cells_buf2[CELLS_X * CELLS_Y];
|
||||
unsigned *cells_prev;
|
||||
unsigned *cells;
|
||||
RenRect rect_buf[CELLS_X * CELLS_Y / 2];
|
||||
char command_buf[COMMAND_BUF_SIZE];
|
||||
int command_buf_idx;
|
||||
RenRect screen_rect;
|
||||
bool show_debug;
|
||||
RenSurface *ren_surface;
|
||||
};
|
||||
typedef struct RenCache RenCache;
|
||||
|
||||
void rencache_init(RenCache *rc);
|
||||
void rencache_show_debug(RenCache *rc, bool enable);
|
||||
void rencache_set_clip_rect(RenCache *rc, RenRect rect);
|
||||
void rencache_draw_rect(RenCache *rc, RenRect rect, RenColor color);
|
||||
int rencache_draw_text(RenCache *rc, 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_invalidate(RenCache *rc);
|
||||
void rencache_begin_frame(RenCache *rc, RenSurface *ren, lua_State *L);
|
||||
void rencache_end_frame(RenCache *rc, lua_State *L);
|
||||
|
||||
#endif
|
||||
|
|
125
src/renderer.c
125
src/renderer.c
|
@ -33,6 +33,7 @@ struct RenFont {
|
|||
};
|
||||
|
||||
static RenWindow window_renderer = {0};
|
||||
RenSurface window_ren_surface[1] = {{SurfaceWindow, &window_renderer}};
|
||||
|
||||
static void* check_alloc(void *ptr) {
|
||||
if (!ptr) {
|
||||
|
@ -59,6 +60,26 @@ static const char* utf8_to_codepoint(const char *p, unsigned *dst) {
|
|||
return p + 1;
|
||||
}
|
||||
|
||||
static int ren_surface_scale(RenSurface *ren) {
|
||||
if (ren->type == SurfaceTexture) {
|
||||
return ((RenTexture *) ren->data)->surface_scale;
|
||||
}
|
||||
return renwin_surface_scale((RenWindow *) ren->data);
|
||||
}
|
||||
|
||||
static SDL_Surface *ren_surface_get_surface(RenSurface *ren) {
|
||||
if (ren->type == SurfaceTexture) {
|
||||
return ((RenTexture *) ren->data)->surface;
|
||||
}
|
||||
return renwin_get_surface((RenWindow *) ren->data);
|
||||
}
|
||||
|
||||
static RenRect ren_surface_clip(RenSurface *ren) {
|
||||
if (ren->type == SurfaceTexture) {
|
||||
return ((RenTexture *) ren->data)->clip;
|
||||
}
|
||||
return ((RenWindow *) ren->data)->clip;
|
||||
}
|
||||
|
||||
void ren_cp_replace_init(CPReplaceTable *rep_table) {
|
||||
rep_table->size = 0;
|
||||
|
@ -97,35 +118,66 @@ void ren_free_window_resources() {
|
|||
void ren_init(SDL_Window *win) {
|
||||
assert(win);
|
||||
window_renderer.window = win;
|
||||
window_renderer.initial_frame = true;
|
||||
renwin_init_surface(&window_renderer);
|
||||
renwin_clip_to_surface(&window_renderer);
|
||||
}
|
||||
|
||||
|
||||
void ren_resize_window() {
|
||||
renwin_resize_surface(&window_renderer);
|
||||
}
|
||||
|
||||
/* FIXME: all this stuff. */
|
||||
#define IMAGE_CELL_SIZE 96
|
||||
#define IMAGE_CELL_X 80
|
||||
|
||||
void ren_update_rects(RenRect *rects, int count) {
|
||||
static bool initial_frame = true;
|
||||
if (initial_frame) {
|
||||
renwin_show_window(&window_renderer);
|
||||
initial_frame = false;
|
||||
static inline int image_cell_idx(int x, int y) {
|
||||
return x + y * IMAGE_CELLS_X;
|
||||
}
|
||||
|
||||
void ren_update_rects(RenSurface *ren, RenRect *rects, int count) {
|
||||
if (ren->type == SurfaceTexture) {
|
||||
RenTexture *rentex = (RenTexture *) ren->data;
|
||||
for (int i = 0; i < count; i++) {
|
||||
RenRect irect = image_rect_to_index(rects[i]);
|
||||
/* Increment the revision number for all cells that needs to be updated,
|
||||
i.e. those that overlaps with one of the update rectangles. */
|
||||
for (int x = irect.x; x <= irect.x + irect.width; x++) {
|
||||
for (int y = irect.y; y <= irect.y + irect.height; y++) {
|
||||
int idx = image_cell_idx(x, y);
|
||||
rentex->revisions[idx] += 1:
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
RenWindow *renwin = (RenWindow *) ren->data;
|
||||
if (renwin->initial_frame) {
|
||||
renwin_show_window(renwin);
|
||||
renwin->initial_frame = false;
|
||||
}
|
||||
renwin_update_rects(renwin, rects, count);
|
||||
}
|
||||
renwin_update_rects(&window_renderer, rects, count);
|
||||
}
|
||||
|
||||
|
||||
void ren_set_clip_rect(RenRect rect) {
|
||||
renwin_set_clip_rect(&window_renderer, rect);
|
||||
// FIXME: duplicated from renwindow.c
|
||||
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 ren_set_clip_rect(RenSurface *ren, RenRect rect) {
|
||||
if (ren->type == SurfaceTexture) {
|
||||
RenTexture *rentex = (RenTexture *) ren->data;
|
||||
rentex->clip = scaled_rect(rect, rentex->surface_scale);
|
||||
} else {
|
||||
renwin_set_clip_rect((RenWindow *) ren->data, rect);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ren_get_size(int *x, int *y) {
|
||||
RenWindow *ren = &window_renderer;
|
||||
const int scale = renwin_surface_scale(ren);
|
||||
SDL_Surface *surface = renwin_get_surface(ren);
|
||||
void ren_get_size(RenSurface *ren, int *x, int *y) {
|
||||
const int scale = ren_surface_scale(ren);
|
||||
SDL_Surface *surface = ren_surface_get_surface(ren);
|
||||
*x = surface->w / scale;
|
||||
*y = surface->h / scale;
|
||||
}
|
||||
|
@ -237,11 +289,11 @@ int ren_get_font_tab_size(RenFont *font) {
|
|||
|
||||
/* 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 ren_get_font_width(RenSurface *ren, 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);
|
||||
const int surface_scale = ren_surface_scale(ren);
|
||||
RenFont *font = font_desc_get_font_at_scale(font_desc, surface_scale);
|
||||
while (*p) {
|
||||
p = utf8_to_codepoint(p, &codepoint);
|
||||
|
@ -259,8 +311,8 @@ int ren_get_font_width(FontDesc *font_desc, const char *text, int *subpixel_scal
|
|||
}
|
||||
|
||||
|
||||
int ren_get_font_height(FontDesc *font_desc) {
|
||||
const int surface_scale = renwin_surface_scale(&window_renderer);
|
||||
int ren_get_font_height(RenSurface *ren, FontDesc *font_desc) {
|
||||
const int surface_scale = ren_surface_scale(ren);
|
||||
RenFont *font = font_desc_get_font_at_scale(font_desc, surface_scale);
|
||||
return (font->height + surface_scale / 2) / surface_scale;
|
||||
}
|
||||
|
@ -284,10 +336,10 @@ static inline RenColor blend_pixel(RenColor dst, RenColor src) {
|
|||
d += dr; \
|
||||
}
|
||||
|
||||
void ren_draw_rect(RenRect rect, RenColor color) {
|
||||
void ren_draw_rect(RenSurface *ren, RenRect rect, RenColor color) {
|
||||
if (color.a == 0) { return; }
|
||||
|
||||
const int surface_scale = renwin_surface_scale(&window_renderer);
|
||||
const int surface_scale = ren_surface_scale(ren);
|
||||
|
||||
/* transforms coordinates in pixels. */
|
||||
rect.x *= surface_scale;
|
||||
|
@ -295,7 +347,7 @@ void ren_draw_rect(RenRect rect, RenColor color) {
|
|||
rect.width *= surface_scale;
|
||||
rect.height *= surface_scale;
|
||||
|
||||
const RenRect clip = window_renderer.clip;
|
||||
const RenRect clip = ren_surface_clip(ren);
|
||||
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;
|
||||
|
@ -303,7 +355,7 @@ void ren_draw_rect(RenRect rect, RenColor color) {
|
|||
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);
|
||||
SDL_Surface *surface = ren_surface_get_surface(ren);
|
||||
RenColor *d = (RenColor*) surface->pixels;
|
||||
d += x1 + y1 * surface->w;
|
||||
int dr = surface->w - (x2 - x1);
|
||||
|
@ -333,11 +385,11 @@ static FR_Clip_Area clip_area_from_rect(const RenRect r) {
|
|||
}
|
||||
|
||||
|
||||
static void draw_text_impl(RenFont *font, const char *text, int x_subpixel, int y_pixel, RenColor color,
|
||||
static void draw_text_impl(RenSurface *ren, 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);
|
||||
SDL_Surface *surf = ren_surface_get_surface(ren);
|
||||
FR_Clip_Area clip = clip_area_from_rect(ren_surface_clip(ren));
|
||||
const char *p = text;
|
||||
unsigned codepoint;
|
||||
const FR_Color color_fr = { .r = color.r, .g = color.g, .b = color.b };
|
||||
|
@ -364,21 +416,21 @@ static void draw_text_impl(RenFont *font, const char *text, int x_subpixel, int
|
|||
}
|
||||
|
||||
|
||||
void ren_draw_text_subpixel(FontDesc *font_desc, const char *text, int x_subpixel, int y, RenColor color,
|
||||
void ren_draw_text_subpixel(RenSurface *ren, 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);
|
||||
const int surface_scale = ren_surface_scale(ren);
|
||||
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);
|
||||
draw_text_impl(ren, 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,
|
||||
void ren_draw_text(RenSurface *ren, 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);
|
||||
const int surface_scale = ren_surface_scale(ren);
|
||||
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);
|
||||
draw_text_impl(ren, font, text, subpixel_scale * x, surface_scale * y, color, replacements, replace_color);
|
||||
}
|
||||
|
||||
// Could be declared as static inline
|
||||
|
@ -395,8 +447,17 @@ int ren_font_subpixel_round(int width, int subpixel_scale, int orientation) {
|
|||
}
|
||||
|
||||
|
||||
int ren_get_font_subpixel_scale(FontDesc *font_desc) {
|
||||
const int surface_scale = renwin_surface_scale(&window_renderer);
|
||||
int ren_get_font_subpixel_scale(RenSurface *ren, FontDesc *font_desc) {
|
||||
const int surface_scale = ren_surface_scale(ren);
|
||||
RenFont *font = font_desc_get_font_at_scale(font_desc, surface_scale);
|
||||
return FR_Subpixel_Scale(font->renderer) * surface_scale;
|
||||
}
|
||||
|
||||
void ren_draw_image(RenSurface *ren, uint32_t image_id, int image_x, int image_y, RenRect rect) {
|
||||
SDL_Rect image_rect;
|
||||
SDL_Surface *image_surf = image_get(image_id, &image_rect, image_x, image_y);
|
||||
SDL_Surface *surface = ren_surface_get_surface(ren);
|
||||
SDL_Rect dst_rect = {rect.x, rect.y, 0, 0};
|
||||
SDL_BlitSurface(image_surf,image_rect, surface, &dst_rect);
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,23 @@ enum {
|
|||
typedef struct { uint8_t b, g, r, a; } RenColor;
|
||||
typedef struct { int x, y, width, height; } RenRect;
|
||||
|
||||
// FIXME: ensure this Max is okay or use dynamic allocation.
|
||||
#define TEXTURE_REVS_MAX 256
|
||||
struct RenTexture {
|
||||
SDL_Surface *surface;
|
||||
int surface_scale;
|
||||
RenRect clip; /* Clipping rect in pixel coordinates. */
|
||||
int revisions[TEXTURE_REVS_MAX];
|
||||
};
|
||||
typedef struct RenTexture RenTexture;
|
||||
|
||||
enum { SurfaceTexture, SurfaceWindow };
|
||||
|
||||
typedef struct {
|
||||
int type; /* Type of surface, RenSurfaceTexture or RenSurfaceWindow. */
|
||||
void *data; /* Can be a RenTexture or RenWindow pointer based on type. */
|
||||
} RenSurface;
|
||||
|
||||
struct CPReplace {
|
||||
unsigned codepoint_src;
|
||||
unsigned codepoint_dst;
|
||||
|
@ -34,14 +51,14 @@ struct CPReplaceTable {
|
|||
};
|
||||
typedef struct CPReplaceTable CPReplaceTable;
|
||||
|
||||
|
||||
void ren_init(SDL_Window *win);
|
||||
void ren_resize_window();
|
||||
void ren_update_rects(RenRect *rects, int count);
|
||||
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();
|
||||
|
||||
void ren_update_rects(RenSurface *ren, RenRect *rects, int count);
|
||||
void ren_set_clip_rect(RenSurface *ren, RenRect rect);
|
||||
void ren_get_size(RenSurface *ren, int *x, int *y); /* Reports the size in points. */
|
||||
|
||||
RenImage* ren_new_image(int width, int height);
|
||||
void ren_free_image(RenImage *image);
|
||||
|
||||
|
@ -51,18 +68,21 @@ 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_get_font_width(RenSurface *ren, FontDesc *font_desc, const char *text, int *subpixel_scale);
|
||||
int ren_get_font_height(RenSurface *ren, FontDesc *font_desc);
|
||||
int ren_get_font_subpixel_scale(RenSurface *ren, 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_draw_rect(RenSurface *ren, RenRect rect, RenColor color);
|
||||
void ren_draw_text(RenSurface *ren, FontDesc *font_desc, const char *text, int x, int y, RenColor color, CPReplaceTable *replacements, RenColor replace_color);
|
||||
void ren_draw_text_subpixel(RenSurface *ren, FontDesc *font_desc, const char *text, int x_subpixel, int y, RenColor color, CPReplaceTable *replacements, RenColor replace_color);
|
||||
void ren_draw_image(RenSurface *ren, uint32_t image_id, int image_x, int image_y, RenRect rect);
|
||||
|
||||
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);
|
||||
|
||||
extern RenSurface window_ren_surface[1];
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
#include <SDL.h>
|
||||
#include <stdbool.h>
|
||||
#include "renderer.h"
|
||||
|
||||
struct RenWindow {
|
||||
SDL_Window *window;
|
||||
RenRect clip; /* Clipping rect in pixel coordinates. */
|
||||
bool initial_frame;
|
||||
#ifdef LITE_USE_SDL_RENDERER
|
||||
SDL_Renderer *renderer;
|
||||
SDL_Texture *texture;
|
||||
|
|
Loading…
Reference in New Issue