1 /*
2  * This software is licensed under the terms of the MIT License.
3  * See COPYING for further information.
4  * ---
5  * Copyright (c) 2011-2019, Lukas Weber <laochailan@web.de>.
6  * Copyright (c) 2012-2019, Andrei Alexeyev <akari@taisei-project.org>.
7  */
8 
9 #include "taisei.h"
10 
11 #include "sprite.h"
12 #include "video.h"
13 #include "renderer/api.h"
14 
15 ResourceHandler sprite_res_handler = {
16 	.type = RES_SPRITE,
17 	.typename = "sprite",
18 	.subdir = SPRITE_PATH_PREFIX,
19 
20 	.procs = {
21 		.find = sprite_path,
22 		.check = check_sprite_path,
23 		.begin_load = load_sprite_begin,
24 		.end_load = load_sprite_end,
25 		.unload = free,
26 	},
27 };
28 
sprite_path(const char * name)29 char* sprite_path(const char *name) {
30 	char *path = strjoin(SPRITE_PATH_PREFIX, name, SPRITE_EXTENSION, NULL);
31 
32 	VFSInfo pinfo = vfs_query(path);
33 
34 	if(!pinfo.exists) {
35 		free(path);
36 		return texture_path(name);
37 	}
38 
39 	return path;
40 }
41 
check_sprite_path(const char * path)42 bool check_sprite_path(const char *path) {
43 	return strendswith(path, SPRITE_EXTENSION) || check_texture_path(path);
44 }
45 
46 struct sprite_load_state {
47 	Sprite *spr;
48 	uint flags;
49 	char *texture_name;
50 };
51 
load_sprite_begin(const char * path,uint flags)52 void* load_sprite_begin(const char *path, uint flags) {
53 	Sprite *spr = calloc(1, sizeof(Sprite));
54 	struct sprite_load_state *state = calloc(1, sizeof(struct sprite_load_state));
55 	state->spr = spr;
56 	state->flags = flags;
57 
58 	if(check_texture_path(path)) {
59 		state->texture_name = resource_util_basename(TEX_PATH_PREFIX, path);
60 		return state;
61 	}
62 
63 	float ofs_x = 0, ofs_y = 0;
64 
65 	if(!parse_keyvalue_file_with_spec(path, (KVSpec[]) {
66 		{ "texture",        .out_str   = &state->texture_name },
67 		{ "region_x",       .out_float = &spr->tex_area.x },
68 		{ "region_y",       .out_float = &spr->tex_area.y },
69 		{ "region_w",       .out_float = &spr->tex_area.w },
70 		{ "region_h",       .out_float = &spr->tex_area.h },
71 		{ "w",              .out_float = &spr->w },
72 		{ "h",              .out_float = &spr->h },
73 		{ "offset_x",       .out_float = &ofs_x, KVSPEC_DEPRECATED("margin_left; margin_right") },
74 		{ "offset_y",       .out_float = &ofs_y, KVSPEC_DEPRECATED("margin_top; margin_bottom") },
75 		{ "padding_top",    .out_float = &spr->padding.top },
76 		{ "padding_bottom", .out_float = &spr->padding.bottom },
77 		{ "padding_left",   .out_float = &spr->padding.left },
78 		{ "padding_right",  .out_float = &spr->padding.right },
79 		{ NULL }
80 	})) {
81 		free(spr);
82 		free(state->texture_name);
83 		free(state);
84 		log_error("Failed to parse sprite file '%s'", path);
85 		return NULL;
86 	}
87 
88 	spr->padding.left += ofs_x;
89 	spr->padding.right -= ofs_x;
90 	spr->padding.top += ofs_y;
91 	spr->padding.bottom -= ofs_y;
92 
93 	if(!state->texture_name) {
94 		state->texture_name = resource_util_basename(TEX_PATH_PREFIX, path);
95 		log_info("%s: inferred texture name from sprite name", state->texture_name);
96 	}
97 
98 	return state;
99 }
100 
load_sprite_end(void * opaque,const char * path,uint flags)101 void* load_sprite_end(void *opaque, const char *path, uint flags) {
102 	struct sprite_load_state *state = opaque;
103 
104 	if(!state) {
105 		return NULL;
106 	}
107 
108 	Sprite *spr = state->spr;
109 
110 	Resource *res = get_resource(RES_TEXTURE, state->texture_name, flags);
111 
112 	free(state->texture_name);
113 	free(state);
114 
115 	if(res == NULL) {
116 		free(spr);
117 		return NULL;
118 	}
119 
120 	spr->tex = res->data;
121 	uint tw, th;
122 	r_texture_get_size(spr->tex, 0, &tw, &th);
123 
124 	float tex_w_flt = tw;
125 	float tex_h_flt = th;
126 
127 	struct infermap {
128 		const char *dstname;
129 		float *dst;
130 		const char *srcname;
131 		float *src;
132 	} infermap[] = {
133 		{ "region width",  &spr->tex_area.w, "texture width",  &tex_w_flt },
134 		{ "region height", &spr->tex_area.h, "texture height", &tex_h_flt },
135 		{ "sprite width",  &spr->w,          "region width",   &spr->tex_area.w },
136 		{ "sprite height", &spr->h,          "region height",  &spr->tex_area.h },
137 		{ NULL },
138 	};
139 
140 	char *basename = resource_util_basename(SPRITE_PATH_PREFIX, path);
141 
142 	for(struct infermap *m = infermap; m->dst; ++m) {
143 		if(*m->dst <= 0) {
144 			*m->dst = *m->src;
145 			log_info("%s: inferred %s from %s (%g)", basename, m->dstname, m->srcname, *m->src);
146 		}
147 	}
148 
149 	free(basename);
150 	return spr;
151 }
152 
prefix_get_sprite(const char * name,const char * prefix)153 Sprite *prefix_get_sprite(const char *name, const char *prefix) {
154 	uint plen = strlen(prefix);
155 	char buf[plen + strlen(name) + 1];
156 	strcpy(buf, prefix);
157 	strcpy(buf + plen, name);
158 	Sprite *spr = get_sprite(buf);
159 	return spr;
160 }
161 
begin_draw_sprite(float x,float y,float scale_x,float scale_y,Sprite * spr)162 static void begin_draw_sprite(float x, float y, float scale_x, float scale_y, Sprite *spr) {
163 	FloatOffset o = sprite_padded_offset(spr);
164 
165 	begin_draw_texture(
166 		(FloatRect){ x + o.x * scale_x, y + o.y * scale_y, spr->w * scale_x, spr->h * scale_y },
167 		(FloatRect){ spr->tex_area.x, spr->tex_area.y, spr->tex_area.w, spr->tex_area.h },
168 		spr->tex
169 	);
170 }
171 
end_draw_sprite(void)172 static void end_draw_sprite(void) {
173 	end_draw_texture();
174 }
175 
draw_sprite_ex(float x,float y,float scale_x,float scale_y,Sprite * spr)176 static void draw_sprite_ex(float x, float y, float scale_x, float scale_y, Sprite *spr) {
177 	begin_draw_sprite(x, y, scale_x, scale_y, spr);
178 	r_draw_quad();
179 	end_draw_sprite();
180 }
181 
draw_sprite(float x,float y,const char * name)182 void draw_sprite(float x, float y, const char *name) {
183 	draw_sprite_p(x, y, get_sprite(name));
184 }
185 
draw_sprite_p(float x,float y,Sprite * spr)186 void draw_sprite_p(float x, float y, Sprite *spr) {
187 	draw_sprite_ex(x, y, 1, 1, spr);
188 }
189