backported imago from eradicate
[dosdemo] / src / image.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <errno.h>
5 #include <imago2.h>
6 #include "image.h"
7 #include "treestor.h"
8 #include "util.h"
9
10 int load_image(struct image *img, const char *fname)
11 {
12         FILE *fp;
13         char sig[8];
14         uint16_t width, height;
15
16         memset(img, 0, sizeof *img);
17
18         if(!(fp = fopen(fname, "rb"))) {
19                 fprintf(stderr, "load_image: failed to open file: %s: %s\n", fname, strerror(errno));
20                 return -1;
21         }
22         if(fread(sig, 1, 8, fp) < 8) {
23                 fprintf(stderr, "unexpected EOF while reading: %s\n", fname);
24                 fclose(fp);
25                 return -1;
26         }
27
28         if(memcmp(sig, "IDUMP565", 8) != 0) {
29                 goto not565;
30         }
31
32         /* it's a 565 dump, read it and return */
33         if(!fread(&width, 2, 1, fp) || !fread(&height, 2, 1, fp)) {
34                 fprintf(stderr, "unexpected EOF while reading: %s\n", fname);
35                 fclose(fp);
36                 return -1;
37         }
38 #ifdef BUILD_BIGENDIAN
39         width = BSWAP16(width);
40         height = BSWAP16(height);
41 #endif
42
43         if(!(img->pixels = malloc(width * height * 2))) {
44                 fprintf(stderr, "failed to allocate %dx%d pixel buffer for %s\n", width, height, fname);
45                 fclose(fp);
46                 return -1;
47         }
48         if(fread(img->pixels, 2, width * height, fp) < width * height) {
49                 fprintf(stderr, "unexpected EOF while reading: %s\n", fname);
50                 free(img->pixels);
51                 img->pixels = 0;
52                 fclose(fp);
53                 return -1;
54         }
55
56 #ifdef BUILD_BIGENDIAN
57         {
58                 int i, npix = width * height;
59                 for(i=0; i<npix; i++) {
60                         uint16_t p = img->pixels[i];
61                         img->pixels[i] = BSWAP16(p);
62                 }
63         }
64 #endif
65
66         fclose(fp);
67         img->width = width;
68         img->height = height;
69         return 0;
70 not565:
71         fclose(fp);
72
73         if(memcmp(sig, "animtex", 7) == 0) {
74                 /* it's an animated texture. read metadata, and recurse with the new name*/
75                 struct ts_node *root, *node;
76                 const char *imgfname;
77                 int fps, num_frames = 0;
78
79                 if(!(root = ts_load(fname))) {
80                         fprintf(stderr, "failed to load animation %s\n", fname);
81                         return -1;
82                 }
83                 if(!(imgfname = ts_lookup_str(root, "animtex.image", 0))) {
84                         fprintf(stderr, "animtex %s missing `image` attribute\n", fname);
85                         ts_free_tree(root);
86                         return -1;
87                 }
88                 if(strcmp(imgfname, fname) == 0) {
89                         fprintf(stderr, "animtex %s is silly...\n", fname);
90                         ts_free_tree(root);
91                         return -1;
92                 }
93
94                 if(load_image(img, imgfname) == -1) {
95                         ts_free_tree(root);
96                         return -1;
97                 }
98
99                 fps = ts_lookup_int(root, "animtex.framerate", 25);
100                 img->frame_interval = 1.0f / (float)fps;
101
102                 /* count frames */
103                 node = root->child_list;
104                 while(node) {
105                         if(strcmp(node->name, "frame") == 0) {
106                                 num_frames++;
107                         }
108                         node = node->next;
109                 }
110
111                 if(!(img->uoffs = malloc(2 * num_frames * sizeof *img->uoffs))) {
112                         fprintf(stderr, "animtex %s: failed to allocate uvoffset array for %d frames\n", fname, num_frames);
113                         free(img->pixels);
114                         ts_free_tree(root);
115                         return -1;
116                 }
117                 img->voffs = img->uoffs + num_frames;
118
119                 num_frames = 0;
120                 node = root->child_list;
121                 while(node) {
122                         if(strcmp(node->name, "frame") == 0) {
123                                 float *v = ts_get_attr_vec(node, "uvoffset", 0);
124                                 if(v) {
125                                         img->uoffs[num_frames] = v[0];
126                                         img->voffs[num_frames++] = v[1];
127                                 } else {
128                                         fprintf(stderr, "animtex %s: ignoring frame without uvoffset\n", fname);
129                                 }
130                         }
131                         node = node->next;
132                 }
133
134                 img->num_frames = num_frames;
135
136                 ts_free_tree(root);
137                 return 0;
138         }
139
140         /* just a regular image */
141         if(!(img->pixels = img_load_pixels(fname, &img->width, &img->height, IMG_FMT_RGB565))) {
142                 fprintf(stderr, "failed to load image: %s\n", fname);
143                 return -1;
144         }
145         return 0;
146 }
147
148 int dump_image(struct image *img, const char *fname)
149 {
150         FILE *fp;
151         uint16_t width, height;
152
153 #ifdef BUILD_BIGENDIAN
154         int i, npix = img->width * img->height;
155         width = BSWAP16(img->width);
156         height = BSWAP16(img->height);
157 #else
158         width = img->width;
159         height = img->height;
160 #endif
161
162         if(!(fp = fopen(fname, "wb"))) {
163                 fprintf(stderr, "dump_image: failed to open %s: %s\n", fname, strerror(errno));
164                 return -1;
165         }
166         fwrite("IDUMP565", 1, 8, fp);
167         fwrite(&width, 2, 1, fp);
168         fwrite(&height, 2, 1, fp);
169
170 #ifdef BUILD_BIGENDIAN
171         for(i=0; i<npix; i++) {
172                 uint16_t p = BSWAP16(img->pixels[i]);
173                 fwrite(&p, 2, 1, fp);
174         }
175 #else
176         fwrite(img->pixels, 2, img->width * img->height, fp);
177 #endif
178         fclose(fp);
179         return 0;
180 }
181
182 void destroy_image(struct image *img)
183 {
184         if(img) {
185                 img_free_pixels(img->pixels);
186                 img->pixels = 0;
187         }
188 }
189
190 int load_cubemap(struct image *cube, const char *fname_fmt)
191 {
192         int i;
193         char dirstr[3] = {0};
194         char fname[256];
195
196         for(i=0; i<6; i++) {
197                 dirstr[0] = i & 1 ? 'n' : 'p';
198                 dirstr[1] = i < 2 ? 'x' : (i < 4 ? 'y' : 'z');
199                 sprintf(fname, fname_fmt, dirstr);
200                 if(load_image(cube + i, fname) == -1) {
201                         while(--i >= 0) {
202                                 destroy_image(cube + i);
203                         }
204                         return -1;
205                 }
206         }
207         return 0;
208 }
209
210 void destroy_cubemap(struct image *cube)
211 {
212         int i;
213
214         if(!cube) return;
215
216         for(i=0; i<6; i++) {
217                 destroy_image(cube + i);
218         }
219 }