a2845ac52db3ae71858d14a39495b928f5e53d39
[img2tiles] / src / main.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include "image.h"
5 #include "dynarr.h"
6
7 void print_usage(const char *argv0);
8 int proc_image(const char *fname);
9
10 enum { OUT_IMG, OUT_C, OUT_ASM } outmode;
11 int tile_xsz = 8, tile_ysz = 8;
12
13 int main(int argc, char **argv)
14 {
15         int i;
16
17         for(i=1; i<argc; i++) {
18                 if(argv[i][0] == '-') {
19                         if(argv[i][2] == 0) {
20                                 switch(argv[i][1]) {
21                                 case 't':
22                                         if(sscanf(argv[++i], "%dx%d", &tile_xsz, &tile_ysz) != 2) {
23                                                 fprintf(stderr, "-t must be followed by the tile size (WxH)\n");
24                                                 return 1;
25                                         }
26                                         break;
27
28                                 case 'c':
29                                         outmode = OUT_C;
30                                         break;
31
32                                 case 's':
33                                         outmode = OUT_ASM;
34                                         break;
35
36                                 case 'h':
37                                         print_usage(argv[0]);
38                                         return 0;
39
40                                 default:
41                                         fprintf(stderr, "invalid option: %s\n", argv[i]);
42                                         print_usage(argv[0]);
43                                         return 1;
44                                 }
45                         } else {
46                                 fprintf(stderr, "invalid option: %s\n", argv[i]);
47                                 print_usage(argv[0]);
48                                 return 1;
49                         }
50
51                 } else {
52                         if(proc_image(argv[i]) == -1) {
53                                 return 1;
54                         }
55                 }
56         }
57
58         return 0;
59 }
60
61 void print_usage(const char *argv0)
62 {
63         printf("Usage: %s [options] <img1> [<img2> ... <imgN>]\n", argv0);
64         printf("Options:\n");
65         printf(" -t WxH: tile size (default 8x8)\n");
66         printf(" -c: output C array\n");
67         printf(" -s: output GNU assembler data\n");
68         printf(" -h: print usage and exit\n\n");
69 }
70
71 int find_tile(struct image *tile, struct image *tiles)
72 {
73         int i, count;
74         count = dynarr_size(tiles);
75         for(i=0; i<count; i++) {
76                 if(cmp_image(tile, tiles + i) == 0) {
77                         return i;
78                 }
79         }
80         return -1;
81 }
82
83 int proc_image(const char *fname)
84 {
85         int i, j, k, idx, xtiles, ytiles, ntiles, result = -1;
86         struct image img, tile;
87         struct image *tiles = 0;
88         unsigned char *tiles_pixels, *tptr;
89         unsigned char *sptr;
90         int *tilemap = 0, *mapptr;
91
92         if(load_image(&img, fname) == -1) {
93                 fprintf(stderr, "failed to load image: %s\n", fname);
94                 return -1;
95         }
96
97         xtiles = img.width / tile_xsz;
98         ytiles = img.height / tile_ysz;
99         if(img.width % tile_xsz != 0 || img.height % tile_ysz != 0) {
100                 fprintf(stderr, "image size (%dx%d) not evenly divisible into %dx%d tiles\n",
101                                 img.width, img.height, tile_xsz, tile_ysz);
102                 goto err;
103         }
104
105         if(!(tilemap = malloc(xtiles * ytiles * sizeof *tilemap))) {
106                 fprintf(stderr, "failed to allocate %dx%d tilemap\n", xtiles, ytiles);
107                 goto err;
108         }
109         mapptr = tilemap;
110
111         if(!(tiles = dynarr_alloc(0, sizeof *tiles))) {
112                 fprintf(stderr, "failed to allocate tile array\n");
113                 goto err;
114         }
115         /* alloc a contiguous buffer for the full tileset pixels, to make it easier to write it
116          * out as a single image in the end
117          */
118         if(!(tiles_pixels = dynarr_alloc(0, tile_xsz * tile_ysz * img.bpp / 8))) {
119                 fprintf(stderr, "failed to allocate tile pixel buffer\n");
120                 goto err;
121         }
122
123         tile = img;
124         tile.width = tile_xsz;
125         tile.height = tile_ysz;
126         tile.scansz = tile_xsz * tile.bpp / 8;
127
128         sptr = img.pixels;
129         for(i=0; i<ytiles; i++) {
130                 for(j=0; j<xtiles; j++) {
131                         tile.pixels = sptr;
132
133                         if((idx = find_tile(&tile, tiles)) == -1) {
134                                 /* we don't have a duplicate of this tile */
135                                 idx = dynarr_size(tiles);
136
137                                 if(!(tiles = dynarr_push(tiles, 0))) {
138                                         goto err;
139                                 }
140                                 if(!(tptr = dynarr_push(tiles_pixels, 0))) {
141                                         goto err;
142                                 }
143
144                                 tiles[idx] = tile;
145                                 tiles[idx].scansz = tiles[idx].pitch = tile.scansz;
146
147                                 /* did the array get relocated? */
148                                 if(tptr != tiles_pixels) {
149                                         tiles_pixels = tptr;
150                                         /* make each tile's pixels pointer point to the right place in the large pixelbuffer */
151                                         for(k=0; k<idx+1; k++) {
152                                                 tiles[k].pixels = tptr;
153                                                 tptr += tile_ysz * tiles[idx].pitch;
154                                         }
155                                 } else {
156                                         /* otherwise just set the new one */
157                                         tiles[idx].pixels = tiles_pixels + idx * tile_ysz * tiles[idx].pitch;
158                                 }
159
160                                 blit(&tile, 0, 0, tile_xsz, tile_ysz, tiles + idx, 0, 0);
161                         }
162
163                         *mapptr++ = idx;
164
165                         sptr += tile.scansz;
166                 }
167                 sptr += (tile_ysz - 1) * tile.scansz;
168         }
169
170         ntiles = dynarr_size(tiles);
171         fprintf(stderr, "%s (%dx%d) -> %d tiles, %d unique\n", fname, img.width, img.height,
172                         xtiles * ytiles, ntiles);
173
174
175         if(ntiles > 0) {
176                 FILE *fp;
177
178                 /* make a big image out of the tiles and write it out */
179                 tile = tiles[0];
180                 tile.height = ntiles * tile_ysz;
181                 if(save_image(&tile, "test.png") == -1) {
182                         fprintf(stderr, "failed to write output image\n");
183                         goto err;
184                 }
185         }
186         /* TODO cont. */
187
188         result = 0;
189
190 err:
191         dynarr_free(tiles_pixels);
192         dynarr_free(tiles);
193         free(tilemap);
194         free(img.pixels);
195         return result;
196 }