diff options
Diffstat (limited to 'snesgfx.c')
-rw-r--r-- | snesgfx.c | 342 |
1 files changed, 342 insertions, 0 deletions
diff --git a/snesgfx.c b/snesgfx.c new file mode 100644 index 0000000..3c59ed8 --- /dev/null +++ b/snesgfx.c @@ -0,0 +1,342 @@ +/* + + snesgfx - Basic PNG to SNES graphics converter + + Copyright (C) 2018 Ian Cowburn (ianc@noddybox.co.uk) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + ------------------------------------------------------------------------- + + Main + +*/ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <setjmp.h> + +#include <png.h> + +static void Usage(void) +{ + fprintf(stderr, "usage: snesgfx [-p palette_file] [-s] [-c colours] " + "source.png dest.data\n"); + exit(EXIT_FAILURE); +} + +static int GetPixel(int x, int y, png_bytep *data, int width, int height) +{ + if (x >= width || y >= height) + { + return 0; + } + else + { + return data[y][x]; + } +} + +static void Convert4Colour(png_bytep *data, + int width, + int height, + int size, + FILE *output) +{ + int x,y; + int dx,dy; + + for(y = 0; y < height; y += size) + { + for(x = 0; x < width; x+= size) + { + for(dy = 0; dy < size; dy++) + { + int plane[2] = {0}; + int f; + + for(dx = 0; dx < size; dx++) + { + int pix = GetPixel(x + dx, y + dy, data, width, height); + + for(f = 0; f < 2; f++) + { + if ((pix & (1 << f)) == (1 << f)) + { + plane[f] = plane[f] | (1 << dx); + } + } + } + + for(f = 0; f < 2; f++) + { + putc(plane[f], output); + } + } + } + } +} + +static void Convert16Colour(png_bytep *data, + int width, + int height, + int size, + FILE *output) +{ + int x,y; + int dx,dy; + + for(y = 0; y < height; y += size) + { + for(x = 0; x < width; x+= size) + { + int plane[size][4]; + int f; + + for(dy = 0; dy < size; dy++) + { + for(f = 0; f < 4; f++) + { + plane[dy][f] = 0; + } + } + + for(dy = 0; dy < size; dy++) + { + for(dx = 0; dx < size; dx++) + { + int pix = GetPixel(x + dx, y + dy, data, width, height); + + for(f = 0; f < 4; f++) + { + if ((pix & (1 << f)) == (1 << f)) + { + plane[dy][f] = plane[dy][f] | (1 << dx); + } + } + } + } + + for(dy = 0; dy < size; dy++) + { + for(f = 0; f < 2; f++) + { + putc(plane[dy][f], output); + } + } + + for(dy = 0; dy < size; dy++) + { + for(f = 2; f < 4; f++) + { + putc(plane[dy][f], output); + } + } + } + } +} + +static void SavePalette(const char *palette, + int colours, + png_color *pal, + int palette_size) +{ + FILE *fp; + int f; + + if (!(fp = fopen(palette, "wb"))) + { + fprintf(stderr, "Failed to create %s\n", palette); + exit(EXIT_FAILURE); + } + + for(f = 0; f < colours; f++) + { + int val; + + if (f < palette_size) + { + int r,g,b; + + r = pal[f].red / 8; + g = pal[f].green / 8; + b = pal[f].blue / 8; + + val = b * 1024 + g * 32 + r; + } + else + { + val = 0; + } + + putc(val & 0xff, fp); + val = val>>8; + putc(val & 0xff, fp); + } + + fclose(fp); +} + +int main(int argc, char *argv[]) +{ + char *palette = NULL; + int size = 8; + int colours = 4; + char *source = NULL; + char *dest = NULL; + char header[8] = {0}; + FILE *input; + FILE *output; + png_structp png; + png_infop info; + png_bytep *rows; + int width; + int height; + int f; + + f = 1; + + while(f < argc && argv[f][0] == '-') + { + switch(argv[f][1]) + { + case 'p': + palette = argv[++f]; + break; + case 's': + size = 16; + break; + case 'c': + colours = atoi(argv[++f]); + break; + default: + Usage(); + break; + } + + f++; + } + + if (f < argc) + { + source = argv[f++]; + } + + if (f < argc) + { + dest = argv[f++]; + } + + if (!source || !dest) + { + Usage(); + } + + if (size == 16) + { + fprintf(stderr, "16x16 mode not yet done\n"); + return EXIT_FAILURE; + } + + if (colours != 4 && colours != 16) + { + fprintf(stderr, "Colours must be 4 or 16\n"); + return EXIT_FAILURE; + } + + if (!(input = fopen(source, "rb"))) + { + fprintf(stderr, "Failed to open %s\n", source); + return EXIT_FAILURE; + } + + fread(header, 1, 8, input); + + if (png_sig_cmp(header, 0, 8)) + { + fprintf(stderr, "%s not a PNG\n", source); + return EXIT_FAILURE; + } + + if (!(output = fopen(dest, "wb"))) + { + fprintf(stderr, "Failed to create %s\n", dest); + return EXIT_FAILURE; + } + + png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + + if (!png) + { + fprintf(stderr, "Failed to initialise PNG\n"); + return EXIT_FAILURE; + } + + info = png_create_info_struct(png); + + if (!info) + { + fprintf(stderr, "Failed to initialise PNG\n"); + return EXIT_FAILURE; + } + + if (setjmp(png_jmpbuf(png))) + { + fprintf(stderr, "Error in PNG library\n"); + return EXIT_FAILURE; + } + + png_init_io(png, input); + png_set_sig_bytes(png, 8); + + png_set_packing(png); + + png_read_png(png, info, PNG_TRANSFORM_STRIP_ALPHA, NULL); + + if (png_get_color_type(png, info) != PNG_COLOR_TYPE_PALETTE) + { + fprintf(stderr, "Expecting a PNG with a palette\n"); + } + + rows = png_get_rows(png, info); + width = png_get_image_width(png, info); + height = png_get_image_height(png, info); + + if (colours == 4) + { + Convert4Colour(rows, width, height, size, output); + } + else + { + Convert16Colour(rows, width, height, size, output); + } + + fclose(input); + fclose(output); + + if (palette) + { + png_color *pal; + int palette_size; + + png_get_PLTE(png, info, &pal, &palette_size); + SavePalette(palette, colours, pal, palette_size); + } + + return EXIT_SUCCESS; +} + +/* +vim: ai sw=4 ts=8 expandtab +*/ |