Blob Blame Raw

#include <glib.h>
#include <cairo.h>

#include "private.h"
#include "world.h"
#include "sprite.h"


static HeliArray cache;

static cairo_status_t read(void *closure, unsigned char *data, unsigned int length) {
	return fread(data, length, 1, (FILE*)closure)
		 ? CAIRO_STATUS_SUCCESS : CAIRO_STATUS_READ_ERROR;
}

static cairo_surface_t* loadFrame(const char *path) {
	if (!heliStringEndsWithLowcase(path, ".png"))
		return NULL;
	FILE *f = fopen(path, "rb");
	if (f) {
		cairo_surface_t *frame = cairo_image_surface_create_from_png_stream(&read, f);
		fclose(f);
		if (!frame)
			fprintf(stderr, "helianthus: cannot load PNG content form file: %s\n", path);
		return frame;
	}
	fprintf(stderr, "helianthus: cannot open image file: %s\n", path);
	return NULL;
}

static HeliAnimationInstance* load(const char *path) {
	HeliAnimationInstance *a = calloc(1, sizeof(*a));
	a->path = heliStringCopy(path);
	
	GDir *dir = g_dir_open(path, 0, NULL);
	if (dir) {
		while(TRUE) {
			const char* name = g_dir_read_name(dir);
			if (!name) break;
			char *p = heliStringConcat3(path, "/", name);
			cairo_surface_t *frame = loadFrame(p);
			free(p);
			if (frame) heliArrayInsert(&a->frames, -1, frame, (HeliFreeCallback)&cairo_surface_destroy);
		}
	} else {
		cairo_surface_t *frame = loadFrame(path);
		if (frame) {
			heliArrayInsert(&a->frames, -1, frame, (HeliFreeCallback)&cairo_surface_destroy);
		} else {
			fprintf(stderr, "helianthus: cannot load animation by path: %s\n", path);
		}
	}
	
	return a;
}

static void unload(HeliAnimationInstance *a) {
	assert(!a->refcount);
	free(a->path);
	heliArrayDestroy(&a->frames);
	free(a);
}

Animation createAnimation(const char *path) {
	HeliPair *item = heliStringmapGet(&cache, path);
	if (!item) item = heliStringmapAdd(&cache, path, load(path), (HeliFreeCallback)&unload);
	HeliAnimationInstance *a = (HeliAnimationInstance*)item->value;
	++a->refcount;
	
	Animation animation = calloc(1, sizeof(*animation));
	animation->instance = a;
	++animation->refcount;
	return animation;
}

void animationDestroy(Animation animation) {
	if (--animation->refcount <= 0) {
		if (--animation->instance->refcount <= 0)
			heliStringmapRemove(&cache, animation->instance->path);
		free(animation);
	}
}

void heliAnimationFinish()
	{ heliArrayDestroy(&cache); }