pngdump: image quantization, first attempt at shademap generation
[gbajam21] / tools / pngdump / main.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <errno.h>
5 #include <assert.h>
6 #include "image.h"
7
8 enum {
9         MODE_PIXELS,
10         MODE_CMAP,
11         MODE_INFO,
12         MODE_SHADE_CMAP,
13         MODE_SHADE_LUT
14 };
15
16 void print_usage(const char *argv0);
17
18 int main(int argc, char **argv)
19 {
20         int i, mode = 0;
21         int text = 0;
22         int renibble = 0;
23         char *outfname = 0;
24         char *infiles[256];
25         int num_infiles = 0;
26         struct image img, tmpimg;
27         FILE *out = stdout;
28         struct cmapent shade_cmap[256] = {0};
29         int *shade_lut = 0;
30         int shade_levels = 8;
31
32         for(i=1; i<argc; i++) {
33                 if(argv[i][0] == '-') {
34                         if(argv[i][2] == 0) {
35                                 switch(argv[i][1]) {
36                                 case 'p':
37                                         mode = MODE_PIXELS;
38                                         break;
39
40                                 case 'c':
41                                         mode = MODE_CMAP;
42                                         break;
43
44                                 case 'i':
45                                         mode = MODE_INFO;
46                                         break;
47
48                                 case 'C':
49                                         mode = MODE_SHADE_CMAP;
50                                         break;
51
52                                 case 'S':
53                                         mode = MODE_SHADE_LUT;
54                                         break;
55
56                                 case 's':
57                                         if(!argv[++i] || (shade_levels = atoi(argv[i])) == 0) {
58                                                 fprintf(stderr, "-s must be followed by the number of shade levels\n");
59                                                 return 1;
60                                         }
61                                         break;
62
63                                 case 't':
64                                         text = 1;
65                                         break;
66
67                                 case 'n':
68                                         renibble = 1;
69                                         break;
70
71                                 case 'o':
72                                         if(!argv[++i]) {
73                                                 fprintf(stderr, "%s must be followed by a filename\n", argv[i - 1]);
74                                                 return 1;
75                                         }
76                                         outfname = argv[i];
77                                         break;
78
79                                 case 'h':
80                                         print_usage(argv[0]);
81                                         return 0;
82
83                                 default:
84                                         fprintf(stderr, "invalid option: %s\n", argv[i]);
85                                         print_usage(argv[0]);
86                                         return 1;
87                                 }
88                         } else {
89                                 fprintf(stderr, "invalid option: %s\n", argv[i]);
90                                 print_usage(argv[0]);
91                                 return 1;
92                         }
93                 } else {
94                         infiles[num_infiles++] = argv[i];
95                 }
96         }
97
98         if(!num_infiles) {
99                 fprintf(stderr, "pass the filename of a PNG file\n");
100                 return 1;
101         }
102         if(load_image(&img, infiles[0]) == -1) {
103                 fprintf(stderr, "failed to load PNG file: %s\n", infiles[0]);
104                 return 1;
105         }
106
107         for(i=1; i<num_infiles; i++) {
108                 if(load_image(&tmpimg, infiles[i]) == -1) {
109                         fprintf(stderr, "failed to load PNG file: %s\n", infiles[i]);
110                         return 1;
111                 }
112                 if(tmpimg.width != img.width || tmpimg.height != img.height) {
113                         fprintf(stderr, "size mismatch: first image (%s) is %dx%d, %s is %dx%d\n",
114                                         infiles[0], img.width, img.height, infiles[i], tmpimg.width, tmpimg.height);
115                         return 1;
116                 }
117                 if(tmpimg.bpp != img.bpp) {
118                         fprintf(stderr, "bpp mismatch: first image (%s) is %d bpp, %s is %d bpp\n",
119                                         infiles[0], img.bpp, infiles[i], img.bpp);
120                         return 1;
121                 }
122
123                 overlay_key(&tmpimg, 0, &img);
124         }
125
126         if(img.bpp == 4 && renibble) {
127                 unsigned char *ptr = img.pixels;
128                 for(i=0; i<img.width * img.height; i++) {
129                         unsigned char p = *ptr;
130                         *ptr++ = (p << 4) | (p >> 4);
131                 }
132         }
133
134         if(outfname) {
135                 if(!(out = fopen(outfname, "wb"))) {
136                         fprintf(stderr, "failed to open output file: %s: %s\n", outfname, strerror(errno));
137                         return 1;
138                 }
139         }
140
141         switch(mode) {
142         case MODE_PIXELS:
143                 fwrite(img.pixels, 1, img.scansz * img.height, out);
144                 break;
145
146         case MODE_CMAP:
147                 if(text) {
148                         for(i=0; i<img.cmap_ncolors; i++) {
149                                 printf("%d %d %d\n", img.cmap[i].r, img.cmap[i].g, img.cmap[i].b);
150                         }
151                 } else {
152                         /*fwrite(img.cmap, sizeof img.cmap[0], img.cmap_ncolors, out);*/
153                         fwrite(img.cmap, sizeof img.cmap[0], 1 << img.bpp, out);
154                 }
155                 break;
156
157         case MODE_INFO:
158                 printf("size: %dx%d\n", img.width, img.height);
159                 printf("bit depth: %d\n", img.bpp);
160                 printf("scanline size: %d bytes\n", img.scansz);
161                 if(img.cmap_ncolors > 0) {
162                         printf("colormap entries: %d\n", img.cmap_ncolors);
163                 } else {
164                         printf("color channels: %d\n", img.nchan);
165                 }
166                 break;
167
168         case MODE_SHADE_LUT:
169                 if(!(shade_lut = malloc(256 * shade_levels * sizeof *shade_lut))) {
170                         fprintf(stderr, "failed to allocate shading look-up table\n");
171                         return 1;
172                 }
173         case MODE_SHADE_CMAP:
174                 if(!img.cmap_ncolors) {
175                         fprintf(stderr, "can't generate shade levels for non-indexed images\n");
176                         return 1;
177                 }
178                 if(gen_shade_lut(&img, shade_levels, 256, shade_cmap, shade_lut) == -1) {
179                         return 1;
180                 }
181                 if(mode == MODE_SHADE_CMAP) {
182                         fwrite(shade_cmap, sizeof shade_cmap, 1, out);
183                 } else {
184                         for(i=0; i<img.cmap_ncolors * shade_levels; i++) {
185                                 fputc(shade_lut[i], out);
186                         }
187                 }
188                 break;
189         }
190
191         fclose(out);
192         return 0;
193 }
194
195 void print_usage(const char *argv0)
196 {
197         printf("Usage: %s [options] <input file>\n", argv0);
198         printf("Options:\n");
199         printf(" -o <output file>: specify output file (default: stdout)\n");
200         printf(" -p: dump pixels (default)\n");
201         printf(" -c: dump colormap (palette) entries\n");
202         printf(" -C: generate shading colormap\n");
203         printf(" -S: generate shading LUT\n");
204         printf(" -s <shade levels>: used in conjunction with -C or -S (default: 8)\n");
205         printf(" -i: print image information\n");
206         printf(" -t: dump as text\n");
207         printf(" -n: swap the order of nibbles (for 4bpp)\n");
208         printf(" -h: print usage and exit\n");
209 }