17 void conv_gba_image(struct image *img);
18 void dump_colormap(struct image *img, int text, FILE *fp);
19 void print_usage(const char *argv0);
21 int main(int argc, char **argv)
27 char *slut_fname = 0, *cmap_fname = 0, *tmap_fname = 0;
30 struct image img, tmpimg;
40 int tile_width = 0, tile_height = 0;
44 for(i=1; i<argc; i++) {
45 if(argv[i][0] == '-') {
65 if(!argv[++i] || (maxcol = atoi(argv[i])) < 2 || maxcol > 256) {
66 fprintf(stderr, "-C must be followed by the number of colors to reduce down to\n");
72 if(!argv[++i] || (shade_levels = atoi(argv[i])) == 0) {
73 fprintf(stderr, "-s must be followed by the number of shade levels\n");
91 if(!argv[++i] || sscanf(argv[i], "%dx%d", &tile_width, &tile_height) != 2 ||
92 tile_width <= 1 || tile_height <= 1) {
93 fprintf(stderr, "-T must be followed by tile widthxheight 2x2 or higher\n");
104 fprintf(stderr, "%s must be followed by a filename\n", argv[i - 1]);
111 print_usage(argv[0]);
115 fprintf(stderr, "invalid option: %s\n", argv[i]);
116 print_usage(argv[0]);
120 if(strcmp(argv[i], "-oc") == 0) {
122 fprintf(stderr, "-oc must be followed by a filename\n");
125 cmap_fname = argv[i];
127 } else if(strcmp(argv[i], "-os") == 0) {
129 fprintf(stderr, "-os must be followed by a filename\n");
132 slut_fname = argv[i];
134 } else if(strcmp(argv[i], "-om") == 0) {
136 fprintf(stderr, "-om must be followed by a filename\n");
139 tmap_fname = argv[i];
141 } else if(strcmp(argv[i], "-555") == 0) {
145 fprintf(stderr, "invalid option: %s\n", argv[i]);
146 print_usage(argv[0]);
151 infiles[num_infiles++] = argv[i];
156 fprintf(stderr, "pass the filename of a PNG file\n");
159 if(load_image(&img, infiles[0]) == -1) {
160 fprintf(stderr, "failed to load PNG file: %s\n", infiles[0]);
165 conv_gba_image(&img);
168 for(i=1; i<num_infiles; i++) {
169 if(load_image(&tmpimg, infiles[i]) == -1) {
170 fprintf(stderr, "failed to load PNG file: %s\n", infiles[i]);
173 if(tmpimg.width != img.width || tmpimg.height != img.height) {
174 fprintf(stderr, "size mismatch: first image (%s) is %dx%d, %s is %dx%d\n",
175 infiles[0], img.width, img.height, infiles[i], tmpimg.width, tmpimg.height);
178 if(tmpimg.bpp != img.bpp) {
179 fprintf(stderr, "bpp mismatch: first image (%s) is %d bpp, %s is %d bpp\n",
180 infiles[0], img.bpp, infiles[i], img.bpp);
184 overlay_key(&tmpimg, 0, &img);
187 /* generate shading LUT and quantize image as necessary */
190 fprintf(stderr, "shading LUT generation is only supported for indexed color images\n");
193 if(!(aux_out = fopen(slut_fname, "wb"))) {
194 fprintf(stderr, "failed to open shading LUT output file: %s: %s\n", slut_fname, strerror(errno));
198 if(!maxcol) maxcol = 256;
200 if(!(shade_lut = malloc(maxcol * shade_levels * sizeof *shade_lut))) {
201 fprintf(stderr, "failed to allocate shading look-up table\n");
205 gen_shades(&img, shade_levels, maxcol, shade_lut);
208 for(i=0; i<maxcol; i++) {
209 for(j=0; j<shade_levels; j++) {
210 lvl = lutptr[shade_levels - j - 1];
212 fprintf(aux_out, "%d%c", lvl, j < shade_levels - 1 ? ' ' : '\n');
217 lutptr += shade_levels;
222 /* perform any color reductions if requested */
223 if(img.bpp <= 8 && img.cmap_ncolors <= maxcol) {
224 fprintf(stderr, "requested reduction to %d colors, but image has %d colors\n", maxcol, img.cmap_ncolors);
227 quantize_image(&img, maxcol);
232 fprintf(stderr, "colormap output works only for indexed color images\n");
235 if(!(aux_out = fopen(cmap_fname, "wb"))) {
236 fprintf(stderr, "failed to open colormap output file: %s: %s\n", cmap_fname, strerror(errno));
239 dump_colormap(&img, text, aux_out);
243 if(img.bpp == 4 && renibble) {
244 unsigned char *ptr = img.pixels;
245 for(i=0; i<img.width * img.height; i++) {
246 unsigned char p = *ptr;
247 *ptr++ = (p << 4) | (p >> 4);
251 if(img.bpp == 16 && conv_555) {
253 unsigned int rgb24[3], rgb15;
255 if(alloc_image(&img555, img.width, img.height, 15) == -1) {
256 fprintf(stderr, "failed to allocate temporary %dx%d image for 555 conversion\n",
257 img.width, img.height);
261 for(i=0; i<img.height; i++) {
262 for(j=0; j<img.width; j++) {
263 get_pixel_rgb(&img, j, i, rgb24);
264 rgb15 = ((rgb24[0] >> 3) & 0x1f) | ((rgb24[1] << 2) & 0x3e0) |
265 ((rgb24[2] << 7) & 0x7c00);
266 put_pixel(&img555, j, i, rgb15);
274 if(img2tiles(tmap_fname ? &tmap : 0, &img, tile_width, tile_height, tile_dedup) == -1) {
279 dump_tilemap(&tmap, tmap_fname);
284 if(!(out = fopen(outfname, "wb"))) {
285 fprintf(stderr, "failed to open output file: %s: %s\n", outfname, strerror(errno));
292 save_image_file(&img, out);
296 fwrite(img.pixels, 1, img.scansz * img.height, out);
300 dump_colormap(&img, text, out);
304 printf("size: %dx%d\n", img.width, img.height);
305 printf("bit depth: %d\n", img.bpp);
306 printf("scanline size: %d bytes\n", img.scansz);
307 if(img.cmap_ncolors > 0) {
308 printf("colormap entries: %d\n", img.cmap_ncolors);
310 printf("color channels: %d\n", img.nchan);
319 #define MIN(a, b) ((a) < (b) ? (a) : (b))
320 #define MIN3(a, b, c) ((a) < (b) ? MIN(a, c) : MIN(b, c))
321 #define MAX(a, b) ((a) > (b) ? (a) : (b))
322 #define MAX3(a, b, c) ((a) > (b) ? MAX(a, c) : MAX(b, c))
324 void rgb_to_hsv(float *rgb, float *hsv)
326 float min, max, delta;
328 min = MIN3(rgb[0], rgb[1], rgb[2]);
329 max = MAX3(rgb[0], rgb[1], rgb[2]);
333 hsv[0] = hsv[1] = hsv[2] = 0;
337 hsv[2] = max; /* value */
338 hsv[1] = delta / max; /* saturation */
342 } else if(max == rgb[0]) {
343 hsv[0] = (rgb[1] - rgb[2]) / delta;
344 } else if(max == rgb[1]) {
345 hsv[0] = 2.0f + (rgb[2] - rgb[0]) / delta;
347 hsv[0] = 4.0f + (rgb[0] - rgb[1]) / delta;
352 if(hsv[0] < 0.0f) hsv[0] += 1.0f;
355 if(hsv[0] < 0) hsv[0] += 360;
359 #define RETRGB(r, g, b) \
367 void hsv_to_rgb(float *hsv, float *rgb)
369 float sec, frac, o, p, q;
373 rgb[0] = rgb[1] = rgb[2] = hsv[2]; /* value */
376 sec = floor(hsv[0] * (360.0f / 60.0f));
377 frac = (hsv[0] * (360.0f / 60.0f)) - sec;
379 o = hsv[2] * (1.0f - hsv[1]);
380 p = hsv[2] * (1.0f - hsv[1] * frac);
381 q = hsv[2] * (1.0f - hsv[1] * (1.0f - frac));
386 case 0: RETRGB(hsv[2], q, o);
387 case 1: RETRGB(p, hsv[2], o);
388 case 2: RETRGB(o, hsv[2], q);
389 case 3: RETRGB(o, p, hsv[2]);
390 case 4: RETRGB(q, o, hsv[2]);
391 case 5: RETRGB(hsv[2], o, p);
395 void gba_color(struct cmapent *color)
397 float rgb[3], hsv[3];
399 rgb[0] = pow((float)color->r / 255.0f, 2.2);
400 rgb[1] = pow((float)color->g / 255.0f, 2.2);
401 rgb[2] = pow((float)color->b / 255.0f, 2.2);
403 /* saturate colors */
404 rgb_to_hsv(rgb, hsv);
407 if(hsv[1] > 1.0f) hsv[1] = 1.0f;
408 if(hsv[2] > 1.0f) hsv[2] = 1.0f;
409 hsv_to_rgb(hsv, rgb);
411 rgb[0] = pow(rgb[0], 1.0 / 2.6);
412 rgb[1] = pow(rgb[1], 1.0 / 2.6);
413 rgb[2] = pow(rgb[2], 1.0 / 2.6);
415 color->r = (int)(rgb[0] * 255.0f);
416 color->g = (int)(rgb[1] * 255.0f);
417 color->b = (int)(rgb[2] * 255.0f);
420 void conv_gba_image(struct image *img)
424 if(img->cmap_ncolors) {
425 for(i=0; i<img->cmap_ncolors; i++) {
426 gba_color(img->cmap + i);
433 void dump_colormap(struct image *img, int text, FILE *fp)
438 for(i=0; i<img->cmap_ncolors; i++) {
439 fprintf(fp, "%d %d %d\n", img->cmap[i].r, img->cmap[i].g, img->cmap[i].b);
442 fwrite(img->cmap, sizeof img->cmap[0], 1 << img->bpp, fp);
446 void print_usage(const char *argv0)
448 printf("Usage: %s [options] <input file>\n", argv0);
449 printf("Options:\n");
450 printf(" -o <output file>: specify output file (default: stdout)\n");
451 printf(" -oc <cmap file>: output colormap to separate file\n");
452 printf(" -os <lut file>: generate and output shading LUT\n");
453 printf(" -p: dump pixels (default)\n");
454 printf(" -P: output in PNG format\n");
455 printf(" -c: dump colormap (palette) entries\n");
456 printf(" -C <colors>: reduce image down to specified number of colors\n");
457 printf(" -s <shade levels>: used in conjunction with -os (default: 8)\n");
458 printf(" -i: print image information\n");
459 printf(" -t: output as text when possible\n");
460 printf(" -n: swap the order of nibbles (for 4bpp)\n");
461 printf(" -555: convert to BGR555\n");
462 printf(" -g: GBA colors (optimize colors for the GBA display)\n");
463 printf(" -T <WxH>: reorder as a series of tiles of the requested size\n");
464 printf(" -D: deduplicate tiles\n");
465 printf(" -om <tilemap file>: output tilemap recreating the image from dedup-ed tiles\n");
466 printf(" -h: print usage and exit\n");