changeset 94:ed72843a7194

core: simplify font/texture interfaces Expose the structure to avoid calling functions and heap allocations. While here remove some useless functions.
author David Demelier <markand@malikania.fr>
date Mon, 30 Mar 2020 13:34:42 +0200
parents d4a72fa16225
children e82eca4f8606
files src/adventure/main.c src/adventure/mainmenu_state.c src/adventure/panic_state.c src/adventure/splashscreen_state.c src/adventure/splashscreen_state.h src/core/debug.c src/core/font.c src/core/font.h src/core/image.c src/core/image.h src/core/map.c src/core/map.h src/core/map_state.c src/core/message.c src/core/message.h src/core/painter.c src/core/sprite.c src/core/sprite.h src/core/texture.c src/core/texture.h src/core/texture_p.h
diffstat 21 files changed, 300 insertions(+), 328 deletions(-) [+]
line wrap: on
line diff
--- a/src/adventure/main.c	Thu Mar 26 10:28:11 2020 +0100
+++ b/src/adventure/main.c	Mon Mar 30 13:34:42 2020 +0200
@@ -48,6 +48,7 @@
 #define WINDOW_HEIGHT 720
 
 static jmp_buf panic_buf;
+static struct font debug_font;
 
 static noreturn void
 unrecoverable(void)
@@ -76,17 +77,18 @@
 {
 	union event ev;
 	struct clock clock;
-	struct font *font;
-	struct texture *frame;
+	struct font font;
+	struct texture frame;
 	int panic_trigger = 0;
 
-	if (!(font = font_openf(sys_datapath("fonts/DejaVuSans.ttf"), 15)))
+	if (!(font_open(&font, sys_datapath("fonts/DejaVuSans.ttf"), 15)))
 		error_fatal();
-	if (!(debug_options.default_font = font_openf(sys_datapath("fonts/DejaVuSans.ttf"), 10)))
+	if (!(font_open(&debug_font, sys_datapath("fonts/DejaVuSans.ttf"), 10)))
 		error_fatal();
-	if (!(frame = image_openf(sys_datapath("images/message.png"))))
+	if (!(image_open(&frame, sys_datapath("images/message.png"))))
 		error_fatal();
 
+	debug_options.default_font = &debug_font;
 	debug_options.enable = true;
 
 	clock_start(&clock);
--- a/src/adventure/mainmenu_state.c	Thu Mar 26 10:28:11 2020 +0100
+++ b/src/adventure/mainmenu_state.c	Mon Mar 30 13:34:42 2020 +0200
@@ -48,10 +48,11 @@
 static unsigned int selection;
 static int destination;
 static enum substate substate;
+static struct texture image;
 
 /* Menu items. */
 static struct {
-	struct texture *texture;
+	struct texture texture;
 	int x;
 	int y;
 } items[3];
@@ -59,9 +60,9 @@
 static void
 enter(void)
 {
-	struct font *font = font_openf(sys_datapath("fonts/pirata-one.ttf"), 30);
+	struct font font = { 0 };
 
-	if (!font)
+	if (!font_open(&font, sys_datapath("fonts/pirata-one.ttf"), 30))
 		error_fatal();
 
 	substate = SUBSTATE_MOVING;
@@ -70,36 +71,33 @@
 	destination = window_height() / 4;
 
 	/* TODO: change continue color if no game exists. */
-	items[0].texture = font_render(font, "New game", 0x000000ff);
-	items[0].x = (window_width() / 2) - (texture_width(items[0].texture) / 2);
+	font_render(&font, &items[0].texture, "New game");
+	items[0].x = (window_width() / 2) - (items[0].texture.w / 2);
 	items[0].y = window_height() * 0.75;
 
-	items[1].texture = font_render(font, "Continue", 0x000000ff);
+	font_render(&font, &items[1].texture, "Continue");
 	items[1].x = items[0].x;
-	items[1].y = items[0].y + texture_height(items[0].texture);
+	items[1].y = items[0].y + items[0].texture.h;
 
-	items[2].texture = font_render(font, "Quit", 0x000000ff);
+	font_render(&font, &items[2].texture, "Quit");
 	items[2].x = items[0].x;
-	items[2].y = items[1].y + texture_height(items[1].texture);
+	items[2].y = items[1].y + items[1].texture.h;
+
+	font_finish(&font);
 }
 
 static void
 new(void)
 {
-	struct map map;
-	struct texture *image;
-
 	/* Prepare map. */
-	if (!map_open(&map, sys_datapath("maps/test.map")))
+	if (!map_open(&map_state_data.map.map, sys_datapath("maps/test.map")))
 		error_fatal();
 
-	memcpy(&map_state_data.map.map, &map, sizeof (map));
-
 	/* Prepare image and sprite. */
-	if (!(image = image_openf(sys_datapath("sprites/test-walk.png"))))
+	if (!(image_open(&image, sys_datapath("sprites/test-walk.png"))))
 		error_fatal();
 
-	sprite_init(&map_state_data.player.sprite, image, 48, 48);
+	sprite_init(&map_state_data.player.sprite, &image, 48, 48);
 	game_switch(&map_state, false);
 }
 
@@ -177,12 +175,12 @@
 {
 	painter_set_color(0xffffffff);
 	painter_clear();
-	texture_draw(splashscreen_state_data.text, x, y);
+	texture_draw(&splashscreen_state_data.text, x, y);
 
 	if (substate == SUBSTATE_WAITING) {
-		texture_draw(items[0].texture, items[0].x, items[0].y);
-		texture_draw(items[1].texture, items[1].x, items[1].y);
-		texture_draw(items[2].texture, items[2].x, items[2].y);
+		texture_draw(&items[0].texture, items[0].x, items[0].y);
+		texture_draw(&items[1].texture, items[1].x, items[1].y);
+		texture_draw(&items[2].texture, items[2].x, items[2].y);
 
 		/* Selection cursor. */
 
@@ -196,8 +194,8 @@
 static void
 leave(void)
 {
-	texture_close(items[0].texture);
-	texture_close(items[1].texture);
+	texture_finish(&items[0].texture);
+	texture_finish(&items[1].texture);
 	memset(items, 0, sizeof (items));
 }
 
--- a/src/adventure/panic_state.c	Thu Mar 26 10:28:11 2020 +0100
+++ b/src/adventure/panic_state.c	Mon Mar 30 13:34:42 2020 +0200
@@ -45,21 +45,21 @@
 
 struct label {
 	const char *text;
-	struct texture *texture;
+	struct texture texture;
 };
 
 static struct {
-	struct font *font;
+	struct font font;
 } data;
 
 static struct label headers[] = {
-	{ "An unrecoverable error occured and the game cannot continue.", NULL },
-	{ "Please report the detailed error as provided below.", NULL },
+	{ "An unrecoverable error occured and the game cannot continue.", 0 },
+	{ "Please report the detailed error as provided below.", 0 },
 };
 
 static struct label bottom[] = {
-	{ "Press <s> to save information and generate a core dump.", NULL },
-	{ "Press <q> to quit without saving information.", NULL }
+	{ "Press <s> to save information and generate a core dump.", 0 },
+	{ "Press <q> to quit without saving information.", 0 }
 };
 
 static struct label lerror;
@@ -136,12 +136,13 @@
 	assert(labels);
 
 	for (size_t i = 0; i < labelsz; ++i) {
-		if (labels[i].texture)
+		if (texture_ok(&labels[i].texture))
 			continue;
 
-		labels[i].texture = font_render(data.font, labels[i].text, FOREGROUND);
+		data.font.color = FOREGROUND;
+		font_render(&data.font, &labels[i].texture, labels[i].text);
 
-		if (!labels[i].texture)
+		if (!texture_ok(&labels[i].texture))
 			error_fatal();
 	}
 }
@@ -169,20 +170,20 @@
 
 	/* Header. */
 	for (size_t i = 0; i < NELEM(headers); ++i) {
-		texture_draw(headers[i].texture, PADDING, y);
-		y += texture_height(headers[i].texture) + 2;
+		texture_draw(&headers[i].texture, PADDING, y);
+		y += headers[i].texture.h + 2;
 	}
 
 	/* Error message. */
-	texture_draw(lerror.texture, PADDING, y + PADDING);
+	texture_draw(&lerror.texture, PADDING, y + PADDING);
 
 	/* Bottom. */
 	y = window_height() - PADDING;
-	y -= texture_height(bottom[0].texture);
+	y -= bottom[0].texture.h;
 
 	for (size_t i = 0; i < NELEM(bottom); ++i) {
-		texture_draw(bottom[i].texture, PADDING, y);
-		y -= texture_height(bottom[i].texture) + 2;
+		texture_draw(&bottom[i].texture, PADDING, y);
+		y -= bottom[i].texture.h + 2;
 	}
 }
 
@@ -207,6 +208,6 @@
 	 * useful information to the screen so as last resort print them
 	 * on the console.
 	 */
-	if (!(data.font = font_openf(sys_datapath(FONT), FONT_SZ)))
+	if (!(font_open(&data.font, sys_datapath(FONT), FONT_SZ)))
 		error_fatal();
 }
--- a/src/adventure/splashscreen_state.c	Thu Mar 26 10:28:11 2020 +0100
+++ b/src/adventure/splashscreen_state.c	Mon Mar 30 13:34:42 2020 +0200
@@ -38,21 +38,23 @@
 static void
 enter(void)
 {
-	struct font *font;
+	struct font font = {
+		.color = 0x000000ff
+	};
 
-	if (!(font = font_openf(sys_datapath("fonts/teutonic1.ttf"), 130)))
+	if (!(font_open(&font, sys_datapath("fonts/teutonic1.ttf"), 130)))
 		error_fatal();
-	if (!(splashscreen_state_data.text = font_render(font, "Molko's Adventure", 0x000000ff)))
+	if (!(font_render(&font, &splashscreen_state_data.text, "Molko's Adventure")))
 		error_fatal();
 
 	/* Compute position. */
-	const unsigned int w = texture_width(splashscreen_state_data.text);
-	const unsigned int h = texture_height(splashscreen_state_data.text);
+	const unsigned int w = splashscreen_state_data.text.w;
+	const unsigned int h = splashscreen_state_data.text.h;
 
 	splashscreen_state_data.x = (window_width() / 2) - (w / 2);
 	splashscreen_state_data.y = (window_height() / 2) - (h / 2);
 
-	font_close(font);
+	font_finish(&font);
 }
 
 static void
@@ -81,7 +83,7 @@
 {
 	painter_set_color(0xffffffff);
 	painter_clear();
-	texture_draw(splashscreen_state_data.text,
+	texture_draw(&splashscreen_state_data.text,
 		splashscreen_state_data.x,
 		splashscreen_state_data.y);
 }
--- a/src/adventure/splashscreen_state.h	Thu Mar 26 10:28:11 2020 +0100
+++ b/src/adventure/splashscreen_state.h	Mon Mar 30 13:34:42 2020 +0200
@@ -24,13 +24,13 @@
  * \brief Splash screen state.
  */
 
-struct texture;
+#include "texture.h"
 
 /**
  * \brief Data for splashscreen.
  */
 extern struct splashscreen_state_data {
-	struct texture *text;           /*!< (RW) Texture for the text. */
+	struct texture text;            /*!< (RW) Texture for the text. */
 	int x;                          /*!< (RW) Position in x. */
 	int y;                          /*!< (RW) Position in y. */
 	unsigned int elapsed;           /*!< (RW) Time elapsed. */
--- a/src/core/debug.c	Thu Mar 26 10:28:11 2020 +0100
+++ b/src/core/debug.c	Mon Mar 30 13:34:42 2020 +0200
@@ -51,21 +51,23 @@
 	assert(fmt);
 
 	char line[DEBUG_LINE_MAX];
-	struct texture *tex;
+	struct texture tex;
 	struct font *font;
 	unsigned int gapy;
 
 	vsnprintf(line, sizeof (line), fmt, ap);
+
 	font = report->font ? report->font : debug_options.default_font;
-	tex = font_render_ex(font, line, report->color, report->style);
+	font->color = report->color;
+	font->style = report->style;
 
-	if (!tex)
+	if (!font_render(font, &tex, line))
 		return;
 
 	gapy = (PADDING_Y * (report->count)) +
 	    (font_height(font) * (report->count));
 	report->count++;
 
-	texture_draw(tex, PADDING_X, gapy);
-	texture_close(tex);
+	texture_draw(&tex, PADDING_X, gapy);
+	texture_finish(&tex);
 }
--- a/src/core/font.c	Thu Mar 26 10:28:11 2020 +0100
+++ b/src/core/font.c	Mon Mar 30 13:34:42 2020 +0200
@@ -29,73 +29,57 @@
 #include "texture_p.h"
 #include "util.h"
 
-struct font {
-	TTF_Font *handle;
-};
-
-struct font *
-font_openf(const char *path, unsigned int size)
+bool
+font_open(struct font *font, const char *path, unsigned int size)
 {
+	assert(font);
 	assert(path);
 
-	struct font *f;
-
-	f = ecalloc(1, sizeof (struct font));
+	if (!(font->handle = TTF_OpenFont(path, size)))
+		return error_sdl();
 
-	if (!(f->handle = TTF_OpenFont(path, size))) {
-		error_sdl();
-		free(f);
-		return NULL;
-	}
-
-	return f;
+	return true;
 }
 
-struct font *
-font_openb(const void *buffer, size_t buflen, unsigned int size)
+bool
+font_openmem(struct font *font, const void *buffer, size_t buflen, unsigned int size)
 {
+	assert(font);
 	assert(buffer);
 
-	struct font *f;
 	SDL_RWops *ops;
 
-	f = ecalloc(1, sizeof (struct font));
-
 	if (!(ops = SDL_RWFromConstMem(buffer, buflen)) ||
-	   (!(f->handle = TTF_OpenFontRW(ops, true, size)))) {
-		error_sdl();
-		free(f);
-		return NULL;
-	}
+	   (!(font->handle = TTF_OpenFontRW(ops, true, size))))
+		return error_sdl();
 
-	return f;
+	return true;
 }
 
-struct texture *
-font_render(struct font *font, const char *text, unsigned long color)
+bool
+font_ok(const struct font *font)
 {
-	return font_render_ex(font, text, color, FONT_STYLE_ANTIALIASED);
+	assert(font);
+
+	return font->handle;
 }
 
-struct texture *
-font_render_ex(struct font *font,
-               const char *text,
-               unsigned long color,
-               enum font_style style)
+bool
+font_render(struct font *font, struct texture *tex, const char *text)
 {
 	assert(font);
 	assert(text);
 
 	SDL_Color fg = {
-		.r = COLOR_R(color),
-		.g = COLOR_G(color),
-		.b = COLOR_B(color),
-		.a = COLOR_A(color)
+		.r = COLOR_R(font->color),
+		.g = COLOR_G(font->color),
+		.b = COLOR_B(font->color),
+		.a = COLOR_A(font->color)
 	};
 	SDL_Surface *surface;
 	SDL_Surface *(*func)(TTF_Font *, const char *, SDL_Color);
 
-	switch (style) {
+	switch (font->style) {
 	case FONT_STYLE_ANTIALIASED:
 		func = TTF_RenderUTF8_Blended;
 		break;
@@ -104,25 +88,25 @@
 		break;
 	}
 
-	if (!(surface = func(font->handle, text, fg))) {
-		error_sdl();
-		return NULL;
-	}
+	if (!(surface = func(font->handle, text, fg)))
+		return error_sdl();
 
-	return texture_from_surface(surface);
+	return texture_from_surface(tex, surface);
 }
 
 unsigned int
 font_height(const struct font *font)
 {
+	assert(font);
+
 	return TTF_FontHeight(font->handle);
 }
 
 void
-font_close(struct font *font)
+font_finish(struct font *font)
 {
 	assert(font);
 
-	TTF_CloseFont(font->handle);
-	free(font);
+	if (font->handle)
+		TTF_CloseFont(font->handle);
 }
--- a/src/core/font.h	Thu Mar 26 10:28:11 2020 +0100
+++ b/src/core/font.h	Mon Mar 30 13:34:42 2020 +0200
@@ -25,70 +25,81 @@
  * \ingroup drawing
  */
 
+#include <stdbool.h>
 #include <stddef.h>
 
-/**
- * \brief Font object.
- *
- * This object is not publicly defined because it contains
- * implementation-defined data.
- */
-struct font;
 struct texture;
 
 /**
  * \brief Font style for rendering.
  */
 enum font_style {
-	FONT_STYLE_NONE,                /*!< No antialiasing. */
-	FONT_STYLE_ANTIALIASED          /*!< Pretty antialiasing looking. */
+	FONT_STYLE_ANTIALIASED,         /*!< Pretty antialiasing looking. */
+	FONT_STYLE_NONE                 /*!< No antialiasing. */
+};
+
+/**
+ * \brief Font object.
+ */
+struct font {
+	enum font_style style;          /*!< (RW) Style for rendering. */
+	unsigned long color;            /*!< (RW) Color for rendering. */
+	void *handle;                   /*!< (RO) Native handle. */
 };
 
 /**
  * Open font from path file.
  *
+ * \pre font != NULL
  * \pre path != NULL
+ * \param font the font to initialize
  * \param path the path to the font
  * \param size the desired size
- * \return the font or NULL on error
+ * \return False on errors.
  */
-struct font *
-font_openf(const char *path, unsigned int size);
+bool
+font_open(struct font *font, const char *path, unsigned int size);
 
 /**
  * Open font from memory buffer.
  *
+ * \pre font != NULL
  * \pre buffer != NULL
+ * \param font the font to initialize
  * \param buffer the memory buffer
  * \param buflen the memory buffer length
  * \param size the desired size
  * \warning The buffer must remain valid until font is closed
- * \return the font or NULL on error
+ * \return False on errors.
  */
-struct font *
-font_openb(const void *buffer, size_t buflen, unsigned int size);
-
-/**
- * Similar to \ref font_render_ex with predefined convenient values.
- */
-struct texture *
-font_render(struct font *font, const char *text, unsigned long color);
+bool
+font_openmem(struct font *font, const void *buffer, size_t buflen, unsigned int size);
 
 /**
- * Render a text.
+ * Tells if the font was properly opened.
  *
  * \pre font != NULL
- * \pre text != NULL
- * \param font the font handle
- * \param text the text in UTF-8
- * \param color the color
- * \param style the font style
+ * \param font the font to check
+ * \return True if the native handle was opened.
  */
-struct texture *
-font_render_ex(struct font *font,
-               const char *text,
-               unsigned long color,
-               enum font_style style);
+bool
+font_ok(const struct font *font);
+
+/**
+ * Render some text into the texture.
+ *
+ * This function use the current color/style and other properties in the font
+ * to render the texture.
+ *
+ * \pre font != NULL
+ * \pre tex != NULL
+ * \param font the font to use
+ * \param tex the texture to generate
+ * \param text the UTF-8 text
+ * \return False on errors.
+ */
+bool
+font_render(struct font *font, struct texture *tex, const char *text);
 
 /**
  * Get the maximum height for all glyphs.
@@ -107,6 +118,6 @@
  * \param font the font handle
  */
 void
-font_close(struct font *font);
+font_finish(struct font *font);
 
 #endif /* !MOLKO_FONT_H */
--- a/src/core/image.c	Thu Mar 26 10:28:11 2020 +0100
+++ b/src/core/image.c	Mon Mar 30 13:34:42 2020 +0200
@@ -24,33 +24,30 @@
 #include "error_p.h"
 #include "texture_p.h"
 
-struct texture *
-image_openf(const char *path)
+bool
+image_open(struct texture *tex, const char *path)
 {
+	assert(tex);
 	assert(path);
 
 	SDL_Surface *surface = IMG_Load(path);
 
-	if (!surface) {
-		error_sdl();
-		return NULL;
-	}
+	if (!surface)
+		return error_sdl();
 
-	return texture_from_surface(surface);
+	return texture_from_surface(tex, surface);
 }
 
-struct texture *
-image_openb(const void *buffer, size_t size)
+bool
+image_openmem(struct texture *tex, const void *buffer, size_t size)
 {
 	assert(buffer);
 
 	SDL_RWops *ops = SDL_RWFromConstMem(buffer, size);
 	SDL_Surface *surface;
 
-	if (!ops || !(surface = IMG_Load_RW(ops, true))) {
-		error_sdl();
-		return NULL;
-	}
+	if (!ops || !(surface = IMG_Load_RW(ops, true)))
+		return error_sdl();
 
-	return texture_from_surface(surface);
+	return texture_from_surface(tex, surface);
 }
--- a/src/core/image.h	Thu Mar 26 10:28:11 2020 +0100
+++ b/src/core/image.h	Mon Mar 30 13:34:42 2020 +0200
@@ -32,22 +32,28 @@
 /**
  * Open a file from a path.
  *
+ * \pre tex != NULL
  * \pre path != NULL
+ * \param tex the texture to initialize
  * \param path the path to the file
- * \return the texture or NULL on error
+ * \return False on errors.
  */
-struct texture *
-image_openf(const char *path);
+bool
+image_open(struct texture *tex, const char *path);
 
 /**
  * Open a file from a memory buffer.
  *
+ * The buffer must be valid until the image is no longer used.
+ *
+ * \pre tex != NULL
  * \pre buffer != NULL
+ * \param tex the texture to initialize
  * \param buffer the memory buffer
  * \param size the memory size
- * \return the texture or NULL on error
+ * \return False on errors.
  */
-struct texture *
-image_openb(const void *buffer, size_t size);
+bool
+image_openmem(struct texture *tex, const void *buffer, size_t size);
 
 #endif /* !MOLKO_IMAGE_H */
--- a/src/core/map.c	Thu Mar 26 10:28:11 2020 +0100
+++ b/src/core/map.c	Mon Mar 30 13:34:42 2020 +0200
@@ -77,10 +77,8 @@
 
 	if (map->tilewidth == 0 || map->tileheight == 0)
 		return;
-	if (!(map->tileset = image_openf(sys_datapath("tilesets/%s", filename))))
+	if (!(image_open(&map->tileset, sys_datapath("tilesets/%s", filename))))
 		return;
-
-	sprite_init(&map->sprite, map->tileset, map->tilewidth, map->tileheight);
 }
 
 static void
@@ -109,7 +107,7 @@
 {
 	if (strlen(map->title) == 0)
 		return error_printf("map has no title");
-	if (!map->tileset)
+	if (!map->tileset.w || !map->tileset.h)
 		return error_printf("unable to open tileset");
 	if (map->width == 0 || map->height == 0)
 		return error_printf("map has null sizes");
@@ -127,16 +125,19 @@
 	assert(map);
 	assert(layer);
 
+	struct sprite sprite;
 	int x = 0, y = 0;
 
+	sprite_init(&sprite, &map->tileset, map->tilewidth, map->tileheight);
+
 	for (unsigned int r = 0; r < map->width; ++r) {
 		for (unsigned int c = 0; c < map->height; ++c) {
 			unsigned int si = r * map->width + c;
-			unsigned int sr = (layer->tiles[si] - 1) / map->sprite.ncols;
-			unsigned int sc = (layer->tiles[si] - 1) % map->sprite.nrows;
+			unsigned int sr = (layer->tiles[si] - 1) / sprite.ncols;
+			unsigned int sc = (layer->tiles[si] - 1) % sprite.nrows;
 
 			if (layer->tiles[si] != 0)
-				sprite_draw(&map->sprite, sr, sc, x, y);
+				sprite_draw(&sprite, sr, sc, x, y);
 
 			x += map->tilewidth;
 		}
@@ -169,14 +170,14 @@
 	fclose(fp);
 
 	if (!check(map)) {
-		map_close(map);
+		map_finish(map);
 		return false;
 	}
 
 	size_t pw = map->width * map->tilewidth;
 	size_t ph = map->height * map->tileheight;
 
-	if (!(map->picture = texture_new(pw, ph)))
+	if (!(texture_new(&map->picture, pw, ph)))
 		return error_sdl();
 
 	return true;
@@ -185,8 +186,8 @@
 void
 map_draw(struct map *map, int srcx, int srcy)
 {
-	texture_draw_ex(
-		map->picture,
+	texture_scale(
+		&map->picture,
 		srcx,
 		srcy,
 		window_width(),
@@ -202,21 +203,19 @@
 void
 map_repaint(struct map *map)
 {
-	PAINTER_BEGIN(map->picture);
+	PAINTER_BEGIN(&map->picture);
 	draw_layer(map, &map->layers[0]);
 	draw_layer(map, &map->layers[1]);
 	PAINTER_END();
 }
 
 void
-map_close(struct map *map)
+map_finish(struct map *map)
 {
 	assert(map);
 
-	if (map->tileset)
-		texture_close(map->tileset);
-	if (map->picture)
-		texture_close(map->picture);
+	texture_finish(&map->tileset);
+	texture_finish(&map->picture);
 
 	free(map->layers[0].tiles);
 	free(map->layers[1].tiles);
--- a/src/core/map.h	Thu Mar 26 10:28:11 2020 +0100
+++ b/src/core/map.h	Mon Mar 30 13:34:42 2020 +0200
@@ -26,15 +26,13 @@
 
 #include <stdbool.h>
 
-#include "sprite.h"
+#include "texture.h"
 
 /**
  * \brief Max title length for a map.
  */
 #define MAP_TITLE_MAX   32
 
-struct texture;
-
 /**
  * \brief Map layer.
  */
@@ -50,9 +48,8 @@
  */
 struct map {
 	char title[MAP_TITLE_MAX];      /*!< (RW) The map title */
-	struct texture *tileset;        /*!< (RW) Tileset to use */
-	struct texture *picture;        /*!< (RO) Map drawn into a picture */
-	struct sprite sprite;           /*!< (RO) Sprite to render */
+	struct texture tileset;         /*!< (RW) Tileset to use */
+	struct texture picture;         /*!< (RO) Map drawn into a picture */
 	int origin_x;                   /*!< (RO) Where the player starts in X */
 	int origin_y;                   /*!< (RO) Where the player starts in Y */
 	unsigned int width;             /*!< (RO) Map width in cells */
@@ -102,6 +99,6 @@
  * \param map the map to render
  */
 void
-map_close(struct map *map);
+map_finish(struct map *map);
 
 #endif /* !MOLKO_MAP_H */
--- a/src/core/map_state.c	Thu Mar 26 10:28:11 2020 +0100
+++ b/src/core/map_state.c	Mon Mar 30 13:34:42 2020 +0200
@@ -122,12 +122,9 @@
 	/* Adjust map properties. */
 	struct map *m = &map_state_data.map.map;
 
-	if (!m->picture)
-		map_repaint(m);
-
 	map_repaint(m);
-	map_state_data.map.w = texture_width(m->picture);
-	map_state_data.map.h = texture_height(m->picture);
+	map_state_data.map.w = m->picture.w;
+	map_state_data.map.h = m->picture.h;
 
 	/* Adjust view. */
 	map_state_data.view.w = window_width();
--- a/src/core/message.c	Thu Mar 26 10:28:11 2020 +0100
+++ b/src/core/message.c	Mon Mar 30 13:34:42 2020 +0200
@@ -74,9 +74,9 @@
 	unsigned int total = 0;
 
 	for (int i = 0; i < 6; ++i) {
-		if (msg->textures[i]) {
+		if (msg->textures[i].w) {
 			n += 1;
-			total += texture_height(msg->textures[i]);
+			total += msg->textures[i].h;
 		}
 	}
 
@@ -86,12 +86,8 @@
 static void
 clear(struct message *msg)
 {
-	for (unsigned int i = 0; i < 12; ++i) {
-		if (msg->textures[i]) {
-			texture_close(msg->textures[i]);
-			msg->textures[i] = NULL;
-		}
-	}
+	for (unsigned int i = 0; i < 12; ++i)
+		texture_finish(&msg->textures[i]);
 }
 
 static void
@@ -105,23 +101,26 @@
 			continue;
 
 		/* Normal lines of text. */
-		unsigned long color = msg->colors[0];
+		msg->font->color = msg->colors[0];
 
 		if (msg->flags & MESSAGE_QUESTION && msg->index == i)
-			color = msg->colors[1];
+			msg->font->color = msg->colors[1];
 
-		if (!msg->textures[i])
-			msg->textures[i] = font_render(
+		if (!texture_ok(&msg->textures[i]))
+			font_render(
 			    msg->font,
-			    msg->text[i],
-			    color
+			    &msg->textures[i],
+			    msg->text[i]
 			);
-		if (!msg->textures[i + 6])
-			msg->textures[i + 6] = font_render(
+		if (!texture_ok(&msg->textures[i + 6])) {
+			msg->font->color = 0x000000ff;
+
+			font_render(
 			    msg->font,
-			    msg->text[i],
-			    0x000000ff
+			    &msg->textures[i + 6],
+			    msg->text[i]
 			);
+		}
 	}
 }
 
@@ -132,7 +131,7 @@
 
 	msg->elapsed = 0;
 	msg->state = msg->flags & MESSAGE_QUICK ? MESSAGE_SHOWING : MESSAGE_OPENING;
-	msg->height[0] = texture_height(msg->frame);
+	msg->height[0] = msg->frame->h;
 	msg->height[1] = 0;
 
 	redraw(msg);
@@ -176,7 +175,7 @@
 
 	switch (msg->state) {
 	case MESSAGE_OPENING:
-		msg->height[1] += texture_height(msg->frame) * ticks / MESSAGE_SPEED;
+		msg->height[1] += msg->frame->h * ticks / MESSAGE_SPEED;
 
 		if (msg->height[1] > msg->height[0])
 			msg->height[1] = msg->height[0];
@@ -195,7 +194,7 @@
 
 		break;
 	case MESSAGE_HIDING:
-		msg->height[1] -= texture_height(msg->frame) * ticks / MESSAGE_SPEED;
+		msg->height[1] -= msg->frame->h * ticks / MESSAGE_SPEED;
 
 		if (msg->elapsed >= MESSAGE_SPEED) {
 			msg->state = MESSAGE_NONE;
@@ -216,8 +215,8 @@
 	assert(msg);
 	assert(msg->frame);
 
-	const unsigned int w = texture_width(msg->frame);
-	const unsigned int h = texture_height(msg->frame);
+	const unsigned int w = msg->frame->w;
+	const unsigned int h = msg->frame->h;
 	const unsigned int x = (window_width() / 2) - (w / 2);
 	const unsigned int y = 80;
 	const unsigned int avgh = average(msg);
@@ -226,7 +225,7 @@
 	switch (msg->state) {
 	case MESSAGE_OPENING:
 	case MESSAGE_HIDING:
-		texture_draw_ex(msg->frame, 0, 0, w, msg->height[1], x, y, w, msg->height[1], 0);
+		texture_scale(msg->frame, 0, 0, w, msg->height[1], x, y, w, msg->height[1], 0);
 		break;
 	case MESSAGE_SHOWING:
 		texture_draw(msg->frame, x, y);
@@ -236,11 +235,11 @@
 			const int real_x = x + 20;
 			const int real_y = y + ((i + 1) * gapy) + (i * avgh);
 
-			if (!msg->textures[i])
+			if (!texture_ok(&msg->textures[i]))
 				continue;
 
-			texture_draw(msg->textures[i + 6], real_x + 2, real_y + 2);
-			texture_draw(msg->textures[i], real_x, real_y);
+			texture_draw(&msg->textures[i + 6], real_x + 2, real_y + 2);
+			texture_draw(&msg->textures[i], real_x, real_y);
 		}
 		break;
 	default:
--- a/src/core/message.h	Thu Mar 26 10:28:11 2020 +0100
+++ b/src/core/message.h	Mon Mar 30 13:34:42 2020 +0200
@@ -65,9 +65,10 @@
 
 #include <stdbool.h>
 
+#include "texture.h"
+
 struct action;
 struct font;
-struct texture;
 
 union event;
 
@@ -98,9 +99,9 @@
  */
 struct message {
 	const char *text[6];            /*!< (RW) Lines of text to show */
-	struct texture *frame;          /*!< (RW) Frame to use */
-	struct texture *avatar;         /*!< (RW) Avatar face (optional) */
-	struct font *font;              /*!< (RW) Font to use */
+	struct texture *frame;          /*!< (RW, ref) Frame to use */
+	struct texture *avatar;         /*!< (RW, ref, optional) Avatar face (optional) */
+	struct font *font;              /*!< (RW, ref) Font to use */
 	unsigned long colors[2];        /*!< (RW) Normal/selected colors */
 	unsigned int index;             /*!< (RW) Line selected */
 	enum message_flags flags;       /*!< (RW) Message flags */
@@ -108,7 +109,7 @@
 
 	/*! \cond PRIVATE */
 
-	struct texture *textures[12];
+	struct texture textures[12];
 	unsigned int elapsed;
 	int height[2];
 
--- a/src/core/painter.c	Thu Mar 26 10:28:11 2020 +0100
+++ b/src/core/painter.c	Mon Mar 30 13:34:42 2020 +0200
@@ -18,7 +18,7 @@
 
 #include "color.h"
 #include "painter.h"
-#include "texture_p.h"
+#include "texture.h"
 #include "window_p.h"
 
 static struct texture *renderer;
--- a/src/core/sprite.c	Thu Mar 26 10:28:11 2020 +0100
+++ b/src/core/sprite.c	Mon Mar 30 13:34:42 2020 +0200
@@ -19,7 +19,6 @@
 #include <assert.h>
 
 #include "sprite.h"
-#include "texture_p.h"
 #include "texture.h"
 
 void
@@ -28,20 +27,14 @@
             unsigned int cellw,
             unsigned int cellh)
 {
-	int w = 0;
-	int h = 0;
-
 	assert(sprite);
-	assert(tex);
-
-	/* TODO: use texture_get_size */
-	SDL_QueryTexture(tex->handle, NULL, NULL, &w, &h);
+	assert(tex && texture_ok(tex));
 
 	sprite->texture = tex;
 	sprite->cellw = cellw;
 	sprite->cellh = cellh;
-	sprite->nrows = h / cellh;
-	sprite->ncols = w / cellw;
+	sprite->nrows = tex->h / cellh;
+	sprite->ncols = tex->w / cellw;
 }
 
 void
@@ -49,7 +42,7 @@
 {
 	assert(sprite);
 
-	texture_draw_ex(
+	texture_scale(
 		sprite->texture,
 		c * sprite->cellw,      /* src y */
 		r * sprite->cellh,      /* src x */
--- a/src/core/sprite.h	Thu Mar 26 10:28:11 2020 +0100
+++ b/src/core/sprite.h	Mon Mar 30 13:34:42 2020 +0200
@@ -51,7 +51,7 @@
  * texture size.
  *
  * \pre sprite != NULL
- * \pre tex != NULL
+ * \pre tex != NULL && texture_ok(tex)
  * \param sprite the sprite to initialize
  * \param tex the texture
  * \param cellw the width per cell in pixels
--- a/src/core/texture.c	Thu Mar 26 10:28:11 2020 +0100
+++ b/src/core/texture.c	Mon Mar 30 13:34:42 2020 +0200
@@ -25,75 +25,59 @@
 #include "util.h"
 #include "window_p.h"
 
-struct texture *
-texture_new(unsigned int w, unsigned int h)
-{
-	struct texture *tex = emalloc(sizeof (struct texture));
-
-	if (!(tex->handle = SDL_CreateTexture(win.renderer,
-	    SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, w, h))) {
-		error_sdl();
-		free(tex);
-		return NULL;
-	}
-
-	return tex;
-}
-
-unsigned int
-texture_width(struct texture *tex)
+bool
+texture_new(struct texture *tex, unsigned int w, unsigned int h)
 {
 	assert(tex);
 
-	int width;
+	tex->handle = SDL_CreateTexture(win.renderer,
+	    SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, w, h);
 
-	if (SDL_QueryTexture(tex->handle, NULL, NULL, &width, NULL) < 0)
-		return 0;
+	if (!tex->handle) {
+		tex->w = tex->h = 0;
+		return error_sdl();
+	}
 
-	return width;
+	tex->w = w;
+	tex->h = h;
+
+	return true;
 }
 
-unsigned int
-texture_height(struct texture *tex)
+bool
+texture_ok(const struct texture *tex)
 {
 	assert(tex);
 
-	int height;
-
-	if (SDL_QueryTexture(tex->handle, NULL, NULL, NULL, &height) < 0)
-		return 0;
-
-	return height;
+	return tex->handle && tex->w && tex->h;
 }
 
 void
 texture_draw(struct texture *tex, int x, int y)
 {
+	assert(tex);
+
 	SDL_Rect dst = {
 		.x = x,
 		.y = y,
-		.w = 0,
-		.h = 0
+		.w = tex->w,
+		.h = tex->h
 	};
 
-	assert(tex);
-
-	/* We need to determine size */
-	SDL_QueryTexture(tex->handle, NULL, NULL, &dst.w, &dst.h);
 	SDL_RenderCopy(win.renderer, tex->handle, NULL, &dst);
 }
 
 void
-texture_draw_ex(struct texture *tex,
-                int src_x,
-                int src_y,
-                unsigned src_w,
-                unsigned src_h,
-                int dst_x,
-                int dst_y,
-                unsigned dst_w,
-                unsigned dst_h,
-                double angle)
+texture_scale(struct texture *tex,
+              int src_x,
+              int src_y,
+              unsigned src_w,
+              unsigned src_h,
+              int dst_x,
+              int dst_y,
+              unsigned dst_w,
+              unsigned dst_h,
+              double angle)
 {
 	const SDL_Rect src = {
 		.x = src_x,
@@ -112,28 +96,31 @@
 }
 
 void
-texture_close(struct texture *tex)
+texture_finish(struct texture *tex)
 {
 	assert(tex);
 
-	SDL_DestroyTexture(tex->handle);
-	free(tex);
+	if (tex->handle)
+		SDL_DestroyTexture(tex->handle);
+
+	memset(tex, 0, sizeof (*tex));
 }
 
 /* private */
 
-struct texture *
-texture_from_surface(SDL_Surface *surface)
+bool
+texture_from_surface(struct texture *tex, SDL_Surface *surface)
 {
+	assert(tex);
 	assert(surface);
 
-	struct texture *texture = ecalloc(1, sizeof (struct texture));
-
-	if (!(texture->handle = SDL_CreateTextureFromSurface(win.renderer, surface))) {
-		error_sdl();
-		free(texture);
-		return NULL;
+	if (!(tex->handle = SDL_CreateTextureFromSurface(win.renderer, surface))) {
+		tex->w = tex->h = 0;
+		return error_sdl();
 	}
 
-	return texture;
+	tex->w = surface->w;
+	tex->h = surface->h;
+
+	return true;
 }
--- a/src/core/texture.h	Thu Mar 26 10:28:11 2020 +0100
+++ b/src/core/texture.h	Mon Mar 30 13:34:42 2020 +0200
@@ -31,41 +31,37 @@
 
 /**
  * \brief Texture object.
- *
- * This object is not publicly defined because it contains
- * implementation-defined data.
  */
-struct texture;
+struct texture {
+	unsigned int w;         /*!< (RO) Texture width. */
+	unsigned int h;         /*!< (RO) Texture height. */
+	void *handle;           /*!< (RO) Native handle. */
+};
 
 /**
  * Create a new texture.
  *
+ * \pre tex != NULL
+ * \param tex the texture to initialize
  * \param w the width
  * \param h the height
- * \return the texture or NULL on error
+ * \return False on error.
  */
-struct texture *
-texture_new(unsigned int w, unsigned int h);
+bool
+texture_new(struct texture *tex, unsigned int w, unsigned int h);
 
 /**
- * Get texture width.
+ * Check if the texture is valid.
+ *
+ * This function simply checks if the texture is initialized and has non-null
+ * dimensions.
  *
  * \pre tex != NULL
- * \param tex the texture
- * \return the width
+ * \param tex the texture to check
+ * \return True if the texture is initialized
  */
-unsigned int
-texture_width(struct texture *tex);
-
-/**
- * Get texture height.
- *
- * \pre tex != NULL
- * \param tex the texture
- * \return the height
- */
-unsigned int
-texture_height(struct texture *tex);
+bool
+texture_ok(const struct texture *tex);
 
 /**
  * Simple texture drawing.
@@ -94,16 +90,16 @@
  * \param angle the angle
  */
 void
-texture_draw_ex(struct texture *tex,
-                int src_x,
-                int src_y,
-                unsigned src_w,
-                unsigned src_h,
-                int dst_x,
-                int dst_y,
-                unsigned dst_w,
-                unsigned dst_h,
-                double angle);
+texture_scale(struct texture *tex,
+              int src_x,
+              int src_y,
+              unsigned src_w,
+              unsigned src_h,
+              int dst_x,
+              int dst_y,
+              unsigned dst_w,
+              unsigned dst_h,
+              double angle);
 
 /**
  * Close the texture, do not use afterwards.
@@ -112,6 +108,6 @@
  * \param tex the texture
  */
 void
-texture_close(struct texture *tex);
+texture_finish(struct texture *tex);
 
 #endif /* !MOLKO_TEXTURE_H */
--- a/src/core/texture_p.h	Thu Mar 26 10:28:11 2020 +0100
+++ b/src/core/texture_p.h	Mon Mar 30 13:34:42 2020 +0200
@@ -19,13 +19,13 @@
 #ifndef MOLKO_TEXTURE_P_H
 #define MOLKO_TEXTURE_P_H
 
+#include <stdbool.h>
+
 #include <SDL.h>
 
-struct texture {
-	SDL_Texture *handle;
-};
+struct texture;
 
-struct texture *
-texture_from_surface(SDL_Surface *surface);
+bool
+texture_from_surface(struct texture *tex, SDL_Surface *surface);
 
 #endif /* !MOLKO_TEXTURE_P_H */