16 void conv_gba_image(struct image *img);
17 void dump_colormap(struct image *img, int text, FILE *fp);
18 void print_usage(const char *argv0);
20 int main(int argc, char **argv)
26 char *slut_fname = 0, *cmap_fname = 0;
29 struct image img, tmpimg;
40 for(i=1; i<argc; i++) {
41 if(argv[i][0] == '-') {
61 if(!argv[++i] || (maxcol = atoi(argv[i])) < 2 || maxcol > 256) {
62 fprintf(stderr, "-C must be followed by the number of colors to reduce down to\n");
68 if(!argv[++i] || (shade_levels = atoi(argv[i])) == 0) {
69 fprintf(stderr, "-s must be followed by the number of shade levels\n");
88 fprintf(stderr, "%s must be followed by a filename\n", argv[i - 1]);
99 fprintf(stderr, "invalid option: %s\n", argv[i]);
100 print_usage(argv[0]);
104 if(strcmp(argv[i], "-oc") == 0) {
106 fprintf(stderr, "-oc must be followed by a filename\n");
109 cmap_fname = argv[i];
111 } else if(strcmp(argv[i], "-os") == 0) {
113 fprintf(stderr, "-os must be followed by a filename\n");
116 slut_fname = argv[i];
118 } else if(strcmp(argv[i], "-555") == 0) {
122 fprintf(stderr, "invalid option: %s\n", argv[i]);
123 print_usage(argv[0]);
128 infiles[num_infiles++] = argv[i];
133 fprintf(stderr, "pass the filename of a PNG file\n");
136 if(load_image(&img, infiles[0]) == -1) {
137 fprintf(stderr, "failed to load PNG file: %s\n", infiles[0]);
142 conv_gba_image(&img);
145 for(i=1; i<num_infiles; i++) {
146 if(load_image(&tmpimg, infiles[i]) == -1) {
147 fprintf(stderr, "failed to load PNG file: %s\n", infiles[i]);
150 if(tmpimg.width != img.width || tmpimg.height != img.height) {
151 fprintf(stderr, "size mismatch: first image (%s) is %dx%d, %s is %dx%d\n",
152 infiles[0], img.width, img.height, infiles[i], tmpimg.width, tmpimg.height);
155 if(tmpimg.bpp != img.bpp) {
156 fprintf(stderr, "bpp mismatch: first image (%s) is %d bpp, %s is %d bpp\n",
157 infiles[0], img.bpp, infiles[i], img.bpp);
161 overlay_key(&tmpimg, 0, &img);
164 /* generate shading LUT and quantize image as necessary */
167 fprintf(stderr, "shading LUT generation is only supported for indexed color images\n");
170 if(!(aux_out = fopen(slut_fname, "wb"))) {
171 fprintf(stderr, "failed to open shading LUT output file: %s: %s\n", slut_fname, strerror(errno));
175 if(!maxcol) maxcol = 256;
177 if(!(shade_lut = malloc(maxcol * shade_levels * sizeof *shade_lut))) {
178 fprintf(stderr, "failed to allocate shading look-up table\n");
182 gen_shades(&img, shade_levels, maxcol, shade_lut);
185 for(i=0; i<maxcol; i++) {
186 for(j=0; j<shade_levels; j++) {
187 lvl = lutptr[shade_levels - j - 1];
189 fprintf(aux_out, "%d%c", lvl, j < shade_levels - 1 ? ' ' : '\n');
194 lutptr += shade_levels;
199 /* perform any color reductions if requested */
200 if(img.bpp <= 8 && img.cmap_ncolors <= maxcol) {
201 fprintf(stderr, "requested reduction to %d colors, but image has %d colors\n", maxcol, img.cmap_ncolors);
204 quantize_image(&img, maxcol);
209 fprintf(stderr, "colormap output works only for indexed color images\n");
212 if(!(aux_out = fopen(cmap_fname, "wb"))) {
213 fprintf(stderr, "failed to open colormap output file: %s: %s\n", cmap_fname, strerror(errno));
216 dump_colormap(&img, text, aux_out);
220 if(img.bpp == 4 && renibble) {
221 unsigned char *ptr = img.pixels;
222 for(i=0; i<img.width * img.height; i++) {
223 unsigned char p = *ptr;
224 *ptr++ = (p << 4) | (p >> 4);
230 unsigned int rgb24[3], rgb15;
232 if(alloc_image(&img555, img.width, img.height, 15) == -1) {
233 fprintf(stderr, "failed to allocate temporary %dx%d image for 555 conversion\n",
234 img.width, img.height);
238 for(i=0; i<img.height; i++) {
239 for(j=0; j<img.width; j++) {
240 get_pixel_rgb(&img, j, i, rgb24);
241 rgb15 = ((rgb24[0] >> 3) & 0x1f) | ((rgb24[1] << 2) & 0x3e0) |
242 ((rgb24[2] << 7) & 0x7c00);
243 put_pixel(&img555, j, i, rgb15);
251 if(!(out = fopen(outfname, "wb"))) {
252 fprintf(stderr, "failed to open output file: %s: %s\n", outfname, strerror(errno));
259 save_image_file(&img, out);
263 fwrite(img.pixels, 1, img.scansz * img.height, out);
267 dump_colormap(&img, text, out);
271 printf("size: %dx%d\n", img.width, img.height);
272 printf("bit depth: %d\n", img.bpp);
273 printf("scanline size: %d bytes\n", img.scansz);
274 if(img.cmap_ncolors > 0) {
275 printf("colormap entries: %d\n", img.cmap_ncolors);
277 printf("color channels: %d\n", img.nchan);
286 #define MIN(a, b) ((a) < (b) ? (a) : (b))
287 #define MIN3(a, b, c) ((a) < (b) ? MIN(a, c) : MIN(b, c))
288 #define MAX(a, b) ((a) > (b) ? (a) : (b))
289 #define MAX3(a, b, c) ((a) > (b) ? MAX(a, c) : MAX(b, c))
291 void rgb_to_hsv(float *rgb, float *hsv)
293 float min, max, delta;
295 min = MIN3(rgb[0], rgb[1], rgb[2]);
296 max = MAX3(rgb[0], rgb[1], rgb[2]);
300 hsv[0] = hsv[1] = hsv[2] = 0;
304 hsv[2] = max; /* value */
305 hsv[1] = delta / max; /* saturation */
309 } else if(max == rgb[0]) {
310 hsv[0] = (rgb[1] - rgb[2]) / delta;
311 } else if(max == rgb[1]) {
312 hsv[0] = 2.0f + (rgb[2] - rgb[0]) / delta;
314 hsv[0] = 4.0f + (rgb[0] - rgb[1]) / delta;
319 if(hsv[0] < 0.0f) hsv[0] += 1.0f;
322 if(hsv[0] < 0) hsv[0] += 360;
326 #define RETRGB(r, g, b) \
334 void hsv_to_rgb(float *hsv, float *rgb)
336 float sec, frac, o, p, q;
340 rgb[0] = rgb[1] = rgb[2] = hsv[2]; /* value */
343 sec = floor(hsv[0] * (360.0f / 60.0f));
344 frac = (hsv[0] * (360.0f / 60.0f)) - sec;
346 o = hsv[2] * (1.0f - hsv[1]);
347 p = hsv[2] * (1.0f - hsv[1] * frac);
348 q = hsv[2] * (1.0f - hsv[1] * (1.0f - frac));
353 case 0: RETRGB(hsv[2], q, o);
354 case 1: RETRGB(p, hsv[2], o);
355 case 2: RETRGB(o, hsv[2], q);
356 case 3: RETRGB(o, p, hsv[2]);
357 case 4: RETRGB(q, o, hsv[2]);
358 case 5: RETRGB(hsv[2], o, p);
362 void gba_color(struct cmapent *color)
364 float rgb[3], hsv[3];
366 rgb[0] = pow((float)color->r / 255.0f, 2.2);
367 rgb[1] = pow((float)color->g / 255.0f, 2.2);
368 rgb[2] = pow((float)color->b / 255.0f, 2.2);
370 /* saturate colors */
371 rgb_to_hsv(rgb, hsv);
374 if(hsv[1] > 1.0f) hsv[1] = 1.0f;
375 if(hsv[2] > 1.0f) hsv[2] = 1.0f;
376 hsv_to_rgb(hsv, rgb);
378 rgb[0] = pow(rgb[0], 1.0 / 2.6);
379 rgb[1] = pow(rgb[1], 1.0 / 2.6);
380 rgb[2] = pow(rgb[2], 1.0 / 2.6);
382 color->r = (int)(rgb[0] * 255.0f);
383 color->g = (int)(rgb[1] * 255.0f);
384 color->b = (int)(rgb[2] * 255.0f);
387 void conv_gba_image(struct image *img)
391 if(img->cmap_ncolors) {
392 for(i=0; i<img->cmap_ncolors; i++) {
393 gba_color(img->cmap + i);
400 void dump_colormap(struct image *img, int text, FILE *fp)
405 for(i=0; i<img->cmap_ncolors; i++) {
406 fprintf(fp, "%d %d %d\n", img->cmap[i].r, img->cmap[i].g, img->cmap[i].b);
409 fwrite(img->cmap, sizeof img->cmap[0], 1 << img->bpp, fp);
413 void print_usage(const char *argv0)
415 printf("Usage: %s [options] <input file>\n", argv0);
416 printf("Options:\n");
417 printf(" -o <output file>: specify output file (default: stdout)\n");
418 printf(" -oc <cmap file>: output colormap to separate file\n");
419 printf(" -os <lut file>: generate and output shading LUT\n");
420 printf(" -p: dump pixels (default)\n");
421 printf(" -P: output in PNG format\n");
422 printf(" -c: dump colormap (palette) entries\n");
423 printf(" -C <colors>: reduce image down to specified number of colors\n");
424 printf(" -s <shade levels>: used in conjunction with -os (default: 8)\n");
425 printf(" -i: print image information\n");
426 printf(" -t: output as text when possible\n");
427 printf(" -n: swap the order of nibbles (for 4bpp)\n");
428 printf(" -555: convert to BGR555\n");
429 printf(" -h: print usage and exit\n");