Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
206 changes: 24 additions & 182 deletions sdl_display/display.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <SDL.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#include <context.h>
Expand Down Expand Up @@ -64,15 +65,6 @@ struct MouseEvent
int y;
};

struct Rectangle
{
int x;
int y;
int width;
int height;
bool valid;
};

static term keyboard_pid;
static struct timespec ts0;
Context *the_ctx;
Expand All @@ -96,135 +88,13 @@ UFontManager *ufont_manager;
static NativeHandlerResult consume_display_mailbox(Context *ctx);
static void *display_loop();

Message *prev_message = NULL;
BaseDisplayItem *prev_items = NULL;
int prev_items_len = 0;

static void destroy_message(Message *m, GlobalContext *global)
{
BEGIN_WITH_STACK_HEAP(1, temp_heap);
mailbox_message_dispose(&m->base, &temp_heap);
END_WITH_STACK_HEAP(temp_heap, global);
}

static inline int int_min(int a, int b)
{
return (a > b) ? b : a;
}

static inline int int_max(int a, int b)
{
return (a > b) ? a : b;
}

static bool cmp_display_item(BaseDisplayItem *a, BaseDisplayItem *b)
{
if (a->primitive != b->primitive || a->x != b->x || a->y != b->y ||
a->width != b->width || a->height != b->height || a->brcolor != b->brcolor) {
return false;
}

switch (a->primitive) {
case PrimitiveImage:
return a->data.image_data.pix == b->data.image_data.pix;

case PrimitiveRect:
return true;

case PrimitiveText:
return (a->data.text_data.fgcolor == b->data.text_data.fgcolor) &&
!strcmp(a->data.text_data.text, b->data.text_data.text);

case PrimitiveScaledCroppedImage:
return (a->data.image_data.pix == b->data.image_data.pix) &&
(a->x_scale == b->x_scale) && (a->y_scale == b->y_scale) &&
(a->source_x == b->source_x) && (a->source_y == b->source_y);

default: {
return true;
}
}
}

static void update_damaged_area(struct Rectangle *area, const struct Rectangle *damage)
{
if (area->valid) {
area->x = int_min(area->x, damage->x);
area->y = int_min(area->y, damage->y);
area->width = int_max(area->x + area->width, damage->x + damage->width) - area->x;
area->height = int_max(area->y + area->height, damage->y + damage->height) - area->y;
} else {
area->x = damage->x;
area->y = damage->y;
area->width = damage->width;
area->height = damage->height;
area->valid = true;
}
}

static void clip_rectangle(struct Rectangle *rectangle, const struct Rectangle *clip_region)
{
rectangle->x = int_max(rectangle->x, clip_region->x);
rectangle->y = int_max(rectangle->y, clip_region->y);
rectangle->width = int_min(rectangle->x + rectangle->width, clip_region->x + clip_region->width) - rectangle->x;
rectangle->height = int_min(rectangle->y + rectangle->height, clip_region->y + clip_region->height) - rectangle->y;
}

static void dumb_diff(BaseDisplayItem *orig, int orig_len, BaseDisplayItem *new, int new_len, struct Rectangle *damaged)
{
if (orig_len == 0) {
for (int i = 0; i < new_len; i++) {
struct Rectangle irect = {
.x = new[i].x,
.y = new[i].y,
.width = new[i].width,
.height = new[i].height,
.valid = true
};
update_damaged_area(damaged, &irect);
}
return;
}

int j = 0;

for (int i = 0; i < new_len; i++) {
if (cmp_display_item(&new[i], &orig[j])) {
j++;
} else {
bool found = false;
for (int k = j + 1; k < orig_len; k++) {
if (cmp_display_item(&new[i], &orig[k])) {
for (int l = k - j; l < k; l++) {
struct Rectangle irect = {
.x = orig[l].x,
.y = orig[l].y,
.width = orig[l].width,
.height = orig[l].height,
.valid = true
};
update_damaged_area(damaged, &irect);
}

j = k + 1;
found = true;
break;
}
}
if (!found) {
struct Rectangle irect = {
.x = new[i].x,
.y = new[i].y,
.width = new[i].width,
.height = new[i].height,
.valid = true
};
update_damaged_area(damaged, &irect);
}
}
}
}

static inline Uint32 uint32_color_to_surface(struct Screen *screen, uint32_t color)
{
return SDL_MapRGB(screen->format, (color >> 24) & 0xFF, (color >> 16) & 0xFF, (color >> 8) & 0xFF);
Expand Down Expand Up @@ -455,55 +325,22 @@ static int draw_x(int xpos, int ypos, BaseDisplayItem items[], size_t items_len)
return 1;
}

static bool do_update(Context *ctx, term display_list)
static void do_update(Context *ctx, term display_list)
{
BaseDisplayItem *items;
size_t len;
if (UNLIKELY(display_items_new_list(display_list, &items, &len, ctx) != DisplayItemsOk)) {
return false;
}

struct Rectangle damaged;
damaged.valid = false;
dumb_diff(prev_items, prev_items_len, items, len, &damaged);
if (prev_message) {
display_items_delete(prev_items, prev_items_len);
destroy_message(prev_message, ctx->global);
}
prev_items = items;
prev_items_len = len;

if (!damaged.valid) {
// skip update
return true;
return;
}

struct Rectangle screen_rect = {
.x = 0,
.y = 0,
.width = screen->w,
.height = screen->h,
.valid = true
};
clip_rectangle(&damaged, &screen_rect);

// BUG: damage area is not correct
// WORKAROUND: always damage the whole screen to force full redraw
damaged.x = 0;
damaged.y = 0;
damaged.height = screen->h;
damaged.width = screen->w;
// END OF WORKAROUND

for (int ypos = damaged.y; ypos < damaged.y + damaged.height; ypos++) {
int xpos = damaged.x;
while (xpos < damaged.x + damaged.width) {
int drawn_pixels = draw_x(xpos, ypos, items, len);
xpos += drawn_pixels;
for (int ypos = 0; ypos < screen->h; ypos++) {
int xpos = 0;
while (xpos < screen->w) {
xpos += draw_x(xpos, ypos, items, len);
}
}

return true;
display_items_delete(items, len);
}

static void process_message(Context *ctx)
Expand Down Expand Up @@ -532,9 +369,7 @@ static void process_message(Context *ctx)
if (cmd == globalcontext_make_atom(ctx->global, "\x6"
"update")) {
term display_list = term_get_tuple_element(req, 1);
if (do_update(ctx, display_list)) {
prev_message = message;
}
do_update(ctx, display_list);

// Copy and scale up
int scale = screen->scale;
Expand All @@ -551,7 +386,7 @@ static void process_message(Context *ctx)
if (term_get_tuple_arity(req) != 2) {
goto invalid_message;
}
term sources = term_get_tuple_element(req, 2);
term sources = term_get_tuple_element(req, 1);
if (term_is_pid(keyboard_pid) || sources != globalcontext_make_atom(ctx->global, "\x3" "all")) {
fprintf(stderr, "Warning: only one subscriber to all sources is supported now\n");
}
Expand All @@ -566,13 +401,22 @@ static void process_message(Context *ctx)

} else if (cmd == globalcontext_make_atom(ctx->global, "\xD" "register_font")) {
term font_bin = term_get_tuple_element(req, 2);
EpdFont *loaded_font = ufont_parse(term_binary_data(font_bin), term_binary_size(font_bin));
size_t font_size = term_binary_size(font_bin);
void *owned_buf = malloc(font_size);
EpdFont *loaded_font = NULL;
if (owned_buf != NULL) {
memcpy(owned_buf, term_binary_data(font_bin), font_size);
loaded_font = ufont_parse(owned_buf, font_size);
if (loaded_font == NULL) {
free(owned_buf);
}
}

char *handle = interop_atom_to_string(ctx, term_get_tuple_element(req, 1));
if (handle != NULL) {
ufont_manager_register(ufont_manager, handle, loaded_font);
free(handle);
if (loaded_font != NULL && handle != NULL) {
ufont_manager_register(ufont_manager, handle, loaded_font, owned_buf);
}
free(handle);

} else {
fprintf(stderr, "unexpected command: ");
Expand Down Expand Up @@ -605,9 +449,7 @@ static void process_message(Context *ctx)
fprintf(stderr, "Expected gen_server call.\n");

free_msg_and_exit:
if (prev_message != message) {
destroy_message(message, ctx->global);
}
destroy_message(message, ctx->global);
return;
}

Expand Down
Loading