0474894a341e5e15aeb8fc84bdcace1678672772
[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 = strchr(basename, '.'))) {
106                 *suffix = 0;
107         }
108
109         xtiles = img.width / tile_xsz;
110         ytiles = img.height / tile_ysz;
111         if(img.width % tile_xsz != 0 || img.height % tile_ysz != 0) {
112                 fprintf(stderr, "image size (%dx%d) not evenly divisible into %dx%d tiles\n",
113                                 img.width, img.height, tile_xsz, tile_ysz);
114                 goto err;
115         }
116
117         if(!(tilemap = malloc(xtiles * ytiles * sizeof *tilemap))) {
118                 fprintf(stderr, "failed to allocate %dx%d tilemap\n", xtiles, ytiles);
119                 goto err;
120         }
121         mapptr = tilemap;
122
123         if(!(tiles = dynarr_alloc(0, sizeof *tiles))) {
124                 fprintf(stderr, "failed to allocate tile array\n");
125                 goto err;
126         }
127         /* alloc a contiguous buffer for the full tileset pixels, to make it easier to write it
128          * out as a single image in the end
129          */
130         if(!(tiles_pixels = dynarr_alloc(0, tile_xsz * tile_ysz * img.bpp / 8))) {
131                 fprintf(stderr, "failed to allocate tile pixel buffer\n");
132                 goto err;
133         }
134
135         tile = img;
136         tile.width = tile_xsz;
137         tile.height = tile_ysz;
138         tile.scansz = tile_xsz * tile.bpp / 8;
139
140         sptr = img.pixels;
141         for(i=0; i<ytiles; i++) {
142                 for(j=0; j<xtiles; j++) {
143                         tile.pixels = sptr;
144
145                         if((idx = find_tile(&tile, tiles)) == -1) {
146                                 /* we don't have a duplicate of this tile */
147                                 idx = dynarr_size(tiles);
148
149                                 if(!(tiles = dynarr_push(tiles, 0))) {
150                                         goto err;
151                                 }
152                                 if(!(tptr = dynarr_push(tiles_pixels, 0))) {
153                                         goto err;
154                                 }
155
156                                 tiles[idx] = tile;
157                                 tiles[idx].scansz = tiles[idx].pitch = tile.scansz;
158
159                                 /* did the array get relocated? */
160                                 if(tptr != tiles_pixels) {
161                                         tiles_pixels = tptr;
162                                         /* make each tile's pixels pointer point to the right place in the large pixelbuffer */
163                                         for(k=0; k<idx+1; k++) {
164                                                 tiles[k].pixels = tptr;
165                                                 tptr += tile_ysz * tiles[idx].pitch;
166                                         }
167                                 } else {
168                                         /* otherwise just set the new one */
169                                         tiles[idx].pixels = tiles_pixels + idx * tile_ysz * tiles[idx].pitch;
170                                 }
171
172                                 blit(&tile, 0, 0, tile_xsz, tile_ysz, tiles + idx, 0, 0);
173                         }
174
175                         *mapptr++ = idx;
176
177                         sptr += tile.scansz;
178                 }
179                 sptr += (tile_ysz - 1) * tile.scansz;
180         }
181
182         ntiles = dynarr_size(tiles);
183         fprintf(stderr, "%s (%dx%d) -> %d tiles, %d unique\n", fname, img.width, img.height,
184                         xtiles * ytiles, ntiles);
185
186         assert(ntiles > 0);
187
188         /* make a big image out of the tiles and write it out */
189         tile = tiles[0];
190         tile.height = ntiles * tile_ysz;
191
192         outfile = alloca(strlen(basename) + 5);
193
194         switch(outmode) {
195         case OUT_IMG:
196                 sprintf(outfile, "%s.png", basename);
197
198                 if(save_image(&tile, outfile) == -1) {
199                         fprintf(stderr, "failed to write output image\n");
200                         goto err;
201                 }
202                 break;
203
204         case OUT_C:
205                 sprintf(outfile, "%s.c", basename);
206
207                 if(!(fp = fopen(outfile, "w"))) {
208                         fprintf(stderr, "failed to open output file: %s: %s\n", outfile, strerror(errno));
209                         goto err;
210                 }
211                 wrtiles_carr(fp, basename, &tile, ntiles);
212                 wrtilemap_carr(fp, basename, tilemap, xtiles, ytiles);
213                 wrpalette_carr(fp, basename, tile.cmap, tile.cmap_ncolors);
214                 break;
215
216         case OUT_ASM:
217                 sprintf(outfile, "%s.s", basename);
218                 /* TODO */
219                 break;
220         }
221
222         result = 0;
223
224 err:
225         dynarr_free(tiles_pixels);
226         dynarr_free(tiles);
227         free(tilemap);
228         free(img.pixels);
229         return result;
230 }
231
232 void wrtiles_carr(FILE *fp, const char *name, struct image *timg, int ntiles)
233 {
234         int i, j, curx = 0;
235         unsigned char *ptr = timg->pixels;
236
237         fprintf(fp, "\nint %s_num_tiles = %d;\n", name, ntiles);
238         fprintf(fp, "int %s_tiles_width = %d;\n", name, timg->width);
239         fprintf(fp, "int %s_tiles_height = %d;\n", name, timg->height);
240         fprintf(fp, "int %s_tiles_bpp = %d;\n", name, timg->bpp);
241         fprintf(fp, "unsigned char %s_tiles[] = {\n", name);
242
243         for(i=0; i<timg->height; i++) {
244                 for(j=0; j<timg->scansz; j++) {
245                         if(curx == 0) {
246                                 curx = 3 + fprintf(fp, "\t%u", (unsigned int)*ptr++);
247                         } else {
248                                 curx += fprintf(fp, ", %u", (unsigned int)*ptr++);
249                         }
250                         if(curx >= 80) {
251                                 fprintf(fp, ",\n");
252                                 curx = 0;
253                         }
254                 }
255                 ptr += timg->pitch - timg->scansz;
256         }
257
258         fprintf(fp, "\n};\n\n");
259 }
260
261 void wrtilemap_carr(FILE *fp, const char *name, int *tmap, int xtiles, int ytiles)
262 {
263         int i, sz, curx = 0;
264
265         fprintf(fp, "\nint %s_tilemap_cols = %d;\n", name, xtiles);
266         fprintf(fp, "int %s_tilemap_rows = %d;\n", name, ytiles);
267         fprintf(fp, "unsigned int %s_tilemap[] = {\n", name);
268
269         sz = xtiles * ytiles;
270         for(i=0; i<sz; i++) {
271                 if(curx == 0) {
272                         curx = 3 + fprintf(fp, "\t%u", (unsigned int)tmap[i]);
273                 } else {
274                         curx += fprintf(fp, ", %u", (unsigned int)tmap[i]);
275                 }
276                 if(curx >= 80) {
277                         fprintf(fp, ",\n");
278                         curx = 0;
279                 }
280         }
281
282         fprintf(fp, "\n};\n\n");
283 }
284
285 void wrpalette_carr(FILE *fp, const char *name, struct cmapent *cmap, int ncol)
286 {
287         int i;
288
289         fprintf(fp, "\nint %s_cmap_colors = %d;\n", name, ncol);
290         fprintf(fp, "unsigned char %s_cmap[][3] = {\n", name);
291         for(i=0; i<ncol; i++) {
292                 fprintf(fp, "\t{%u, %u, %u}", cmap[i].r, cmap[i].g, cmap[i].b);
293                 if(i < ncol - 1) {
294                         fputs(",\n", fp);
295                 } else {
296                         fputc('\n', fp);
297                 }
298         }
299         fprintf(fp, "};\n\n");
300 }