
/* 
 * Argyll Color Correction System
 *
 * RGB gamut boundary test image generator
 *
 * Author: Graeme W. Gill
 * Date:   28/12/2005
 *
 * Copyright 2005 Graeme W. Gill
 * All rights reserved.
 *
 * This material is licenced under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 :-
 * see the License.txt file for licencing details.
 */

/*
 * Generate TIFF image with two RGB cube surface hexagons,
 * plus a rectangular grey wedges between them, on a grey
 * background, or a rectangular gamut surface test image.
 */

/*
 * TTBD:
 */

#undef DEBUG

#define verbo stdout

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <fcntl.h>
#include "copyright.h"
#include "aconfig.h"
#include "numsup.h"
#include "render.h"

#define DEF_DPI 200
#define DITHER 0		/* [0] 1 for test 8 bit dithering, 2 for test error diffusion */
							/* 0x8001 for dithering FG only, 0x8002 for err. diff. FG only */
#undef PNG_MEM				/* Test PNG save to memory */

void
usage(void) {
	fprintf(stderr,"Create test images, default hex RGB surface and wedge, Version %s\n",ARGYLL_VERSION_STR);
	fprintf(stderr,"Author: Graeme W. Gill, licensed under the AGPL Version 3\n");
	fprintf(stderr,"usage: timage [-options] outfile.[tif|png]\n");
//	fprintf(stderr," -v                Verbose\n");
	fprintf(stderr," -t                Generate rectangular gamut boundary test chart\n");
	fprintf(stderr," -p steps          Generate a colorspace step chart with L* steps^2\n");
	fprintf(stderr," -r res            Resolution in DPI (default %d)\n",DEF_DPI);
	fprintf(stderr," -s                Smooth blend\n");
	fprintf(stderr," -x                16 bit output\n");
	fprintf(stderr," -4                CMYK output\n");
	fprintf(stderr," -g prop           Percentage towards grey (default 0%%)\n");
	fprintf(stderr," -P                Save as PNG file (deffault TIFF)\n");
//	fprintf(stderr," -D	               Debug primitives plot */
	fprintf(stderr," outfile.[tif|png] Output TIFF or PNG file\n");
	exit(1);
	}

int main(int argc, char *argv[]) {
	int fa,nfa;				/* current argument we're looking at */
	int verb = 0;
	int rchart = 0;			/* Rectangular chart */
	int schart = 0;			/* Step chart with steps^2 */
	int smooth = 0;			/* Use smooth blending */
	rend_format fmt = tiff_file;	/* Output filr format */
	int debugchart = 0;		/* Debug chart */
	double res = DEF_DPI;
	depth2d depth = bpc8_2d;
	int cmyk = 0;			/* Do CMYK output */
	char outname[MAXNAMEL+1] = { 0 };	/* Output TIFF name */
	render2d *r;
	color2d c;
	double vv[6][2];
	color2d cc[4];
	double gbf = 1.0;		/* Grey blend factor */
	double w, h;			/* Size of page in mm */
	int i, j;

	error_program = "timage";

	if (argc <= 1)
		usage();

	/* Process the arguments */
	for(fa = 1;fa < argc;fa++) {

		nfa = fa;					/* skip to nfa if next argument is used */
		if (argv[fa][0] == '-') {	/* Look for any flags */
			char *na = NULL;		/* next argument after flag, null if none */

			if (argv[fa][2] != '\000')
				na = &argv[fa][2];		/* next is directly after flag */
			else {
				if ((fa+1) < argc) {
					if (argv[fa+1][0] != '-') {
						nfa = fa + 1;
						na = argv[nfa];		/* next is seperate non-flag argument */
					}
				}
			}

			if (argv[fa][1] == '?')
				usage();

			else if (argv[fa][1] == 'v')
				verb = 1;

			/* Rectangular chart */
			else if (argv[fa][1] == 't') {
				rchart = 1;
				schart = 0;

			/* Smooth blending */
			} else if (argv[fa][1] == 's')
				smooth = 1;

			/* 16 bit depth */
			else if (argv[fa][1] == 'x')
				depth = bpc16_2d;

			/* cmyk */
			else if (argv[fa][1] == '4')
				cmyk = 1;

			/* step chart */
			else if (argv[fa][1] == 'p') {
				fa = nfa;
				if (na == NULL) usage();
				schart = atoi(na);
				if (schart <= 0) usage();
				rchart = 0;
			}

			/* PNG file */
			else if (argv[fa][1] == 'P') {
				fmt = png_file;
			}

			/* debug chart */
			else if (argv[fa][1] == 'D') {
				debugchart = 1;
			}

			/* resolution */
			else if (argv[fa][1] == 'r') {
				fa = nfa;
				if (na == NULL) usage();
				res = atof(na);
				if (res <= 0.0) usage();
			}

			/* grey blend */
			else if (argv[fa][1] == 'g') {
				fa = nfa;
				if (na == NULL) usage();
				gbf = 1.0 - 0.01 * atof(na);
				if (gbf < 0.0 || gbf > 1.0) usage();
			}
			else 
				usage();
		} else
			break;
	}

	/* Get the file name arguments */
	if (fa >= argc || argv[fa][0] == '-') usage();
	strncpy(outname,argv[fa++],MAXNAMEL); outname[MAXNAMEL] = '\000';

	res /= 25.4;				/* Convert to DPmm */ 

	/* Debug chart - test each primitive in RGB space */
	if (debugchart) {
		font2d fo;

		h = 140.0;
		w = 200.0;

		if (cmyk)
			error("CMYK not supported for test chart");

		if ((r = new_render2d(w, h, NULL, res, res, rgb_2d, 0, depth, DITHER, NULL, NULL, 0.0)) == NULL) {
			error("new_render2d() failed");
		}
	
		/* Set the background color */
//		c[0] = 0.5;
//		c[1] = 0.5;
//		c[2] = 0.5;
		c[0] = 1.0;
		c[1] = 1.0;
		c[2] = 1.0;
		r->set_defc(r, c);
	
		c[0] = 1.0;	/* Red rectangle, bottom left */
		c[1] = 0.0;
		c[2] = 0.0;
		r->add(r, new_rect2d(r, 5.0, 5.0, 8.0, 8.0, c)) ;

		c[0] = 0.0;	/* Green to the right of red */
		c[1] = 1.0;
		c[2] = 0.0;
		r->add(r, new_rect2d(r, 15.0, 5.0, 8.0, 8.0, c)) ;

		c[0] = 0.0;	/* Blue to the right of green */
		c[1] = 0.0;
		c[2] = 1.0;
		r->add(r, new_rect2d(r, 25.0, 5.0, 8.0, 8.0, c)) ;

		/* A vertex shaded rectangle */
		cc[0][0] = 1.0; 	/* Red */
		cc[0][1] = 0.0;
		cc[0][2] = 0.0;
		cc[1][0] = 0.0;		/* Green */
		cc[1][1] = 1.0;
		cc[1][2] = 0.0;
		cc[2][0] = 0.0;		/* Blue */
		cc[2][1] = 0.0;
		cc[2][2] = 1.0;
		cc[3][0] = 1.0;		/* Yellow */
		cc[3][1] = 1.0;
		cc[3][2] = 0.0;

		r->add(r, new_rectvs2d(r, 5.0, 20.0, 18.0, 18.0, cc));

		/* A shaded triangle to the right of the shaded rectangle */
		vv[0][0] = 30.0;
		vv[0][1] = 20.0;
		cc[0][0] = 0.0;
		cc[0][1] = 1.0;
		cc[0][2] = 1.0;
	
		vv[1][0] = 50.0;
		vv[1][1] = 20.0;
		cc[1][0] = 1.0;
		cc[1][1] = 0.0;
		cc[1][2] = 1.0;
	
		vv[2][0] = 40.0;
		vv[2][1] = 40.0;
		cc[2][0] = 1.0;
		cc[2][1] = 1.0;
		cc[2][2] = 0.0;
		r->add(r, new_trivs2d(r, vv, cc));

		/* A circular disk right of the shaded triangle */
		c[0] = 0.0;	/* Cyan */
		c[1] = 1.0;
		c[2] = 1.0;
		r->add(r, new_disk2d(r, 65.0, 29.0, 9.0, c));

		/* A circle right of the disk */
		c[0] = 1.0;	/* Magenta */
		c[1] = 0.0;
		c[2] = 1.0;
		add_circle2d(r, 90.0, 29.0, 9.0, 2.0, c);


		/* A polygon */
		c[0] = 0.8;	/* Brown */
		c[1] = 0.8;
		c[2] = 0.2;

		vv[0][0] = 115.0;
		vv[0][1] = 20.0;
		vv[1][0] = 130.0;
		vv[1][1] = 20.0;
		vv[2][0] = 140.0;
		vv[2][1] = 35.0;
		vv[3][0] = 125.0;
		vv[3][1] = 25.0;
		vv[4][0] = 110.0;
		vv[4][1] = 40.0;
		r->add(r, new_poly2d(r, 5, vv, c));

		/* A diagonal wide line */
		c[0] = 0.0;
		c[1] = 0.0;
		c[2] = 0.0;
		r->add(r, new_line2d(r, 10.0, 45.0, 11.0, 55.0, 3.0, 1, c));
	
		/* A dashed line */
		add_dashed_line2d(r, 20.0, 45.0, 100.0, 65.0, 1.0,  3.0, 5.0,  1, c);
	
		fo = futura_l;

		/* rectange the size the letter A should be */
		c[0] = 0.7;
		c[1] = 0.0;
		c[2] = 0.0;
		r->add(r, new_rect2d(r, 10.0, 60.0, 7.7, 10.0, c)) ;

		c[0] = 0.0;
		c[1] = 0.0;
		c[2] = 0.0;
		/* The letter A */
		add_char2d(r, NULL, NULL, fo, 'A', 10.0, 60.0, 10.0, 0, c);

		/* A test string */
		add_string2d(r, NULL, NULL, fo, "Testing 1234", 10.0, 70.0, 7.0, 3, c);

		/* The full font */
		{
			double x, y;
			char chars[33];

			x = 15.0;
			y = 125.0; 
			for (j = 0; j < 4; j++) {
				for (i = 0; i < 32; i++)
					chars[i] = j * 32 + i;
				chars[i] = '\000';
				add_string2d(r, NULL, NULL, fo, chars, x, y, 7.0, 0, c);
				y -= 10.0;
			}
		}

	/* RGB Hexagon chart */
	} else if (rchart == 0 && schart == 0) {
		double r3o2;			/* 0.866025 */
		double bb = 0.07;		/* Border proportion */
		double hh = 40.0;		/* Height of hexagon in mm */
		color2d white;
		color2d red;
		color2d green;
		color2d blue;
		color2d cyan;
		color2d magenta;
		color2d yellow;
		color2d black;
		color2d grey;
		color2d kblack;

		r3o2 = sqrt(3.0)/2.0;		/* Width to heigh of hexagon */
		h = (1.0 + 2.0 * bb) * hh;
		w = (4.0 * bb + 0.25 + 2.0 * r3o2) * hh;
	
		if ((r = new_render2d(w, h, NULL, res, res, cmyk ? cmyk_2d : rgb_2d, 0, depth, DITHER, NULL, NULL, 0.0)) == NULL) {
			error("new_render2d() failed");
		}
	
		if (cmyk) {
			white[0] = 0.0;
			white[1] = 0.0;
			white[2] = 0.0;
			white[3] = 0.0;
			red[0] = 0.0;
			red[1] = 1.0;
			red[2] = 1.0;
			red[3] = 0.0;
			green[0] = 1.0;
			green[1] = 0.0;
			green[2] = 1.0;
			green[3] = 0.0;
			blue[0] = 1.0;
			blue[1] = 1.0;
			blue[2] = 0.0;
			blue[3] = 0.0;
			cyan[0] = 1.0;
			cyan[1] = 0.0;
			cyan[2] = 0.0;
			cyan[3] = 0.0;
			magenta[0] = 0.0;
			magenta[1] = 1.0;
			magenta[2] = 0.0;
			magenta[3] = 0.0;
			yellow[0] = 0.0;
			yellow[1] = 0.0;
			yellow[2] = 1.0;
			yellow[3] = 0.0;
			kblack[0] = 0.0;
			kblack[1] = 0.0;
			kblack[2] = 0.0;
			kblack[3] = 1.0;
			grey[0] = 0.0;
			grey[1] = 0.0;
			grey[2] = 0.0;
			grey[3] = 0.5;
			black[0] = 1.0;
			black[1] = 1.0;
			black[2] = 1.0;
			black[3] = 0.0;
		} else {
			white[0] = 1.0;
			white[1] = 1.0;
			white[2] = 1.0;
			red[0] = 1.0;
			red[1] = 0.0;
			red[2] = 0.0;
			green[0] = 0.0;
			green[1] = 1.0;
			green[2] = 0.0;
			blue[0] = 0.0;
			blue[1] = 0.0;
			blue[2] = 1.0;
			cyan[0] = 0.0;
			cyan[1] = 1.0;
			cyan[2] = 1.0;
			magenta[0] = 1.0;
			magenta[1] = 0.0;
			magenta[2] = 1.0;
			yellow[0] = 1.0;
			yellow[1] = 1.0;
			yellow[2] = 0.0;
			black[0] = 0.0;
			black[1] = 0.0;
			black[2] = 0.0;
			grey[0] = 0.5;
			grey[1] = 0.5;
			grey[2] = 0.5;
		}

		/* Set the default color */
		r->set_defc(r, grey);
	
		/* Left hand hex */
		vv[0][0] = hh * bb + r3o2 * 0.5 * hh;
		vv[0][1] = hh * bb + hh/2.0;
		cc[0][0] = white[0] * gbf + (1.0 - gbf) * grey[0];
		cc[0][1] = white[1] * gbf + (1.0 - gbf) * grey[1];
		cc[0][2] = white[2] * gbf + (1.0 - gbf) * grey[2];
		cc[0][3] = white[3] * gbf + (1.0 - gbf) * grey[3];
	
		vv[1][0] = hh * bb + r3o2 * 0.5 * hh;
		vv[1][1] = hh * bb;
		cc[1][0] = red[0] * gbf + (1.0 - gbf) * grey[0];
		cc[1][1] = red[1] * gbf + (1.0 - gbf) * grey[1];
		cc[1][2] = red[2] * gbf + (1.0 - gbf) * grey[2];
		cc[1][3] = red[3] * gbf + (1.0 - gbf) * grey[3];
	
		vv[2][0] = hh * bb;
		vv[2][1] = hh * bb + 0.25 * hh;
		cc[2][0] = magenta[0] * gbf + (1.0 - gbf) * grey[0];
		cc[2][1] = magenta[1] * gbf + (1.0 - gbf) * grey[1];
		cc[2][2] = magenta[2] * gbf + (1.0 - gbf) * grey[2];
		cc[2][3] = magenta[3] * gbf + (1.0 - gbf) * grey[3];
		r->add(r, new_trivs2d(r, vv, cc));
	
		vv[1][0] = hh * bb;
		vv[1][1] = hh * bb + 0.75 * hh;
		cc[1][0] = blue[0] * gbf + (1.0 - gbf) * grey[0];
		cc[1][1] = blue[1] * gbf + (1.0 - gbf) * grey[1];
		cc[1][2] = blue[2] * gbf + (1.0 - gbf) * grey[2];
		cc[1][3] = blue[3] * gbf + (1.0 - gbf) * grey[3];
		r->add(r, new_trivs2d(r, vv, cc));
	
		vv[2][0] = hh * bb + r3o2 * 0.5 * hh;
		vv[2][1] = hh * bb + 1.0 * hh;
		cc[2][0] = cyan[0] * gbf + (1.0 - gbf) * grey[0];
		cc[2][1] = cyan[1] * gbf + (1.0 - gbf) * grey[1];
		cc[2][2] = cyan[2] * gbf + (1.0 - gbf) * grey[2];
		cc[2][3] = cyan[3] * gbf + (1.0 - gbf) * grey[3];
		r->add(r, new_trivs2d(r, vv, cc));
	
		vv[1][0] = hh * bb + r3o2 * 1.0 * hh;;
		vv[1][1] = hh * bb + 0.75 * hh;
		cc[1][0] = green[0] * gbf + (1.0 - gbf) * grey[0];
		cc[1][1] = green[1] * gbf + (1.0 - gbf) * grey[1];
		cc[1][2] = green[2] * gbf + (1.0 - gbf) * grey[2];
		cc[1][3] = green[3] * gbf + (1.0 - gbf) * grey[3];
		r->add(r, new_trivs2d(r, vv, cc));
	
		vv[2][0] = hh * bb + r3o2 * 1.0 * hh;
		vv[2][1] = hh * bb + 0.25 * hh;
		cc[2][0] = yellow[0] * gbf + (1.0 - gbf) * grey[0];
		cc[2][1] = yellow[1] * gbf + (1.0 - gbf) * grey[1];
		cc[2][2] = yellow[2] * gbf + (1.0 - gbf) * grey[2];
		cc[2][3] = yellow[3] * gbf + (1.0 - gbf) * grey[3];
		r->add(r, new_trivs2d(r, vv, cc));
	
		vv[1][0] = hh * bb + r3o2 * 0.5 * hh;;
		vv[1][1] = hh * bb;
		cc[1][0] = red[0] * gbf + (1.0 - gbf) * grey[0];
		cc[1][1] = red[1] * gbf + (1.0 - gbf) * grey[1];
		cc[1][2] = red[2] * gbf + (1.0 - gbf) * grey[2];
		cc[1][3] = red[3] * gbf + (1.0 - gbf) * grey[3];
		r->add(r, new_trivs2d(r, vv, cc));
	
	
		/* Right hand hex */
		vv[0][0] = hh * (3.0 * bb + 0.25 + r3o2) + r3o2 * 0.5 * hh;
		vv[0][1] = hh * bb + hh/2.0;
		cc[0][0] = black[0] * gbf + (1.0 - gbf) * grey[0];
		cc[0][1] = black[1] * gbf + (1.0 - gbf) * grey[1];
		cc[0][2] = black[2] * gbf + (1.0 - gbf) * grey[2];
		cc[0][3] = black[3] * gbf + (1.0 - gbf) * grey[3];
	
		vv[1][0] = hh * (3.0 * bb + 0.25 + r3o2) + r3o2 * 0.5 * hh;
		vv[1][1] = hh * bb;
		cc[1][0] = red[0] * gbf + (1.0 - gbf) * grey[0];
		cc[1][1] = red[1] * gbf + (1.0 - gbf) * grey[1];
		cc[1][2] = red[2] * gbf + (1.0 - gbf) * grey[2];
		cc[1][3] = red[3] * gbf + (1.0 - gbf) * grey[3];
	
		vv[2][0] = hh * (3.0 * bb + 0.25 + r3o2);
		vv[2][1] = hh * bb + 0.25 * hh;
		cc[2][0] = magenta[0] * gbf + (1.0 - gbf) * grey[0];
		cc[2][1] = magenta[1] * gbf + (1.0 - gbf) * grey[1];
		cc[2][2] = magenta[2] * gbf + (1.0 - gbf) * grey[2];
		cc[2][3] = magenta[3] * gbf + (1.0 - gbf) * grey[3];
		r->add(r, new_trivs2d(r, vv, cc));
	
		vv[1][0] = hh * (3.0 * bb + 0.25 + r3o2);
		vv[1][1] = hh * bb + 0.75 * hh;
		cc[1][0] = blue[0] * gbf + (1.0 - gbf) * grey[0];
		cc[1][1] = blue[1] * gbf + (1.0 - gbf) * grey[1];
		cc[1][2] = blue[2] * gbf + (1.0 - gbf) * grey[2];
		cc[1][3] = blue[3] * gbf + (1.0 - gbf) * grey[3];
		r->add(r, new_trivs2d(r, vv, cc));
	
		vv[2][0] = hh * (3.0 * bb + 0.25 + r3o2) + r3o2 * 0.5 * hh;
		vv[2][1] = hh * bb + 1.0 * hh;
		cc[2][0] = cyan[0] * gbf + (1.0 - gbf) * grey[0];
		cc[2][1] = cyan[1] * gbf + (1.0 - gbf) * grey[1];
		cc[2][2] = cyan[2] * gbf + (1.0 - gbf) * grey[2];
		cc[1][3] = cyan[3] * gbf + (1.0 - gbf) * grey[3];
		r->add(r, new_trivs2d(r, vv, cc));
	
		vv[1][0] = hh * (3.0 * bb + 0.25 + r3o2) + r3o2 * 1.0 * hh;;
		vv[1][1] = hh * bb + 0.75 * hh;
		cc[1][0] = green[0] * gbf + (1.0 - gbf) * grey[0];
		cc[1][1] = green[1] * gbf + (1.0 - gbf) * grey[1];
		cc[1][2] = green[2] * gbf + (1.0 - gbf) * grey[2];
		cc[1][3] = green[3] * gbf + (1.0 - gbf) * grey[3];
		r->add(r, new_trivs2d(r, vv, cc));
	
		vv[2][0] = hh * (3.0 * bb + 0.25 + r3o2) + r3o2 * 1.0 * hh;
		vv[2][1] = hh * bb + 0.25 * hh;
		cc[2][0] = yellow[0] * gbf + (1.0 - gbf) * grey[0];
		cc[2][1] = yellow[1] * gbf + (1.0 - gbf) * grey[1];
		cc[2][2] = yellow[2] * gbf + (1.0 - gbf) * grey[2];
		cc[2][3] = yellow[3] * gbf + (1.0 - gbf) * grey[3];
		r->add(r, new_trivs2d(r, vv, cc));
	
		vv[1][0] = hh * (3.0 * bb + 0.25 + r3o2) + r3o2 * 0.5 * hh;;
		vv[1][1] = hh * bb;
		cc[1][0] = red[0] * gbf + (1.0 - gbf) * grey[0];
		cc[1][1] = red[1] * gbf + (1.0 - gbf) * grey[1];
		cc[1][2] = red[2] * gbf + (1.0 - gbf) * grey[2];
		cc[1][3] = red[3] * gbf + (1.0 - gbf) * grey[3];
		r->add(r, new_trivs2d(r, vv, cc));
	
		/* Center wedge */
		cc[0][0] = black[0] * gbf + (1.0 - gbf) * grey[0];
		cc[0][1] = black[1] * gbf + (1.0 - gbf) * grey[1];
		cc[0][2] = black[2] * gbf + (1.0 - gbf) * grey[2];
		cc[0][3] = black[3] * gbf + (1.0 - gbf) * grey[3];
		cc[1][0] = black[0] * gbf + (1.0 - gbf) * grey[0];
		cc[1][1] = black[1] * gbf + (1.0 - gbf) * grey[1];
		cc[1][2] = black[2] * gbf + (1.0 - gbf) * grey[2];
		cc[1][3] = black[3] * gbf + (1.0 - gbf) * grey[3];
		cc[2][0] = white[0] * gbf + (1.0 - gbf) * grey[0];
		cc[2][1] = white[1] * gbf + (1.0 - gbf) * grey[1];
		cc[2][2] = white[2] * gbf + (1.0 - gbf) * grey[2];
		cc[2][3] = white[3] * gbf + (1.0 - gbf) * grey[3];
		cc[3][0] = white[0] * gbf + (1.0 - gbf) * grey[0];
		cc[3][1] = white[1] * gbf + (1.0 - gbf) * grey[1];
		cc[3][2] = white[2] * gbf + (1.0 - gbf) * grey[2];
		cc[3][3] = white[3] * gbf + (1.0 - gbf) * grey[3];
		if (cmyk)
			r->add(r, new_rectvs2d(r, (2.0 * bb + r3o2) * hh, bb * hh, 0.125 * hh, hh, cc));
		else
			r->add(r, new_rectvs2d(r, (2.0 * bb + r3o2) * hh, bb * hh, 0.25 * hh, hh, cc));

		/* Center CMY wedge */
		if (cmyk) {
			cc[0][0] = kblack[0] * gbf + (1.0 - gbf) * grey[0];
			cc[0][1] = kblack[1] * gbf + (1.0 - gbf) * grey[1];
			cc[0][2] = kblack[2] * gbf + (1.0 - gbf) * grey[2];
			cc[0][3] = kblack[3] * gbf + (1.0 - gbf) * grey[3];
			cc[1][0] = kblack[0] * gbf + (1.0 - gbf) * grey[0];
			cc[1][1] = kblack[1] * gbf + (1.0 - gbf) * grey[1];
			cc[1][2] = kblack[2] * gbf + (1.0 - gbf) * grey[2];
			cc[1][3] = kblack[3] * gbf + (1.0 - gbf) * grey[3];
			cc[2][0] = white[0] * gbf + (1.0 - gbf) * grey[0];
			cc[2][1] = white[1] * gbf + (1.0 - gbf) * grey[1];
			cc[2][2] = white[2] * gbf + (1.0 - gbf) * grey[2];
			cc[2][3] = white[3] * gbf + (1.0 - gbf) * grey[3];
			cc[3][0] = white[0] * gbf + (1.0 - gbf) * grey[0];
			cc[3][1] = white[1] * gbf + (1.0 - gbf) * grey[1];
			cc[3][2] = white[2] * gbf + (1.0 - gbf) * grey[2];
			cc[3][3] = white[3] * gbf + (1.0 - gbf) * grey[3];
			r->add(r, new_rectvs2d(r, (2.0 * bb + r3o2 + 0.125) * hh, bb * hh, 0.125 * hh, hh, cc));
		}


	/* RGB Rectangular chart */
	} else if (schart == 0) {
		double bb = 0.07;		/* Border proportion */
		double hh = 50.0;		/* Height of hexagon in mm */
		double sc[6][3] = {		/* Saturated color sequence */
			{ 1, 0, 0 },
			{ 1, 0, 1 },
			{ 0, 0, 1 },
			{ 0, 1, 1 },
			{ 0, 1, 0 },
			{ 1, 1, 0 }
		};

		if (cmyk)
			error("CMYK not supported for test chart");

		h = (1.0 + 2.0 * bb) * hh;
		w = (2.0 * bb + 0.20 * 7.0) * hh;
	
		if ((r = new_render2d(w, h, NULL, res, res, rgb_2d, 0, depth, DITHER, NULL, NULL, 0.0)) == NULL) {
			error("new_render2d() failed");
		}
	
		/* Set the default color */
		c[0] = 0.5;
		c[1] = 0.5;
		c[2] = 0.5;
		r->set_defc(r, c);
	
		for (i = 0; i < 7; i++) {
			prim2d *p;

			/* Top rectangle */
			cc[0][0] = sc[i % 6][0] * gbf + (1.0 - gbf) * 0.5;
			cc[0][1] = sc[i % 6][1] * gbf + (1.0 - gbf) * 0.5;
			cc[0][2] = sc[i % 6][2] * gbf + (1.0 - gbf) * 0.5;
			cc[1][0] = sc[(i+1) % 6][0] * gbf + (1.0 - gbf) * 0.5;
			cc[1][1] = sc[(i+1) % 6][1] * gbf + (1.0 - gbf) * 0.5;
			cc[1][2] = sc[(i+1) % 6][2] * gbf + (1.0 - gbf) * 0.5;
			cc[2][0] = 1.0 * gbf + (1.0 - gbf) * 0.5;
			cc[2][1] = 1.0 * gbf + (1.0 - gbf) * 0.5;
			cc[2][2] = 1.0 * gbf + (1.0 - gbf) * 0.5;
			cc[3][0] = 1.0 * gbf + (1.0 - gbf) * 0.5;
			cc[3][1] = 1.0 * gbf + (1.0 - gbf) * 0.5;
			cc[3][2] = 1.0 * gbf + (1.0 - gbf) * 0.5;
			p = new_rectvs2d(r, (bb + i * 0.2) * hh, (bb + 0.5) * hh, 0.2 * hh, 0.5 * hh, cc);
			if (smooth) {
				rectvs2d *pp = (rectvs2d *)p;
				pp->x_blend = 2;
				pp->y_blend = 3;
			}
			r->add(r, p);

			/* Bottom rectangle */
			cc[0][0] = 0.0 * gbf + (1.0 - gbf) * 0.5;
			cc[0][1] = 0.0 * gbf + (1.0 - gbf) * 0.5;
			cc[0][2] = 0.0 * gbf + (1.0 - gbf) * 0.5;
			cc[1][0] = 0.0 * gbf + (1.0 - gbf) * 0.5;
			cc[1][1] = 0.0 * gbf + (1.0 - gbf) * 0.5;
			cc[1][2] = 0.0 * gbf + (1.0 - gbf) * 0.5;
			cc[2][0] = sc[i % 6][0] * gbf + (1.0 - gbf) * 0.5;
			cc[2][1] = sc[i % 6][1] * gbf + (1.0 - gbf) * 0.5;
			cc[2][2] = sc[i % 6][2] * gbf + (1.0 - gbf) * 0.5;
			cc[3][0] = sc[(i+1) % 6][0] * gbf + (1.0 - gbf) * 0.5;
			cc[3][1] = sc[(i+1) % 6][1] * gbf + (1.0 - gbf) * 0.5;
			cc[3][2] = sc[(i+1) % 6][2] * gbf + (1.0 - gbf) * 0.5;
			p = new_rectvs2d(r, (bb + i * 0.2) * hh, bb * hh, 0.2 * hh, 0.5 * hh, cc);
			if (smooth) {
				rectvs2d *pp = (rectvs2d *)p;
				pp->x_blend = 2;
				pp->y_blend = 2;
			}
			r->add(r, p);
		}

	} else {	/* Lab step chart */
		double hh = 50.0;		/* Height of hexagon in mm */
		double bb = 0.05;		/* Border proportion */
		double ss, bs;			/* Step size, border size */

		h = hh;
		w = hh;

		if (cmyk)
			error("CMYK not supported for test chart");

		bs = (bb * hh)/(schart + 1.0);
		ss = hh * (1.0 - bb)/schart;
	
		if ((r = new_render2d(w, h, NULL, res, res, lab_2d, 0, depth, DITHER, NULL, NULL, 0.0)) == NULL) {
			error("new_render2d() failed");
		}
	
		/* Set the default color */
		c[0] = 0.0;
		c[1] = 0.0;
		c[2] = 0.0;
		r->set_defc(r, c);
	
		for (i = 0; i < schart; i++) {
			for (j = 0; j < schart; j++) {
				double lv;
				
				lv = (double)(j * schart + i)/(schart * schart - 1.0) * 100.0;

				cc[0][0] = lv;
				cc[0][1] = -127.0;
				cc[0][2] = -127.0;
				cc[1][0] = lv;
				cc[1][1] =  127.0;
				cc[1][2] = -127.0;
				cc[2][0] = lv;
				cc[2][1] = -127.0;
				cc[2][2] =  127.0;
				cc[3][0] = lv;
				cc[3][1] =  127.0;
				cc[3][2] =  127.0;
				r->add(r, new_rectvs2d(r, bs + i * (bs + ss),
				                          bs + j * (bs + ss),
				                          ss, ss, cc));
			}
		}
	}

#ifdef PNG_MEM
	{
		char *nmode = "w";
		FILE *fp;
		unsigned char *buf;
		size_t len, wlen;

#if !defined(O_CREAT) && !defined(_O_CREAT)
# error "Need to #include fcntl.h!"
#endif
#if defined(O_BINARY) || defined(_O_BINARY)
		nmode = "wb";
#endif

		if (r->write(r, "MemoryBuf", 1, &buf, &len, png_mem))
			error("render->write failed");

		if ((fp = fopen(outname, nmode)) == NULL)
			error("render2d: open '%s' for writing",outname);

		if (len != (wlen = fwrite(buf, 1, len, fp)))
			error("render2d: writing %u bytes to '%s' failed (wrote %u)",len,outname,wlen);

		if (fclose(fp))
			error("render2d: failed to close after writing",outname);
	}
#else
	if (r->write(r, outname, 1, NULL, NULL, fmt))
		error("render->write failed");
#endif
	r->del(r);

	return 0;
}


