~strahinja/lav-sdl

a91fd2986ddd67cf89dc68e314c802e2498760c5 — Страхиња Радић 1 year, 3 months ago b521092
Optimize drawing by buffering statical parts of display

Signed-off-by: Страхиња Радић <contact@strahinja.org>
1 files changed, 161 insertions(+), 96 deletions(-)

M lav-sdl.c
M lav-sdl.c => lav-sdl.c +161 -96
@@ 41,7 41,8 @@ enum {
	CELL_WALL,
	CELL_FAIL,
	CELL_OK,
	CELL_HERO
	CELL_START,
	CELL_END
};

enum {


@@ 106,6 107,7 @@ static int screen_height	  = DEFAULT_HEIGHT;
static SDL_Texture* sprites	  = NULL;
static int state		  = STATE_SEARCHING_PAUSED;
static SDL_Texture* ter16_texture = NULL;
static SDL_Texture* background	  = NULL;
static SDL_Window* window	  = NULL;

void assign_pointIF(const SDL_Point* from, SDL_FPoint* to);


@@ 116,12 118,13 @@ void draw(void);
void draw_help_dialog(void);
void draw_text(const int x, const int y, const int font_width,
	const int font_height, SDL_Texture* font_tex, const char* text, ...);
void get_hero_sprite_xy(int* x, int* y);
void get_sprite_xy(const char cell, int* x, int* y);
void handle_event(SDL_Event* event);
void labyrinth_coord_to_screen_coord(const int x, const int y, float* to_x,
	float* to_y);
void labyrinth_coord_to_screen_coordF(const float x, const float y, float* to_x,
	float* to_y);
void labyrinth_coord_to_screen_coord(const int x, const int y,
	const float startx, const float starty, float* to_x, float* to_y);
void labyrinth_coord_to_screen_coordF(const float x, const float y,
	const float startx, const float starty, float* to_x, float* to_y);
void load_textures(void);
void load_labyrinth(const char* pathname);
int location_pop(SDL_Point* location);


@@ 172,6 175,8 @@ cleanup(void)
			free(labyrinth.cells[y]);
	free(labyrinth.cells);

	if (background)
		SDL_DestroyTexture(background);
	if (ter16_texture)
		SDL_DestroyTexture(ter16_texture);
	if (sprites)


@@ 188,66 193,120 @@ draw(void)
{
	SDL_Rect bg_rect, cell_rect;
	SDL_FRect dest_rect;
	SDL_DisplayMode mode;
	int x, y;
	int cx = screen_width / 2, cy = screen_height / 2;
	int cx, cy;
	float startx, starty;

	SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
	SDL_RenderClear(renderer);
	cx = screen_width / 2;
	cy = screen_height / 2;

	startx = cx - labyrinth.width / 2 * CELL_SIZE * scale_factor;
	starty = cy - labyrinth.height / 2 * CELL_SIZE * scale_factor;

	bg_rect.x = cx - labyrinth.width / 2 * CELL_SIZE * scale_factor;
	bg_rect.y = cy - labyrinth.height / 2 * CELL_SIZE * scale_factor;
	bg_rect.x = 0;
	bg_rect.y = 0;
	bg_rect.w = CELL_SIZE * scale_factor * labyrinth.width;
	bg_rect.h = CELL_SIZE * scale_factor * labyrinth.height;
	SDL_SetRenderDrawColor(renderer, 0x22, 0x22, 0x22, SDL_ALPHA_OPAQUE);
	SDL_RenderFillRect(renderer, &bg_rect);

	SDL_SetRenderDrawColor(renderer, 0, 0, 0, SDL_ALPHA_OPAQUE);
	SDL_RenderClear(renderer);

	cell_rect.w = CELL_SIZE;
	cell_rect.h = CELL_SIZE;

	dest_rect.w = CELL_SIZE * scale_factor;
	dest_rect.h = CELL_SIZE * scale_factor;
	/* Render background */
	if (!background)
	{
		if (SDL_GetCurrentDisplayMode(0, &mode) < 0)
		{
			print_error("SDL_GetCurrentDisplayMode failed: %s",
				SDL_GetError());
			exit(1);
		}

	for (y = 0; y < labyrinth.height; y++)
		for (x = 0; x < labyrinth.width; x++)
		background = SDL_CreateTexture(renderer, mode.format,
			SDL_TEXTUREACCESS_TARGET, bg_rect.w, bg_rect.h);
		if (!background)
		{
			if (x == start.x && y == start.y
				&& (animation.active || hero.x != x
					|| hero.y != y))
			{
				cell_rect.x = 1 + 8 * (CELL_SIZE + 1);
				cell_rect.y = 1;
			}
			else if (x == end.x && y == end.y
				&& (animation.active || hero.x != x
					|| hero.y != y))
			print_error("SDL_CreateTextureFromSurface failed: %s",
				SDL_GetError());
			exit(1);
		}
		if (SDL_SetRenderTarget(renderer, background) < 0)
		{
			print_error("SDL_SetRenderTarget failed: %s",
				SDL_GetError());
			exit(1);
		}

		SDL_SetRenderDrawColor(renderer, 0x22, 0x22, 0x22,
			SDL_ALPHA_OPAQUE);
		SDL_RenderFillRect(renderer, &bg_rect);

		dest_rect.w = CELL_SIZE * scale_factor;
		dest_rect.h = CELL_SIZE * scale_factor;
		for (y = 0; y < labyrinth.height; y++)
			for (x = 0; x < labyrinth.width; x++)
			{
				cell_rect.x = 1 + 7 * (CELL_SIZE + 1);
				cell_rect.y = 1;
			}
			else if (animation.active
				&& labyrinth.cells[y][x] == CELL_HERO)
				get_sprite_xy(CELL_EMPTY, &cell_rect.x,
					&cell_rect.y);
			else
				get_sprite_xy(labyrinth.cells[y][x],
					&cell_rect.x, &cell_rect.y);
			labyrinth_coord_to_screen_coord(x, y, &dest_rect.x,
				&dest_rect.y);
				labyrinth_coord_to_screen_coord(x, y, 0.0f,
					0.0f, &dest_rect.x, &dest_rect.y);
				SDL_RenderCopyF(renderer, sprites, &cell_rect,
					&dest_rect);
			}

		SDL_SetRenderTarget(renderer, NULL);
	}

	/* Draw buffered copy of background */
	dest_rect.x = startx;
	dest_rect.y = starty;
	dest_rect.w = bg_rect.w;
	dest_rect.h = bg_rect.h;
	SDL_RenderCopyF(renderer, background, &bg_rect, &dest_rect);

	/* Draw nuggets/cobwebs */
	dest_rect.w = CELL_SIZE * scale_factor;
	dest_rect.h = CELL_SIZE * scale_factor;
	for (y = 0; y < labyrinth.height; y++)
		for (x = 0; x < labyrinth.width; x++)
		{
			if ((labyrinth.cells[y][x] != CELL_OK
				&& labyrinth.cells[y][x] != CELL_FAIL)
				|| (x == start.x && y == start.y)
				|| (x == end.x && y == end.y))
				continue;

			get_sprite_xy(labyrinth.cells[y][x], &cell_rect.x,
				&cell_rect.y);
			labyrinth_coord_to_screen_coord(x, y, startx, starty,
				&dest_rect.x, &dest_rect.y);
			SDL_RenderCopyF(renderer, sprites, &cell_rect,
				&dest_rect);
		}

	draw_text(bg_rect.x, bg_rect.y + bg_rect.h, TER16_WIDTH, TER16_HEIGHT,
		ter16_texture,
		"Column: %2d,  Row: %2d    F1 = Help    Status: %s", hero.x + 1,
		hero.y + 1, state_descriptions[state]);
	/* Render hero */
	cell_rect.w = CELL_SIZE;
	cell_rect.h = CELL_SIZE;

	dest_rect.w = CELL_SIZE * scale_factor;
	dest_rect.h = CELL_SIZE * scale_factor;

	get_hero_sprite_xy(&cell_rect.x, &cell_rect.y);
	if (animation.active)
	{
		labyrinth_coord_to_screen_coordF(animation.position.x,
			animation.position.y, &dest_rect.x, &dest_rect.y);
		get_sprite_xy((const char)CELL_HERO, &cell_rect.x, &cell_rect.y);
		SDL_RenderCopyF(renderer, sprites, &cell_rect, &dest_rect);
			animation.position.y, startx, starty,
			&dest_rect.x, &dest_rect.y);
	else
		labyrinth_coord_to_screen_coord(hero.x, hero.y, startx,
			starty, &dest_rect.x, &dest_rect.y);

	SDL_RenderCopyF(renderer, sprites, &cell_rect, &dest_rect);

	if (animation.active)
	{
		animation.position.x += animation.delta.x;
		animation.position.y += animation.delta.y;
		animation.step++;


@@ 256,10 315,15 @@ draw(void)
			animation.active = 0;
			hero.x		 = (int)animation.destination.x;
			hero.y		 = (int)animation.destination.y;
			labyrinth.cells[hero.y][hero.x] = CELL_HERO;
		}
	}

	/* Render status */
	draw_text(startx, starty + bg_rect.h, TER16_WIDTH,
		TER16_HEIGHT, ter16_texture,
		"Column: %2d,  Row: %2d    F1 = Help    Status: %s", hero.x + 1,
		hero.y + 1, state_descriptions[state]);

	if (help_shown)
		draw_help_dialog();



@@ 375,10 439,38 @@ draw_text(const int x, const int y, const int font_width, const int font_height,
}

void
get_hero_sprite_xy(int* x, int* y)
{
	switch (state)
	{
	case STATE_SUCCESS:
		*x = 1 + 4 * (CELL_SIZE + 1);
		*y = 1;
		break;
	case STATE_FAILURE:
		*x = 1 + 6 * (CELL_SIZE + 1);
		*y = 1;
		break;
	default:
		*x = 1 + 3 * (CELL_SIZE + 1);
		*y = 1;
		break;
	}
}

void
get_sprite_xy(const char cell, int* x, int* y)
{
	switch (cell)
	{
	case CELL_START:
		*x = 1 + 8 * (CELL_SIZE + 1);
		*y = 1;
		break;
	case CELL_END:
		*x = 1 + 7 * (CELL_SIZE + 1);
		*y = 1;
		break;
	case CELL_WALL:
		*x = 1;
		*y = 1;


@@ 391,23 483,6 @@ get_sprite_xy(const char cell, int* x, int* y)
		*x = 1 + 2 * (CELL_SIZE + 1);
		*y = 1;
		break;
	case CELL_HERO:
		switch (state)
		{
		case STATE_SUCCESS:
			*x = 1 + 4 * (CELL_SIZE + 1);
			*y = 1;
			break;
		case STATE_FAILURE:
			*x = 1 + 6 * (CELL_SIZE + 1);
			*y = 1;
			break;
		default:
			*x = 1 + 3 * (CELL_SIZE + 1);
			*y = 1;
			break;
		}
		break;
	default:
		*x = 1 + 5 * (CELL_SIZE + 1);
		*y = 1;


@@ 423,6 498,9 @@ handle_event(SDL_Event* event)
		return;
	switch (event->type)
	{
	case SDL_QUIT:
		running = 0;
		break;
	case SDL_WINDOWEVENT:
		switch (event->window.event)
		{


@@ 478,27 556,19 @@ handle_event(SDL_Event* event)
}

void
labyrinth_coord_to_screen_coord(const int x, const int y, float* to_x,
	float* to_y)
labyrinth_coord_to_screen_coord(const int x, const int y, const float startx,
	const float starty, float* to_x, float* to_y)
{
	int cx = screen_width / 2, cy = screen_height / 2;

	*to_x = cx - labyrinth.width / 2 * CELL_SIZE * scale_factor
		+ x * CELL_SIZE * scale_factor;
	*to_y = cy - labyrinth.height / 2 * CELL_SIZE * scale_factor
		+ y * CELL_SIZE * scale_factor;
	*to_x = startx + x * CELL_SIZE * scale_factor;
	*to_y = starty + y * CELL_SIZE * scale_factor;
}

void
labyrinth_coord_to_screen_coordF(const float x, const float y, float* to_x,
	float* to_y)
labyrinth_coord_to_screen_coordF(const float x, const float y,
	const float startx, const float starty, float* to_x, float* to_y)
{
	int cx = screen_width / 2, cy = screen_height / 2;

	*to_x = cx - labyrinth.width / 2 * CELL_SIZE * scale_factor
		+ x * CELL_SIZE * scale_factor;
	*to_y = cy - labyrinth.height / 2 * CELL_SIZE * scale_factor
		+ y * CELL_SIZE * scale_factor;
	*to_x = startx + x * CELL_SIZE * scale_factor;
	*to_y = starty + y * CELL_SIZE * scale_factor;
}

void


@@ 620,9 690,10 @@ load_labyrinth(const char* pathname)
				ch, ch);
	}

	hero.x				= start.x;
	hero.y				= start.y;
	labyrinth.cells[hero.y][hero.x] = CELL_HERO;
	hero.x				  = start.x;
	hero.y				  = start.y;
	labyrinth.cells[start.y][start.x] = CELL_START;
	labyrinth.cells[end.y][end.x]	  = CELL_END;

	fclose(input);
}


@@ 669,7 740,6 @@ next_step(void)
		animation.step	     = 1;
		animation.final_step = ANIM_STEPS;
		animation.active     = 1;
		// labyrinth.cells[hero.y][hero.x] = CELL_HERO;
	}
	else if (passable(hero.x, hero.y - 1))
	{


@@ 684,7 754,6 @@ next_step(void)
		animation.step	     = 1;
		animation.final_step = ANIM_STEPS;
		animation.active     = 1;
		// labyrinth.cells[hero.y][hero.x] = CELL_HERO;
	}
	else if (passable(hero.x + 1, hero.y))
	{


@@ 699,7 768,6 @@ next_step(void)
		animation.step	     = 1;
		animation.final_step = ANIM_STEPS;
		animation.active     = 1;
		// labyrinth.cells[hero.y][hero.x] = CELL_HERO;
	}
	else if (passable(hero.x, hero.y + 1))
	{


@@ 714,7 782,6 @@ next_step(void)
		animation.step	     = 1;
		animation.final_step = ANIM_STEPS;
		animation.active     = 1;
		// labyrinth.cells[hero.y][hero.x] = CELL_HERO;
	}
	else
	{


@@ 744,7 811,8 @@ int
passable(const int x, const int y)
{
	return (x >= 0 && x < labyrinth.width && y >= 0 && y < labyrinth.height
		&& labyrinth.cells[y][x] == CELL_EMPTY);
		&& (labyrinth.cells[y][x] == CELL_EMPTY
			|| labyrinth.cells[y][x] == CELL_END));
}

void


@@ 777,15 845,13 @@ reset(void)
	for (int y = 0; y < labyrinth.height; y++)
		for (int x = 0; x < labyrinth.width; x++)
			if (labyrinth.cells[y][x] == CELL_FAIL
				|| labyrinth.cells[y][x] == CELL_OK
				|| labyrinth.cells[y][x] == CELL_HERO)
				|| labyrinth.cells[y][x] == CELL_OK)
				labyrinth.cells[y][x] = CELL_EMPTY;
	hero.x				= start.x;
	hero.y				= start.y;
	labyrinth.cells[hero.y][hero.x] = CELL_HERO;
	state				= STATE_SEARCHING_PAUSED;
	location_pointer		= 0;
	redraw				= 1;
	hero.x		 = start.x;
	hero.y		 = start.y;
	state		 = STATE_SEARCHING_PAUSED;
	location_pointer = 0;
	redraw		 = 1;
}

void


@@ 856,7 922,6 @@ main(int argc, char** argv)
			if (state == STATE_SEARCHING)
			{
				next_step();
				SDL_Delay(SEARCHING_DELAY);
			}

			if (redraw)


@@ 864,8 929,8 @@ main(int argc, char** argv)
				draw();
				redraw = 0;
			}

		}
		/* TODO: More sophisticated framerate/vsync/delay handling */
		SDL_Delay(10);
	}