summaryrefslogtreecommitdiff
path: root/snesgfx.c
diff options
context:
space:
mode:
Diffstat (limited to 'snesgfx.c')
-rw-r--r--snesgfx.c342
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
+*/