#!/usr/bin/tcc -run
/*
 * This tool is made to load a PNG, and convert it to a C array with
 * ARGB8888 format, premultiplied alpha.
 * It uses stb_image.h to load the PNG, which isn't included in the
 * repository. The tool isn't essential to build, but is useful if you
 * want icons in your menus.
 * The output is a C array, with the width and height of the image
 * followed by the pixels in ARGB8888 format.
 * The output is written to stdout, unless -o is used.
 */
#include <ctype.h>
#define STB_IMAGE_IMPLEMENTATION
#define STBI_NO_SIMD
#define STBI_NO_HDR
#include "/opt/projects/stb/stb_image.h"

static void usage(const char *argv0)
{
	fprintf(stderr,
		"Usage: %s -n <array_name> "
				"[-o <output_filename>] "
				"[-t <int type>] "
				"<PNG filename>\n",
				argv0);
}

int main(int argc, char **argv)
{
	const char *array_name = NULL;
	const char *fname = "docs/Apple_logo_rainbow_version2_28x28.png";
	const char *int_type = "uint32_t";
	FILE *out = stdout;

	for (int i = 1; i < argc; i++) {
		if (!strcmp(argv[i], "-n") && i + 1 < argc) {
			array_name = argv[i + 1];
			i++;
		} else if (!strcmp(argv[i], "-o") && i + 1 < argc) {
			out = fopen(argv[i + 1], "w");
			if (!out) {
				fprintf(stderr, "Error opening %s\n", argv[i + 1]);
				usage(argv[0]);
				return 1;
			}
			i++;
		} else if (!strcmp(argv[i], "-t") && i + 1 < argc) {
			int_type = argv[i + 1];
			i++;
		} else if (!strcmp(argv[i], "-h")) {
			usage(argv[0]);
			return 0;
		} else {
			fname = argv[i];
		}
	}
	if (!array_name) {
		fprintf(stderr, "Missing array name\n");
		usage(argv[0]);
		return 1;
	}
	char define_name[256];
	strcpy(define_name, array_name);
	for (int i = 0; define_name[i]; i++)
		define_name[i] = toupper(define_name[i]);
	int width, height, channels;
	unsigned char *data = stbi_load(fname, &width, &height, &channels, 0);
	if (!data) {
		fprintf(stderr, "Error loading image\n");
		return 1;
	}
	fprintf(out, "// Autogenerated with:\n//\t");
	for (int i = 0; i < argc; i++)
		fprintf(out, " %s", argv[i]);
	fprintf(out, "\n");
	fprintf(out, "// Image with a W:%dpx, H:%dpx and %d channels\n"
			"// Converted to ARGB8888 and premultiplied alpha\n"
			"#pragma once\n",
		width, height, channels);
	fprintf(out, "#define %s_SIZE %d\n", define_name, 2 + (width * height));
	fprintf(out, "extern const %s %s[%s_SIZE];\n", int_type, array_name, define_name);
	fprintf(out, "#ifdef %s_DEFINE\n", define_name);
	fprintf(out, "const %s %s[%s_SIZE] = {\n", int_type, array_name, define_name);
	fprintf(out, "%d, %d, // width, height\n", width, height);
	uint32_t *pixels = (uint32_t *)data;
	for (int y = 0; y < height; y++) {
		for (int x = 0; x < width; x++) {
			uint32_t pixel = pixels[y * width + x];
			// flip R and B channels
			pixel = (pixel & 0xff00ff00) |
						((pixel & 0xff) << 16) | ((pixel >> 16) & 0xff);
			// also premultiply channels with alpha
			uint32_t a = pixel >> 24;
			pixel = (pixel & ~(0xff << 0)) |
						((((((pixel >> 0) & 0xff) * a) >> 8) & 0xff) << 0);
			pixel = (pixel & ~(0xff << 8)) |
						((((((pixel >> 8) & 0xff) * a) >> 8) & 0xff) << 8);
			pixel = (pixel & ~(0xff << 16)) |
						((((((pixel >> 16) & 0xff) * a) >> 8) & 0xff) << 16);
			fprintf(out, "0x%08x,", pixel);
			if (((y * height) + x) % 8 == 7)
				fprintf(out, "\n");
		}
	}
	fprintf(out, "};\n");
	fprintf(out, "#endif /* %s_DEFINE */\n", define_name);
	return 0;
}
