character: reuse already loaded bitmaps

This commit is contained in:
Sebastian Krzyszkowiak 2018-08-03 05:02:44 +02:00
parent 05c75a9437
commit 093a808dc2
6 changed files with 120 additions and 30 deletions

View file

@ -105,8 +105,13 @@ SYMBOL_EXPORT void LoadSpritesheets(struct Game* game, struct Character* charact
PrintConsole(game, "- %s", tmp->name);
if ((!tmp->bitmap) && (tmp->file)) {
char filename[255] = {0};
snprintf(filename, 255, "sprites/%s/%s", character->name, tmp->file);
tmp->bitmap = al_load_bitmap(GetDataFilePath(game, filename));
if (strstr(tmp->file, "../") == tmp->file) {
snprintf(filename, 255, "sprites/%s", tmp->file + 3);
} else {
snprintf(filename, 255, "sprites/%s/%s", character->name, tmp->file);
}
tmp->bitmap = AddBitmap(game, filename);
tmp->filepath = strdup(filename);
tmp->width = al_get_bitmap_width(tmp->bitmap) / tmp->cols;
tmp->height = al_get_bitmap_height(tmp->bitmap) / tmp->rows;
}
@ -116,8 +121,13 @@ SYMBOL_EXPORT void LoadSpritesheets(struct Game* game, struct Character* charact
PrintConsole(game, " - %s", tmp->frames[i].file);
}
char filename[255] = {0};
snprintf(filename, 255, "sprites/%s/%s", character->name, tmp->frames[i].file);
tmp->frames[i].bitmap = al_load_bitmap(GetDataFilePath(game, filename));
if (strstr(tmp->frames[i].file, "../") == tmp->frames[i].file) {
snprintf(filename, 255, "sprites/%s", tmp->frames[i].file + 3);
} else {
snprintf(filename, 255, "sprites/%s/%s", character->name, tmp->frames[i].file);
}
tmp->frames[i].bitmap = AddBitmap(game, filename);
tmp->frames[i].filepath = strdup(filename);
int width = al_get_bitmap_width(tmp->frames[i].bitmap) + tmp->frames[i].x;
if (width > tmp->width) {
tmp->width = width;
@ -146,12 +156,14 @@ SYMBOL_EXPORT void UnloadSpritesheets(struct Game* game, struct Character* chara
struct Spritesheet* tmp = character->spritesheets;
while (tmp) {
for (int i = 0; i < tmp->frameCount; i++) {
if (tmp->frames[i].bitmap) {
if (tmp->frames[i].filepath) {
RemoveBitmap(game, tmp->frames[i].filepath);
} else {
al_destroy_bitmap(tmp->frames[i].bitmap);
}
}
if (tmp->bitmap) {
al_destroy_bitmap(tmp->bitmap);
RemoveBitmap(game, tmp->filepath);
}
tmp->bitmap = NULL;
tmp = tmp->next;
@ -227,6 +239,8 @@ SYMBOL_EXPORT void RegisterSpritesheet(struct Game* game, struct Character* char
strncpy(s->predecessor, predecessor, len);
}
s->filepath = NULL;
{
s->file = NULL;
const char* file = al_get_config_value(config, "animation", "file");
@ -263,6 +277,7 @@ SYMBOL_EXPORT void RegisterSpritesheet(struct Game* game, struct Character* char
s->frames[i].file = malloc(len * sizeof(char));
strncpy(s->frames[i].file, file, len);
}
s->frames[i].filepath = NULL;
if (!file) {
s->frames[i].col = i % s->cols;
@ -342,15 +357,23 @@ SYMBOL_EXPORT void DestroyCharacter(struct Game* game, struct Character* charact
free(tmp->file);
}
for (int i = 0; i < tmp->frameCount; i++) {
if (tmp->frames[i].bitmap) {
if (tmp->frames[i].filepath) {
RemoveBitmap(game, tmp->frames[i].filepath);
} else {
al_destroy_bitmap(tmp->frames[i].bitmap);
}
if (tmp->frames[i].file) {
free(tmp->frames[i].file);
}
if (tmp->frames[i].filepath) {
free(tmp->frames[i].filepath);
}
}
if (tmp->bitmap) {
al_destroy_bitmap(tmp->bitmap);
RemoveBitmap(game, tmp->filepath);
}
if (tmp->filepath) {
free(tmp->filepath);
}
free(tmp->frames);
free(tmp->name);
@ -475,6 +498,7 @@ SYMBOL_EXPORT ALLEGRO_TRANSFORM GetCharacterTransform(struct Game* game, struct
ALLEGRO_TRANSFORM transform;
int w = character->spritesheet->width, h = character->spritesheet->height;
al_identity_transform(&transform);
al_translate_transform(&transform, -w / 2.0, -h / 2.0);
al_scale_transform(&transform, ((character->flipX ^ character->spritesheet->flipX ^ character->frame->flipX) ? -1 : 1), ((character->flipY ^ character->spritesheet->flipY ^ character->frame->flipY) ? -1 : 1)); // flipping; FIXME: should it be here or later?
al_translate_transform(&transform, w / 2.0, h / 2.0);
@ -483,10 +507,13 @@ SYMBOL_EXPORT ALLEGRO_TRANSFORM GetCharacterTransform(struct Game* game, struct
al_translate_transform(&transform, -w * character->spritesheet->pivotX, -h * character->spritesheet->pivotY); // pivot
al_scale_transform(&transform, character->scaleX, character->scaleY);
al_rotate_transform(&transform, character->angle);
al_translate_transform(&transform, GetCharacterX(game, character), GetCharacterY(game, character)); // position
if (character->parent) {
ALLEGRO_TRANSFORM parent = GetCharacterTransform(game, character->parent);
al_compose_transform(&transform, &parent);
// FIXME: position should be calculated in relation to parents pivot point
}
return transform;
}

View file

@ -27,6 +27,7 @@
struct SpritesheetFrame {
char* file;
char* filepath;
ALLEGRO_BITMAP* bitmap;
double duration;
int row;
@ -46,6 +47,7 @@ struct Spritesheet {
int cols; /*!< Number of columns in the spritesheet. */
double duration;
char* file;
char* filepath;
int repeats; /*!< Number of repeats to make before the spritesheet is changed to its successor. */
char* successor; /*!< Name of animation successor. If it's not blank, then animation will be played only once. */
char* predecessor;

View file

@ -359,13 +359,13 @@ SYMBOL_INTERNAL void CloseGamestate(struct Game* game, struct Gamestate* gamesta
al_destroy_bitmap(gamestate->fb);
}
SYMBOL_INTERNAL struct libsuperderpy_list* AddToList(struct libsuperderpy_list* list, void* data) {
SYMBOL_INTERNAL struct List* AddToList(struct List* list, void* data) {
if (!list) {
list = malloc(sizeof(struct libsuperderpy_list));
list = malloc(sizeof(struct List));
list->data = data;
list->next = NULL;
} else {
struct libsuperderpy_list* elem = malloc(sizeof(struct libsuperderpy_list));
struct List* elem = malloc(sizeof(struct List));
elem->next = list;
elem->data = data;
list = elem;
@ -373,12 +373,12 @@ SYMBOL_INTERNAL struct libsuperderpy_list* AddToList(struct libsuperderpy_list*
return list;
}
static bool Identity(struct libsuperderpy_list* elem, void* data) {
static bool Identity(struct List* elem, void* data) {
return elem->data == data;
}
SYMBOL_INTERNAL struct libsuperderpy_list* FindInList(struct libsuperderpy_list* list, void* data, bool (*identity)(struct libsuperderpy_list* elem, void* data)) {
struct libsuperderpy_list* tmp = list;
SYMBOL_INTERNAL struct List* FindInList(struct List* list, void* data, bool (*identity)(struct List* elem, void* data)) {
struct List* tmp = list;
if (!identity) {
identity = Identity;
}
@ -391,8 +391,8 @@ SYMBOL_INTERNAL struct libsuperderpy_list* FindInList(struct libsuperderpy_list*
return NULL;
}
SYMBOL_INTERNAL void* RemoveFromList(struct libsuperderpy_list** list, void* data, bool (*identity)(struct libsuperderpy_list* elem, void* data)) {
struct libsuperderpy_list *prev = NULL, *tmp = *list, *start;
SYMBOL_INTERNAL void* RemoveFromList(struct List** list, void* data, bool (*identity)(struct List* elem, void* data)) {
struct List *prev = NULL, *tmp = *list, *start;
void* d = NULL;
if (!identity) {
identity = Identity;
@ -424,7 +424,7 @@ SYMBOL_INTERNAL void* AddGarbage(struct Game* game, void* data) {
}
SYMBOL_INTERNAL void ClearGarbage(struct Game* game) {
struct libsuperderpy_list* tmp;
struct List* tmp;
while (game->_priv.garbage) {
free(game->_priv.garbage->data);
tmp = game->_priv.garbage->next;
@ -438,16 +438,16 @@ SYMBOL_INTERNAL void AddTimeline(struct Game* game, struct Timeline* timeline) {
}
SYMBOL_INTERNAL void RemoveTimeline(struct Game* game, struct Timeline* timeline) {
struct libsuperderpy_list* tmp = game->_priv.timelines;
struct List* tmp = game->_priv.timelines;
if (tmp->data == timeline) {
struct libsuperderpy_list* next = tmp->next;
struct List* next = tmp->next;
free(tmp);
game->_priv.timelines = next;
return;
}
while (tmp->next) {
if (tmp->next->data == timeline) {
struct libsuperderpy_list* next = tmp->next->next;
struct List* next = tmp->next->next;
free(tmp->next);
tmp->next = next;
return;
@ -500,7 +500,7 @@ SYMBOL_INTERNAL void DrawTimelines(struct Game* game) {
if (!game->_priv.showtimeline) {
return;
}
struct libsuperderpy_list* tmp = game->_priv.timelines;
struct List* tmp = game->_priv.timelines;
int i = 0;
while (tmp) {
DrawTimeline(game, tmp->data, i);
@ -574,3 +574,54 @@ SYMBOL_INTERNAL char* GetGameName(struct Game* game, const char* format) {
SUPPRESS_END
return AddGarbage(game, result);
}
static int HashString(struct Game* game, const char* str) {
unsigned long hash = 5381;
int c;
while ((c = *str++)) {
hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
}
//PrintConsole(game, "sum %d, bucket %d", hash, hash % LIBSUPERDERPY_BITMAP_HASHMAP_BUCKETS);
return hash % LIBSUPERDERPY_BITMAP_HASHMAP_BUCKETS;
}
static bool RefCountIdentity(struct List* elem, void* data) {
struct RefCount* item = elem->data;
return strcmp(data, item->id) == 0;
}
SYMBOL_INTERNAL ALLEGRO_BITMAP* AddBitmap(struct Game* game, char* filename) {
int bucket = HashString(game, filename);
struct List* item = FindInList(game->_priv.bitmaps[bucket], filename, RefCountIdentity);
struct RefCount* rc;
if (item) {
rc = item->data;
rc->counter++;
} else {
rc = malloc(sizeof(struct RefCount));
rc->counter = 1;
rc->id = strdup(filename);
rc->data = al_load_bitmap(GetDataFilePath(game, filename));
game->_priv.bitmaps[bucket] = AddToList(game->_priv.bitmaps[bucket], rc);
}
return rc->data;
}
SYMBOL_INTERNAL void RemoveBitmap(struct Game* game, char* filename) {
int bucket = HashString(game, filename);
struct List* item = FindInList(game->_priv.bitmaps[bucket], filename, RefCountIdentity);
if (item) {
struct RefCount* rc = item->data;
rc->counter--;
if (rc->counter == 0) {
RemoveFromList(&game->_priv.bitmaps[bucket], filename, RefCountIdentity);
al_destroy_bitmap(rc->data);
free(rc->id);
free(rc);
}
} else {
PrintConsole(game, "Tried to remove non-existent bitmap %s!", filename);
}
}

View file

@ -46,9 +46,15 @@
#define SUPPRESS_END
#endif
struct libsuperderpy_list {
struct RefCount {
int counter;
char* id;
void* data;
struct libsuperderpy_list* next;
};
struct List {
void* data;
struct List* next;
};
struct GamestateLoadingThreadData {
@ -80,9 +86,9 @@ void GamestateProgress(struct Game* game);
void* AddGarbage(struct Game* game, void* data);
void ClearGarbage(struct Game* game);
void ClearScreen(struct Game* game);
struct libsuperderpy_list* AddToList(struct libsuperderpy_list* list, void* data);
struct libsuperderpy_list* FindInList(struct libsuperderpy_list* list, void* data, bool (*identity)(struct libsuperderpy_list* elem, void* data));
void* RemoveFromList(struct libsuperderpy_list** list, void* data, bool (*identity)(struct libsuperderpy_list* elem, void* data));
struct List* AddToList(struct List* list, void* data);
struct List* FindInList(struct List* list, void* data, bool (*identity)(struct List* elem, void* data));
void* RemoveFromList(struct List** list, void* data, bool (*identity)(struct List* elem, void* data));
void AddTimeline(struct Game* game, struct Timeline* timeline);
void RemoveTimeline(struct Game* game, struct Timeline* timeline);
void DrawTimelines(struct Game* game);
@ -96,5 +102,7 @@ void ResumeExecution(struct Game* game);
void ReloadShaders(struct Game* game, bool force);
void DestroyShaders(struct Game* game);
char* GetGameName(struct Game* game, const char* format);
ALLEGRO_BITMAP* AddBitmap(struct Game* game, char* filename);
void RemoveBitmap(struct Game* game, char* filename);
#endif

View file

@ -57,6 +57,8 @@ struct GamestateResources;
#include <math.h>
#include <sys/param.h>
#define LIBSUPERDERPY_BITMAP_HASHMAP_BUCKETS 16
struct Gamestate;
struct Viewport {
@ -129,7 +131,7 @@ struct Game {
struct Gamestate* current_gamestate;
struct libsuperderpy_list *garbage, *timelines, *shaders;
struct List *garbage, *timelines, *shaders, *bitmaps[LIBSUPERDERPY_BITMAP_HASHMAP_BUCKETS];
bool draw;

View file

@ -96,7 +96,7 @@ SYMBOL_EXPORT ALLEGRO_SHADER* CreateShader(struct Game* game, const char* vertex
return shader;
}
static bool ShaderIdentity(struct libsuperderpy_list* item, void* shader) {
static bool ShaderIdentity(struct List* item, void* shader) {
return ((struct ShaderListItem*)item->data)->shader == shader;
}
@ -118,7 +118,7 @@ SYMBOL_EXPORT void DestroyShader(struct Game* game, ALLEGRO_SHADER* shader) {
}
SYMBOL_INTERNAL void ReloadShaders(struct Game* game, bool force) {
struct libsuperderpy_list* list = game->_priv.shaders;
struct List* list = game->_priv.shaders;
PrintConsole(game, "Reloading shaders...");
while (list) {
struct ShaderListItem* item = list->data;
@ -150,7 +150,7 @@ SYMBOL_INTERNAL void DestroyShaders(struct Game* game) {
if (item->fragment) {
free(item->fragment);
}
struct libsuperderpy_list* prev = game->_priv.shaders;
struct List* prev = game->_priv.shaders;
game->_priv.shaders = game->_priv.shaders->next;
free(prev);
}