The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
/*
 * This tool creates a frame pattern data for debug purpose used by
 * test_qrspec. test_qrspec and create_frame_pattern uses the same function
 * of libqrencode. This means the test is meaningless if test_qrspec is run
 * with a pattern data created by create_frame_pattern of the same version.
 * In order to test it correctly, create a pattern data by the tool of the
 * previous version, or use the frame data attached to the package.
 */

#include <stdio.h>
#include <string.h>
#include <png.h>
#include "common.h"
#include "../qrspec.h"

void append_pattern(int version, FILE *fp)
{
	int width;
	unsigned char *frame;

	frame = QRspec_newFrame(version);
	width = QRspec_getWidth(version);
	fwrite(frame, 1, width * width, fp);
	free(frame);
}

static int writePNG(unsigned char *frame, int width, const char *outfile)
{
	static FILE *fp;
	png_structp png_ptr;
	png_infop info_ptr;
	unsigned char *row, *p, *q;
	int x, y, xx, yy, bit;
	int realwidth;
	const int margin = 0;
	const int size = 1;

	realwidth = (width + margin * 2) * size;
	row = (unsigned char *)malloc((realwidth + 7) / 8);
	if(row == NULL) {
		fprintf(stderr, "Failed to allocate memory.\n");
		exit(EXIT_FAILURE);
	}

	if(outfile[0] == '-' && outfile[1] == '\0') {
		fp = stdout;
	} else {
		fp = fopen(outfile, "wb");
		if(fp == NULL) {
			fprintf(stderr, "Failed to create file: %s\n", outfile);
			perror(NULL);
			exit(EXIT_FAILURE);
		}
	}

	png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
	if(png_ptr == NULL) {
		fclose(fp);
		fprintf(stderr, "Failed to initialize PNG writer.\n");
		exit(EXIT_FAILURE);
	}

	info_ptr = png_create_info_struct(png_ptr);
	if(info_ptr == NULL) {
		fclose(fp);
		fprintf(stderr, "Failed to initialize PNG write.\n");
		exit(EXIT_FAILURE);
	}

	if(setjmp(png_jmpbuf(png_ptr))) {
		png_destroy_write_struct(&png_ptr, &info_ptr);
		fclose(fp);
		fprintf(stderr, "Failed to write PNG image.\n");
		exit(EXIT_FAILURE);
	}

	png_init_io(png_ptr, fp);
	png_set_IHDR(png_ptr, info_ptr,
			realwidth, realwidth,
			1,
			PNG_COLOR_TYPE_GRAY,
			PNG_INTERLACE_NONE,
			PNG_COMPRESSION_TYPE_DEFAULT,
			PNG_FILTER_TYPE_DEFAULT);
	png_write_info(png_ptr, info_ptr);

	/* top margin */
	memset(row, 0xff, (realwidth + 7) / 8);
	for(y=0; y<margin * size; y++) {
		png_write_row(png_ptr, row);
	}

	/* data */
	p = frame;
	for(y=0; y<width; y++) {
		bit = 7;
		memset(row, 0xff, (realwidth + 7) / 8);
		q = row;
		q += margin * size / 8;
		bit = 7 - (margin * size % 8);
		for(x=0; x<width; x++) {
			for(xx=0; xx<size; xx++) {
				*q ^= (*p & 1) << bit;
				bit--;
				if(bit < 0) {
					q++;
					bit = 7;
				}
			}
			p++;
		}
		for(yy=0; yy<size; yy++) {
			png_write_row(png_ptr, row);
		}
	}
	/* bottom margin */
	memset(row, 0xff, (realwidth + 7) / 8);
	for(y=0; y<margin * size; y++) {
		png_write_row(png_ptr, row);
	}

	png_write_end(png_ptr, info_ptr);
	png_destroy_write_struct(&png_ptr, &info_ptr);

	fclose(fp);
	free(row);

	return 0;
}

void write_pattern_image(int version, const char *filename)
{
	int width;
	unsigned char *frame;
	static char str[256];

	frame = QRspec_newFrame(version);
	width = QRspec_getWidth(version);

	snprintf(str, 256, "%s-%d.png", filename, version);
	writePNG(frame, width, str);
	free(frame);
}

void write_pattern(const char *filename)
{
	FILE *fp;
	int i;

	fp = fopen(filename, "wb");
	if(fp == NULL) {
		perror("Failed to open a file to write:");
		abort();
	}
	for(i=1; i<=QRSPEC_VERSION_MAX; i++) {
		append_pattern(i, fp);
		write_pattern_image(i, filename);
	}
	fclose(fp);
}

int main(int argc, char **argv)
{
	if(argc < 2) {
		printf("Create empty frame patterns.\nUsage: %s FILENAME\n", argv[0]);
		exit(0);
	}
	write_pattern(argv[1]);
	return 0;
}