2f011c9d8def5fd2cbd070ff10f05a3a20860068
[img2tiles] / src / main.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <assert.h>
5 #include <errno.h>
6 #include "image.h"
7 #include "dynarr.h"
8
9 void print_usage(const char *argv0);
10 int proc_image(const char *fname);
11 void wrtiles_carr(FILE *fp, const char *name, struct image *timg, int ntiles);
12 void wrtilemap_carr(FILE *fp, const char *name, int *tmap, int xtiles, int ytiles);
13 void wrpalette_carr(FILE *fp, const char *name, struct cmapent *cmap, int ncol);
14
15 enum { OUT_IMG, OUT_C, OUT_ASM } outmode;
16 int tile_xsz = 8, tile_ysz = 8;
17
18 int main(int argc, char **argv)
19 {
20         int i;
21
22         for(i=1; i<argc; i++) {
23                 if(argv[i][0] == '-') {
24                         if(argv[i][2] == 0) {
25                                 switch(argv[i][1]) {
26                                 case 't':
27                                         if(sscanf(argv[++i], "%dx%d", &tile_xsz, &tile_ysz) != 2) {
28                                                 fprintf(stderr, "-t must be followed by the tile size (WxH)\n");
29                                                 return 1;
30                                         }
31                                         break;
32
33                                 case 'c':
34                                         outmode = OUT_C;
35                                         break;
36
37                                 case 's':
38                                         outmode = OUT_ASM;
39                                         break;
40
41                                 case 'h':
42                                         print_usage(argv[0]);
43                                         return 0;
44
45                                 default:
46                                         fprintf(stderr, "invalid option: %s\n", argv[i]);
47                                         print_usage(argv[0]);
48                                         return 1;
49                                 }
50                         } else {
51                                 fprintf(stderr, "invalid option: %s\n", argv[i]);
52                                 print_usage(argv[0]);
53                                 return 1;
54                         }
55
56                 } else {
57                         if(proc_image(argv[i]) == -1) {
58                                 return 1;
59                         }
60                 }
61         }
62
63         return 0;
64 }
65
66 void print_usage(const char *argv0)
67 {
68         printf("Usage: %s [options] <img1> [<img2> ... <imgN>]\n", argv0);
69         printf("Options:\n");
70         printf(" -t WxH: tile size (default 8x8)\n");
71         printf(" -c: output C array\n");
72         printf(" -s: output GNU assembler data\n");
73         printf(" -h: print usage and exit\n\n");
74 }
75
76 int find_tile(struct image *tile, struct image *tiles)
77 {
78         int i, count;
79         count = dynarr_size(tiles);
80         for(i=0; i<count; i++) {
81                 if(cmp_image(tile, tiles + i) == 0) {
82                         return i;
83                 }
84         }
85         return -1;
86 }
87
88 int proc_image(const char *fname)
89 {
90         int i, j, k, idx, xtiles, ytiles, ntiles, result = -1;
91         FILE *fp;
92         struct image img, tile;
93         struct image *tiles = 0;
94         unsigned char *tiles_pixels, *tptr;
95         unsigned char *sptr;
96         int *tilemap = 0, *mapptr;
97         char *basename, *suffix, *outfile;
98
99         if(load_image(&img, fname) == -1) {
100                 fprintf(stderr, "failed to load image: %s\n", fname);
101                 return -1;
102         }
103         basename = alloca(strlen(fname) + 1);
104         strcpy(basename, fname);
105         if((suffix = strrchr(basename, '/'))) {
106                 basename = suffix;
107         }
108         if((suffix = strchr(basename, '.'))) {
109                 *suffix = 0;
110         }
111
112         xtiles = img.width / tile_xsz;
113         ytiles = img.height / tile_ysz;
114         if(img.width % tile_xsz != 0 || img.height % tile_ysz != 0) {
115                 fprintf(stderr, "image size (%dx%d) not evenly divisible into %dx%d tiles\n",
116                                 img.width, img.height, tile_xsz, tile_ysz);
117                 goto err;
118         }
119
120         if(!(tilemap = malloc(xtiles * ytiles * sizeof *tilemap))) {
121                 fprintf(stderr, "failed to allocate %dx%d tilemap\n", xtiles, ytiles);
122                 goto err;
123         }
124         mapptr = tilemap;
125
126         if(!(tiles = dynarr_alloc(0, sizeof *tiles))) {
127                 fprintf(stderr, "failed to allocate tile array\n");
128                 goto err;
129         }
130         /* alloc a contiguous buffer for the full tileset pixels, to make it easier to write it
131          * out as a single image in the end
132          */
133         if(!(tiles_pixels = dynarr_alloc(0, tile_xsz * tile_ysz * img.bpp / 8))) {
134                 fprintf(stderr, "failed to allocate tile pixel buffer\n");
135                 goto err;
136         }
137
138         tile = img;
139         tile.width = tile_xsz;
140         tile.height = tile_ysz;
141         tile.scansz = tile_xsz * tile.bpp / 8;
142
143         sptr = img.pixels;
144         for(i=0; i<ytiles; i++) {
145                 for(j=0; j<xtiles; j++) {
146                         tile.pixels = sptr;
147
148                         if((idx = find_tile(&tile, tiles)) == -1) {
149                                 /* we don't have a duplicate of this tile */
150                                 idx = dynarr_size(tiles);
151
152                                 if(!(tiles = dynarr_push(tiles, 0))) {
153                                         goto err;
154                                 }
155                                 if(!(tptr = dynarr_push(tiles_pixels, 0))) {
156                                         goto err;
157                                 }
158
159                                 tiles[idx] = tile;
160                                 tiles[idx].scansz = tiles[idx].pitch = tile.scansz;
161
162                                 /* did the array get relocated? */
163                                 if(tptr != tiles_pixels) {
164                                         tiles_pixels = tptr;
165                                         /* make each tile's pixels pointer point to the right place in the large pixelbuffer */
166                                         for(k=0; k<idx+1; k++) {
167                                                 tiles[k].pixels = tptr;
168                                                 tptr += tile_ysz * tiles[idx].pitch;
169                                         }
170                                 } else {
171                                         /* otherwise just set the new one */
172                                         tiles[idx].pixels = tiles_pixels + idx * tile_ysz * tiles[idx].pitch;
173                                 }
174
175                                 blit(&tile, 0, 0, tile_xsz, tile_ysz, tiles + idx, 0, 0);
176                         }
177
178                         *mapptr++ = idx;
179
180                         sptr += tile.scansz;
181                 }
182                 sptr += (tile_ysz - 1) * tile.scansz;
183         }
184
185         ntiles = dynarr_size(tiles);
186         fprintf(stderr, "%s (%dx%d) -> %d tiles, %d unique\n", fname, img.width, img.height,
187                         xtiles * ytiles, ntiles);
188
189         assert(ntiles > 0);
190
191         /* make a big image out of the tiles and write it out */
192         tile = tiles[0];
193         tile.height = ntiles * tile_ysz;
194
195         outfile = alloca(strlen(basename) + 5);
196
197         switch(outmode) {
198         case OUT_IMG:
199                 sprintf(outfile, "%s.png", basename);
200
201                 if(save_image(&tile, outfile) == -1) {
202                         fprintf(stderr, "failed to write output image\n");
203                         goto err;
204                 }
205                 break;
206
207         case OUT_C:
208                 sprintf(outfile, "%s.c", basename);
209
210                 if(!(fp = fopen(outfile, "w"))) {
211                         fprintf(stderr, "failed to open output file: %s: %s\n", outfile, strerror(errno));
212                         goto err;
213                 }
214                 wrtiles_carr(fp, basename, &tile, ntiles);
215                 wrtilemap_carr(fp, basename, tilemap, xtiles, ytiles);
216                 wrpalette_carr(fp, basename, tile.cmap, tile.cmap_ncolors);
217                 break;
218
219         case OUT_ASM:
220                 sprintf(outfile, "%s.s", basename);
221                 /* TODO */
222                 break;
223         }
224
225         result = 0;
226
227 err:
228         dynarr_free(tiles_pixels);
229         dynarr_free(tiles);
230         free(tilemap);
231         free(img.pixels);
232         return result;
233 }
234
235 void wrtiles_carr(FILE *fp, const char *name, struct image *timg, int ntiles)
236 {
237         int i, j, curx = 0;
238         unsigned char *ptr = timg->pixels;
239
240         fprintf(fp, "\nint %s_num_tiles = %d;\n", name, ntiles);
241         fprintf(fp, "int %s_tiles_width = %d;\n", name, timg->width);
242         fprintf(fp, "int %s_tiles_height = %d;\n", name, timg->height);
243         fprintf(fp, "int %s_tiles_bpp = %d;\n", name, timg->bpp);
244         fprintf(fp, "unsigned char %s_tiles[] = {\n", name);
245
246         for(i=0; i<timg->height; i++) {
247                 for(j=0; j<timg->scansz; j++) {
248                         if(curx == 0) {
249                                 curx = 3 + fprintf(fp, "\t%u", (unsigned int)*ptr++);
250                         } else {
251                                 curx += fprintf(fp, ", %u", (unsigned int)*ptr++);
252                         }
253                         if(curx >= 80) {
254                                 fprintf(fp, ",\n");
255                                 curx = 0;
256                         }
257                 }
258                 ptr += timg->pitch - timg->scansz;
259         }
260
261         fprintf(fp, "\n};\n\n");
262 }
263
264 void wrtilemap_carr(FILE *fp, const char *name, int *tmap, int xtiles, int ytiles)
265 {
266         int i, sz, curx = 0;
267
268         fprintf(fp, "\nint %s_tilemap_cols = %d;\n", name, xtiles);
269         fprintf(fp, "int %s_tilemap_rows = %d;\n", name, ytiles);
270         fprintf(fp, "unsigned int %s_tilemap[] = {\n", name);
271
272         sz = xtiles * ytiles;
273         for(i=0; i<sz; i++) {
274                 if(curx == 0) {
275                         curx = 3 + fprintf(fp, "\t%u", (unsigned int)tmap[i]);
276                 } else {
277                         curx += fprintf(fp, ", %u", (unsigned int)tmap[i]);
278                 }
279                 if(curx >= 80) {
280                         fprintf(fp, ",\n");
281                         curx = 0;
282                 }
283         }
284
285         fprintf(fp, "\n};\n\n");
286 }
287
288 void wrpalette_carr(FILE *fp, const char *name, struct cmapent *cmap, int ncol)
289 {
290         int i;
291
292         fprintf(fp, "\nint %s_cmap_colors = %d;\n", name, ncol);
293         fprintf(fp, "unsigned char %s_cmap[][3] = {\n", name);
294         for(i=0; i<ncol; i++) {
295                 fprintf(fp, "\t{%u, %u, %u}", cmap[i].r, cmap[i].g, cmap[i].b);
296                 if(i < ncol - 1) {
297                         fputs(",\n", fp);
298                 } else {
299                         fputc('\n', fp);
300                 }
301         }
302         fprintf(fp, "};\n\n");
303 }