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