rev |
line source |
nuclear@0
|
1 #include <stdio.h>
|
nuclear@0
|
2 #include <stdlib.h>
|
nuclear@0
|
3 #include <string.h>
|
nuclear@5
|
4 #include <ctype.h>
|
nuclear@0
|
5 #include <math.h>
|
nuclear@0
|
6 #include <float.h>
|
nuclear@0
|
7 #include <errno.h>
|
nuclear@0
|
8 #ifndef WIN32
|
nuclear@0
|
9 #include <alloca.h>
|
nuclear@0
|
10 #else
|
nuclear@0
|
11 #include <malloc.h>
|
nuclear@0
|
12 #endif
|
nuclear@0
|
13 #include <imago2.h>
|
nuclear@0
|
14
|
nuclear@0
|
15 struct palentry {
|
nuclear@0
|
16 float r, g, b;
|
nuclear@0
|
17 struct palentry *next;
|
nuclear@0
|
18 };
|
nuclear@0
|
19
|
nuclear@5
|
20 void print_usage(const char *argv0);
|
nuclear@0
|
21 int load_palette(const char *fname);
|
nuclear@5
|
22 int save_palette(const char *fname);
|
nuclear@0
|
23 int proc_tile(const char *fname);
|
nuclear@0
|
24 float find_nearest(float r, float g, float b);
|
nuclear@0
|
25
|
nuclear@0
|
26 struct palentry *palette;
|
nuclear@0
|
27 int palsize;
|
nuclear@5
|
28 float colscale[10] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
|
nuclear@5
|
29 int scale_count = 0;
|
nuclear@5
|
30 int key = -1;
|
nuclear@0
|
31
|
nuclear@0
|
32 int main(int argc, char **argv)
|
nuclear@0
|
33 {
|
nuclear@0
|
34 int i;
|
nuclear@5
|
35 const char *outpalname = 0;
|
nuclear@0
|
36
|
nuclear@0
|
37 for(i=1; i<argc; i++) {
|
nuclear@0
|
38 if(argv[i][0] == '-') {
|
nuclear@0
|
39 switch(argv[i][1]) {
|
nuclear@0
|
40 case 'p':
|
nuclear@0
|
41 if(load_palette(argv[++i]) == -1) {
|
nuclear@0
|
42 return 1;
|
nuclear@0
|
43 }
|
nuclear@0
|
44 break;
|
nuclear@0
|
45
|
nuclear@5
|
46 case 'P':
|
nuclear@5
|
47 outpalname = argv[++i];
|
nuclear@5
|
48 break;
|
nuclear@5
|
49
|
nuclear@5
|
50 case 'f':
|
nuclear@5
|
51 {
|
nuclear@5
|
52 int idx, maxcol;
|
nuclear@5
|
53 if(sscanf(argv[++i], "%d=%d", &idx, &maxcol) != 2) {
|
nuclear@5
|
54 fprintf(stderr, "-f option expects <Z>=<MAX COLOR> pairs\n");
|
nuclear@5
|
55 return 1;
|
nuclear@5
|
56 }
|
nuclear@5
|
57 if(idx < 0 || idx >= 10) {
|
nuclear@5
|
58 fprintf(stderr, "-f expects a z value from 0 to 9\n");
|
nuclear@5
|
59 return 1;
|
nuclear@5
|
60 }
|
nuclear@5
|
61 colscale[idx] = (float)maxcol / 255.0;
|
nuclear@5
|
62 if(idx >= scale_count) scale_count = idx + 1;
|
nuclear@5
|
63 }
|
nuclear@5
|
64 break;
|
nuclear@5
|
65
|
nuclear@5
|
66 case 'k':
|
nuclear@5
|
67 {
|
nuclear@5
|
68 char *endp;
|
nuclear@5
|
69 long x = strtol(argv[++i], &endp, 10);
|
nuclear@5
|
70 if(endp == argv[i]) {
|
nuclear@5
|
71 fprintf(stderr, "-k must be followed by a palette index\n");
|
nuclear@5
|
72 return 1;
|
nuclear@5
|
73 }
|
nuclear@5
|
74 key = x;
|
nuclear@5
|
75 }
|
nuclear@5
|
76 break;
|
nuclear@5
|
77
|
nuclear@5
|
78 case 'h':
|
nuclear@5
|
79 print_usage(argv[0]);
|
nuclear@5
|
80 return 0;
|
nuclear@5
|
81
|
nuclear@0
|
82 default:
|
nuclear@0
|
83 fprintf(stderr, "invalid option: %s\n", argv[i]);
|
nuclear@5
|
84 print_usage(argv[0]);
|
nuclear@0
|
85 return 1;
|
nuclear@0
|
86 }
|
nuclear@0
|
87 } else {
|
nuclear@0
|
88 if(proc_tile(argv[i]) == -1) {
|
nuclear@0
|
89 return 1;
|
nuclear@0
|
90 }
|
nuclear@0
|
91 }
|
nuclear@0
|
92 }
|
nuclear@5
|
93
|
nuclear@5
|
94 if(outpalname) {
|
nuclear@5
|
95 if(save_palette(outpalname) == -1) {
|
nuclear@5
|
96 fprintf(stderr, "failed to write extended palette: %s\n", outpalname);
|
nuclear@5
|
97 return 1;
|
nuclear@5
|
98 }
|
nuclear@5
|
99 }
|
nuclear@5
|
100
|
nuclear@0
|
101 return 0;
|
nuclear@0
|
102 }
|
nuclear@0
|
103
|
nuclear@5
|
104 void print_usage(const char *argv0)
|
nuclear@5
|
105 {
|
nuclear@5
|
106 printf("Usage: %s <options> [img1 img2 img3 ... imgN]\n", argv0);
|
nuclear@5
|
107 printf("Options:\n");
|
nuclear@5
|
108 printf(" -p <file> use palette from file\n");
|
nuclear@5
|
109 printf(" -f <z>=<b> darken the image (max brightness 'b') based on the digit (z) in its filename\n");
|
nuclear@5
|
110 printf(" -k <idx> colorkey index (preserve this color regardless of depth cues)\n");
|
nuclear@5
|
111 printf(" -h print usage and exit\n");
|
nuclear@5
|
112 }
|
nuclear@5
|
113
|
nuclear@0
|
114 int load_palette(const char *fname)
|
nuclear@0
|
115 {
|
nuclear@0
|
116 FILE *fp;
|
nuclear@0
|
117 char buf[128];
|
nuclear@0
|
118 struct palentry *newpal;
|
nuclear@0
|
119 struct palentry *head = 0, *tail;
|
nuclear@0
|
120 int nent = 0;
|
nuclear@0
|
121
|
nuclear@0
|
122 if(!(fp = fopen(fname, "r"))) {
|
nuclear@0
|
123 fprintf(stderr, "failed to open palette file: %s: %s\n", fname, strerror(errno));
|
nuclear@0
|
124 return -1;
|
nuclear@0
|
125 }
|
nuclear@0
|
126
|
nuclear@0
|
127 while(fgets(buf, sizeof buf, fp)) {
|
nuclear@0
|
128 struct palentry *pent;
|
nuclear@0
|
129 char *endp, *line = buf;
|
nuclear@0
|
130 int r, g, b;
|
nuclear@0
|
131
|
nuclear@0
|
132 if(!line || !*line) continue;
|
nuclear@0
|
133
|
nuclear@0
|
134 if(*line == '#') { /* hex html-like values */
|
nuclear@0
|
135 unsigned int val = strtol(line + 1, &endp, 16);
|
nuclear@0
|
136 if(endp == line) {
|
nuclear@0
|
137 fprintf(stderr, "unrecognized line \"%s\" in palette file: %s\n", line, fname);
|
nuclear@0
|
138 goto fail;
|
nuclear@0
|
139 }
|
nuclear@0
|
140
|
nuclear@0
|
141 r = (val >> 16) & 0xff;
|
nuclear@0
|
142 g = (val >> 8) & 0xff;
|
nuclear@0
|
143 b = val & 0xff;
|
nuclear@0
|
144 } else {
|
nuclear@0
|
145 fprintf(stderr, "unrecognized line \"%s\" in palette file: %s\n", line, fname);
|
nuclear@0
|
146 goto fail;
|
nuclear@0
|
147 }
|
nuclear@0
|
148
|
nuclear@0
|
149 if(!(pent = malloc(sizeof *pent))) {
|
nuclear@0
|
150 perror("failed to allocate palette entry");
|
nuclear@0
|
151 goto fail;
|
nuclear@0
|
152 }
|
nuclear@0
|
153 pent->r = (float)r / 255.0;
|
nuclear@0
|
154 pent->g = (float)g / 255.0;
|
nuclear@0
|
155 pent->b = (float)b / 255.0;
|
nuclear@0
|
156 pent->next = 0;
|
nuclear@0
|
157
|
nuclear@0
|
158 if(head) {
|
nuclear@0
|
159 tail->next = pent;
|
nuclear@0
|
160 tail = pent;
|
nuclear@0
|
161 } else {
|
nuclear@0
|
162 head = tail = pent;
|
nuclear@0
|
163 }
|
nuclear@0
|
164 nent++;
|
nuclear@0
|
165 }
|
nuclear@0
|
166
|
nuclear@0
|
167 /* flatten */
|
nuclear@0
|
168 if(!(newpal = malloc(nent * sizeof *newpal))) {
|
nuclear@0
|
169 fprintf(stderr, "failed to allocate %d palette entries: %s\n", nent, strerror(errno));
|
nuclear@0
|
170 goto fail;
|
nuclear@0
|
171 }
|
nuclear@0
|
172 palette = newpal;
|
nuclear@0
|
173 palsize = nent;
|
nuclear@0
|
174
|
nuclear@0
|
175 while(head) {
|
nuclear@0
|
176 struct palentry *ent = head;
|
nuclear@0
|
177 head = head->next;
|
nuclear@0
|
178
|
nuclear@0
|
179 *newpal++ = *ent;
|
nuclear@0
|
180 free(ent);
|
nuclear@0
|
181 }
|
nuclear@0
|
182
|
nuclear@0
|
183 printf("loaded palette: %s (%d colors)\n", fname, nent);
|
nuclear@0
|
184
|
nuclear@0
|
185 fclose(fp);
|
nuclear@0
|
186 return 0;
|
nuclear@0
|
187
|
nuclear@0
|
188 fail:
|
nuclear@0
|
189 fclose(fp);
|
nuclear@0
|
190 while(head) {
|
nuclear@0
|
191 void *tmp = head;
|
nuclear@0
|
192 head = head->next;
|
nuclear@0
|
193 free(tmp);
|
nuclear@0
|
194 }
|
nuclear@0
|
195 return -1;
|
nuclear@0
|
196 }
|
nuclear@0
|
197
|
nuclear@5
|
198 int save_palette(const char *fname)
|
nuclear@5
|
199 {
|
nuclear@5
|
200 FILE *fp;
|
nuclear@5
|
201 int i, j;
|
nuclear@5
|
202 int extsize = palsize * scale_count;
|
nuclear@5
|
203
|
nuclear@5
|
204 printf("saving extended palette: %s\n", fname);
|
nuclear@5
|
205
|
nuclear@5
|
206 if(!(fp = fopen(fname, "w"))) {
|
nuclear@5
|
207 fprintf(stderr, "failed to save extended palette: %s: %s\n", fname, strerror(errno));
|
nuclear@5
|
208 return -1;
|
nuclear@5
|
209 }
|
nuclear@5
|
210
|
nuclear@5
|
211 if(extsize > 256) {
|
nuclear@5
|
212 fprintf(stderr, "warning: extended palette size too large (%d)\n", extsize);
|
nuclear@5
|
213 }
|
nuclear@5
|
214
|
nuclear@5
|
215 for(i=0; i<scale_count; i++) {
|
nuclear@5
|
216 for(j=0; j<palsize; j++) {
|
nuclear@5
|
217 int r = (int)(palette[j].r * colscale[i] * 255.0);
|
nuclear@5
|
218 int g = (int)(palette[j].g * colscale[i] * 255.0);
|
nuclear@5
|
219 int b = (int)(palette[j].b * colscale[i] * 255.0);
|
nuclear@5
|
220
|
nuclear@5
|
221 if(r > 255) r = 255;
|
nuclear@5
|
222 if(g > 255) g = 255;
|
nuclear@5
|
223 if(b > 255) b = 255;
|
nuclear@5
|
224
|
nuclear@5
|
225 fprintf(fp, "#%02x%02x%02x\n", r, g, b);
|
nuclear@5
|
226 }
|
nuclear@5
|
227 }
|
nuclear@5
|
228 fclose(fp);
|
nuclear@5
|
229 return 0;
|
nuclear@5
|
230 }
|
nuclear@5
|
231
|
nuclear@0
|
232 int proc_tile(const char *fname)
|
nuclear@0
|
233 {
|
nuclear@0
|
234 int i;
|
nuclear@0
|
235 int xsz, ysz;
|
nuclear@0
|
236 unsigned char *pixels, *pptr;
|
nuclear@0
|
237 char *outfname, *cptr;
|
nuclear@0
|
238 FILE *fp;
|
nuclear@0
|
239 const char *magic = "TILEIMAG";
|
nuclear@5
|
240 int zidx = -1;
|
nuclear@0
|
241
|
nuclear@0
|
242 outfname = alloca(strlen(fname) + 4);
|
nuclear@0
|
243 strcpy(outfname, fname);
|
nuclear@0
|
244 if((cptr = strrchr(outfname, '.'))) {
|
nuclear@0
|
245 *cptr = 0;
|
nuclear@0
|
246 }
|
nuclear@0
|
247 strcat(outfname, ".til");
|
nuclear@0
|
248
|
nuclear@5
|
249 cptr = outfname + strlen(outfname) - 1;
|
nuclear@5
|
250 while(cptr >= outfname && !isdigit(*cptr)) --cptr;
|
nuclear@5
|
251 if(cptr >= outfname) {
|
nuclear@5
|
252 zidx = *cptr - '0';
|
nuclear@5
|
253 }
|
nuclear@5
|
254
|
nuclear@0
|
255 printf("processing tile: %s -> %s\n", fname, outfname);
|
nuclear@0
|
256
|
nuclear@0
|
257 if(!(pixels = img_load_pixels(fname, &xsz, &ysz, IMG_FMT_RGB24))) {
|
nuclear@0
|
258 fprintf(stderr, "failed to load source tile image: %s\n", fname);
|
nuclear@0
|
259 return -1;
|
nuclear@0
|
260 }
|
nuclear@0
|
261 if(!(fp = fopen(outfname, "wb"))) {
|
nuclear@0
|
262 fprintf(stderr, "failed to open output tile image for writing: %s: %s\n", outfname, strerror(errno));
|
nuclear@0
|
263 img_free_pixels(pixels);
|
nuclear@0
|
264 return -1;
|
nuclear@0
|
265 }
|
nuclear@0
|
266 /* write header */
|
nuclear@0
|
267 fwrite(magic, 1, 8, fp);
|
nuclear@0
|
268 fwrite(&xsz, sizeof xsz, 1, fp);
|
nuclear@0
|
269 fwrite(&ysz, sizeof ysz, 1, fp);
|
nuclear@0
|
270
|
nuclear@0
|
271 pptr = pixels;
|
nuclear@0
|
272 for(i=0; i<xsz * ysz; i++) {
|
nuclear@0
|
273 int idx = find_nearest(pptr[0] / 255.0, pptr[1] / 255.0, pptr[2] / 255.0);
|
nuclear@5
|
274
|
nuclear@5
|
275 if(zidx > 0 && idx != key) {
|
nuclear@5
|
276 /* shift the indices to the appropriate copy of the palette */
|
nuclear@5
|
277 idx += zidx * palsize;
|
nuclear@5
|
278 }
|
nuclear@5
|
279
|
nuclear@0
|
280 fputc(idx, fp);
|
nuclear@0
|
281 pptr += 3;
|
nuclear@0
|
282 }
|
nuclear@0
|
283 img_free_pixels(pixels);
|
nuclear@0
|
284 fclose(fp);
|
nuclear@0
|
285 return 0;
|
nuclear@0
|
286 }
|
nuclear@0
|
287
|
nuclear@0
|
288 float find_nearest(float r, float g, float b)
|
nuclear@0
|
289 {
|
nuclear@0
|
290 int i, res;
|
nuclear@0
|
291 float mindist = FLT_MAX;
|
nuclear@0
|
292
|
nuclear@0
|
293 for(i=0; i<palsize; i++) {
|
nuclear@0
|
294 float dr = r - palette[i].r;
|
nuclear@0
|
295 float dg = g - palette[i].g;
|
nuclear@0
|
296 float db = b - palette[i].b;
|
nuclear@0
|
297 float dist = dr * dr + dg * dg + db * db;
|
nuclear@0
|
298
|
nuclear@0
|
299 if(dist < mindist) {
|
nuclear@0
|
300 mindist = dist;
|
nuclear@0
|
301 res = i;
|
nuclear@0
|
302 }
|
nuclear@0
|
303 }
|
nuclear@0
|
304 return res;
|
nuclear@0
|
305 }
|