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