Blame src/webpng.c

Packit ed3af9
#ifdef HAVE_CONFIG_H
Packit ed3af9
#include "config.h"
Packit ed3af9
#endif
Packit ed3af9
Packit ed3af9
/* Bring in standard I/O and string manipulation functions */
Packit ed3af9
#include <stdarg.h>
Packit ed3af9
#ifdef HAVE_ERRNO_H
Packit ed3af9
#include <errno.h>
Packit ed3af9
#endif
Packit ed3af9
#include <stdio.h>
Packit ed3af9
#ifdef HAVE_STDLIB_H
Packit ed3af9
#include <stdlib.h>
Packit ed3af9
#endif
Packit ed3af9
#ifdef HAVE_STRING_H
Packit ed3af9
#include <string.h>
Packit ed3af9
#endif
Packit ed3af9
#ifdef HAVE_UNISTD_H
Packit ed3af9
#include <unistd.h>
Packit ed3af9
#endif
Packit ed3af9
Packit ed3af9
#ifdef __clang__
Packit ed3af9
/* Workaround broken clang behavior: https://llvm.org/bugs/show_bug.cgi?id=20144 */
Packit ed3af9
#undef strcmp
Packit ed3af9
#endif
Packit ed3af9
Packit ed3af9
/* Bring in the gd library functions */
Packit ed3af9
#include "gd.h"
Packit ed3af9
Packit ed3af9
#define KEEP_TRANS (-100)
Packit ed3af9
Packit ed3af9
static const char argv0[] = "webpng";
Packit ed3af9
Packit ed3af9
static void usage(const char *msg)
Packit ed3af9
{
Packit ed3af9
	/* If the command failed, output an explanation. */
Packit ed3af9
	fprintf(msg == NULL ? stdout : stderr,
Packit ed3af9
		"Usage: %s [-i y|n] [-l] [-t index|none] [-d] [-a] pngname.png\n"
Packit ed3af9
		"  -i <y|n>   Turns on/off interlace\n"
Packit ed3af9
		"  -l         Prints the table of color indexes\n"
Packit ed3af9
		"  -t <index> Set the transparent color to the specified index (0-255 or \"none\")\n"
Packit ed3af9
		"  -d         Reports the dimensions and other characteristics of the image\n"
Packit ed3af9
		"  -a         Prints all alpha channels that are not 100%% opaque\n"
Packit ed3af9
		"\n"
Packit ed3af9
		"If you specify '-' as the input file, stdin/stdout will be used as input/output.\n",
Packit ed3af9
		argv0);
Packit ed3af9
	if (msg)
Packit ed3af9
		fprintf(stderr, "\nError: %s\n", msg);
Packit ed3af9
	exit(msg == NULL ? 0 : 1);
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
static void err(const char *fmt, ...)
Packit ed3af9
{
Packit ed3af9
	va_list ap;
Packit ed3af9
	int e = errno;
Packit ed3af9
Packit ed3af9
	fprintf(stderr, "%s: error: ", argv0);
Packit ed3af9
	va_start(ap, fmt);
Packit ed3af9
	vfprintf(stderr, fmt, ap);
Packit ed3af9
	va_end(ap);
Packit ed3af9
	if (e)
Packit ed3af9
		fprintf(stderr, ": %s", strerror(e));
Packit ed3af9
	fputs("\n", stderr);
Packit ed3af9
Packit ed3af9
	exit(1);
Packit ed3af9
}
Packit ed3af9
Packit ed3af9
int
Packit ed3af9
main(int argc, char **argv)
Packit ed3af9
{
Packit ed3af9
	FILE *in;
Packit ed3af9
	FILE *out;
Packit ed3af9
	const char *infile;
Packit ed3af9
	char *tmpfile;
Packit ed3af9
	int i;
Packit ed3af9
	int use_stdin_stdout = 0;
Packit ed3af9
Packit ed3af9
	int interlace = -100;
Packit ed3af9
	int list_color_table = 0;
Packit ed3af9
	int trans_col = KEEP_TRANS;
Packit ed3af9
	int report_details = 0;
Packit ed3af9
	int print_alpha = 0;
Packit ed3af9
Packit ed3af9
	/* Declare our image pointer */
Packit ed3af9
	gdImagePtr im = 0;
Packit ed3af9
	/* We'll set 'write' once we know the user's request
Packit ed3af9
	   requires that the image be written back to disk. */
Packit ed3af9
	int write = 0;
Packit ed3af9
	int got_a_flag = 0;
Packit ed3af9
Packit ed3af9
	/* Consider each argument in turn. */
Packit ed3af9
	opterr = 0;
Packit ed3af9
	while ((i = getopt(argc, argv, "i:lt:da")) != -1) {
Packit ed3af9
		got_a_flag = 1;
Packit ed3af9
		switch (i) {
Packit ed3af9
		case 'i':
Packit ed3af9
			/* -i turns on and off interlacing. */
Packit ed3af9
			if (strcmp(optarg, "y") == 0)
Packit ed3af9
				interlace = 1;
Packit ed3af9
			else if (strcmp(optarg, "n") == 0)
Packit ed3af9
				interlace = 0;
Packit ed3af9
			else
Packit ed3af9
				usage("-i specified without y or n");
Packit ed3af9
			write = 1;
Packit ed3af9
			break;
Packit ed3af9
Packit ed3af9
		case 'l':
Packit ed3af9
			/* List the colors in the color table. */
Packit ed3af9
			list_color_table = 1;
Packit ed3af9
			break;
Packit ed3af9
Packit ed3af9
		case 't':
Packit ed3af9
			/* Set transparent index (or none). */
Packit ed3af9
			if (strcmp(optarg, "none") == 0) {
Packit ed3af9
				/* -1 means not transparent. */
Packit ed3af9
				trans_col = -1;
Packit ed3af9
			} else {
Packit ed3af9
				/* XXX: Should check for errors. */
Packit ed3af9
				trans_col = atoi(optarg);
Packit ed3af9
				if (trans_col < 0 || trans_col > 255)
Packit ed3af9
					err("-t has to be in the range of 0 and 255 (inclusive)");
Packit ed3af9
			}
Packit ed3af9
			write = 1;
Packit ed3af9
			break;
Packit ed3af9
Packit ed3af9
		case 'd':
Packit ed3af9
			/* Output dimensions, etc. */
Packit ed3af9
			report_details = 1;
Packit ed3af9
			break;
Packit ed3af9
Packit ed3af9
		case 'a':
Packit ed3af9
			/* Alpha channel info -- thanks to Wez Furlong */
Packit ed3af9
			print_alpha = 1;
Packit ed3af9
			break;
Packit ed3af9
Packit ed3af9
		case 'h':
Packit ed3af9
			usage(NULL);
Packit ed3af9
			break;
Packit ed3af9
		default:
Packit ed3af9
		case '?':
Packit ed3af9
			if (optind < argc && strcmp(argv[optind], "--help") == 0)
Packit ed3af9
				usage(NULL);
Packit ed3af9
			usage("unknown option");
Packit ed3af9
			break;
Packit ed3af9
		}
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	if (got_a_flag == 0)
Packit ed3af9
		usage("missing operation flag");
Packit ed3af9
Packit ed3af9
	if (argc == optind)
Packit ed3af9
		usage("missing filename");
Packit ed3af9
	else if (argc != optind + 1)
Packit ed3af9
		usage("can only specify one file");
Packit ed3af9
Packit ed3af9
	infile = argv[optind];
Packit ed3af9
	if (strcmp(infile, "-") == 0) {
Packit ed3af9
		/* - is synonymous with STDIN */
Packit ed3af9
		use_stdin_stdout = 1;
Packit ed3af9
		in = stdin;
Packit ed3af9
	} else
Packit ed3af9
		in = fopen(infile, "rb");
Packit ed3af9
Packit ed3af9
	if (!in)
Packit ed3af9
		err("can't open file %s", infile);
Packit ed3af9
Packit ed3af9
	/* Now load the image. */
Packit ed3af9
	im = gdImageCreateFromPng(in);
Packit ed3af9
	fclose(in);
Packit ed3af9
	/* If the load failed, it must not be a PNG file. */
Packit ed3af9
	if (!im)
Packit ed3af9
		err("%s is not a valid PNG file", infile);
Packit ed3af9
Packit ed3af9
	if (list_color_table) {
Packit ed3af9
		/* List the colors in the color table. */
Packit ed3af9
		if (!im->trueColor) {
Packit ed3af9
			int j;
Packit ed3af9
			/* Tabs used below. */
Packit ed3af9
			printf("Index	Red	Green	Blue Alpha\n");
Packit ed3af9
			for (j = 0; j < gdImageColorsTotal(im); ++j) {
Packit ed3af9
				/* Use access macros to learn colors. */
Packit ed3af9
				printf("%d	%d	%d	%d	%d\n", j,
Packit ed3af9
					gdImageRed(im, j),
Packit ed3af9
					gdImageGreen(im, j),
Packit ed3af9
					gdImageBlue(im, j),
Packit ed3af9
					gdImageAlpha(im, j));
Packit ed3af9
			}
Packit ed3af9
		} else
Packit ed3af9
			printf("Truecolor image, no palette entries to list.\n");
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	if (report_details) {
Packit ed3af9
		/* Output dimensions, etc. */
Packit ed3af9
		int t;
Packit ed3af9
		printf("Width: %d Height: %d Colors: %d\n",
Packit ed3af9
			gdImageSX(im),
Packit ed3af9
			gdImageSY(im),
Packit ed3af9
			gdImageColorsTotal(im));
Packit ed3af9
Packit ed3af9
		/* -1 means the image is not transparent. */
Packit ed3af9
		t = gdImageGetTransparent(im);
Packit ed3af9
		if (t != -1)
Packit ed3af9
			printf("First 100%% transparent index: %d\n", t);
Packit ed3af9
		else
Packit ed3af9
			printf("First 100%% transparent index: none\n");
Packit ed3af9
Packit ed3af9
		if (gdImageGetInterlaced(im))
Packit ed3af9
			printf("Interlaced: yes\n");
Packit ed3af9
		else
Packit ed3af9
			printf("Interlaced: no\n");
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	if (print_alpha) {
Packit ed3af9
		/* Alpha channel info -- thanks to Wez Furlong */
Packit ed3af9
		int maxx, maxy, x, y, alpha, pix, nalpha = 0;
Packit ed3af9
Packit ed3af9
		maxx = gdImageSX(im);
Packit ed3af9
		maxy = gdImageSY(im);
Packit ed3af9
Packit ed3af9
		printf("alpha channel information:\n");
Packit ed3af9
Packit ed3af9
		if (im->trueColor) {
Packit ed3af9
			for (y = 0; y < maxy; y++) {
Packit ed3af9
				for (x = 0; x < maxx; x++) {
Packit ed3af9
					pix = gdImageGetPixel(im, x, y);
Packit ed3af9
					alpha = gdTrueColorGetAlpha(pix);
Packit ed3af9
Packit ed3af9
					if (alpha > gdAlphaOpaque) {
Packit ed3af9
						/* Use access macros to learn colors. */
Packit ed3af9
						printf("%d	%d	%d	%d\n",
Packit ed3af9
							gdTrueColorGetRed(pix),
Packit ed3af9
							gdTrueColorGetGreen(pix),
Packit ed3af9
							gdTrueColorGetBlue(pix),
Packit ed3af9
							alpha);
Packit ed3af9
						nalpha++;
Packit ed3af9
					}
Packit ed3af9
Packit ed3af9
				}
Packit ed3af9
			}
Packit ed3af9
		} else
Packit ed3af9
			printf("NOT a true color image\n");
Packit ed3af9
Packit ed3af9
		printf("%d alpha channels\n", nalpha);
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	/* If no modifications requested, break out. */
Packit ed3af9
	if (write == 0) {
Packit ed3af9
		gdImageDestroy(im);
Packit ed3af9
		return 0;
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	if (interlace == 1)
Packit ed3af9
		gdImageInterlace(im, 1);
Packit ed3af9
	else if (interlace == 0)
Packit ed3af9
		gdImageInterlace(im, 0);
Packit ed3af9
Packit ed3af9
	if (trans_col != KEEP_TRANS)
Packit ed3af9
		gdImageColorTransparent(im, trans_col);
Packit ed3af9
Packit ed3af9
	if (use_stdin_stdout) {
Packit ed3af9
		out = stdout;
Packit ed3af9
	} else {
Packit ed3af9
		/* Open a temporary file. */
Packit ed3af9
		size_t filelen = strlen(infile);
Packit ed3af9
		size_t len = filelen + 8;
Packit ed3af9
		int outfd;
Packit ed3af9
Packit ed3af9
		tmpfile = malloc(len);
Packit ed3af9
		if (tmpfile == NULL)
Packit ed3af9
			err("could not create a tempfile");
Packit ed3af9
		memcpy(tmpfile, infile, filelen);
Packit ed3af9
		strcpy(tmpfile + filelen, ".XXXXXX");
Packit ed3af9
Packit ed3af9
		outfd = mkstemp(tmpfile);
Packit ed3af9
		if (outfd == -1)
Packit ed3af9
			err("could not open %s", tmpfile);
Packit ed3af9
Packit ed3af9
		out = fdopen(outfd, "wb");
Packit ed3af9
		if (!out)
Packit ed3af9
			err("could not open %s", tmpfile);
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	/* Write the new PNG. */
Packit ed3af9
	gdImagePng(im, out);
Packit ed3af9
Packit ed3af9
	if (!use_stdin_stdout) {
Packit ed3af9
		fclose(out);
Packit ed3af9
		/* Erase the old PNG. */
Packit ed3af9
		unlink(infile);
Packit ed3af9
		/* Rename the new to the old. */
Packit ed3af9
		if (rename(tmpfile, infile) != 0)
Packit ed3af9
			err("unable to rename %s to %s", infile, tmpfile);
Packit ed3af9
	}
Packit ed3af9
Packit ed3af9
	/* Delete the image from memory. */
Packit ed3af9
	gdImageDestroy(im);
Packit ed3af9
Packit ed3af9
	/* All's well that ends well. */
Packit ed3af9
	return 0;
Packit ed3af9
}