
#ifndef RENDER2D_H
#define RENDER2D_H

/* 
 * render2d
 *
 * Simple 2D raster rendering support.
 *
 * Author:  Graeme W. Gill
 * Date:    28/12/2005
 *
 * Copyright 2005, 2008, 2012, 2014 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.
 */

/* This is basically a simple 2D ray tracing renderer, so it's not especially */
/* efficient, but it's simple and direct, easy to add new primitives or */
/* capabilities, is high quality, and has an accelleration algorithm that */
/* makes it fast enough for printed output. */

/* Mathematical coordinate in mm are used for primitives, ie. the origin is */
/* the bottom left corner. */
/* Device color values range from 0.0 to 1.0 */

#define MXCH2D  16			/* Maximum color channels */
#define TOTC2D  (MXCH2D+1)	/* Maximum total components */
#define PRIX2D  (MXCH2D)	/* Index of primitive kept with color value */

#define MXPATSIZE 4			/* Maximum color pattern size */

/* Color type */
/* Shouldn't this be an xcolorants mask ? */
typedef enum {
	w_2d,			/* Video style grey */
	k_2d,			/* Printing style grey */
	lab_2d,			/* Lab */
	rgb_2d,			/* RGB */
	cmyk_2d,		/* CMYK */
	ncol_2d,		/* N color */
	ncol_a_2d		/* N color with extra as alpha */
} colort2d;

/* Pixel depth */
typedef enum {
	bpc8_2d,		/* 8 bits per component */
	bpc16_2d		/* 16 bits per component */
} depth2d;

typedef double color2d[TOTC2D]; 

/* Font type */
typedef enum {
	rowman_s = 0,	/* Rownman, single stroke */
	rowman_d = 1,	/* Rownman, double stroke */
	rowman_t = 2,	/* Rownman, triple stroke */
	timesr   = 3,	/* Times Roman */
	timesr_b = 4,	/* Times Roman, Bold */
	futura_l = 5,	/* Futura, Light */
	futura_m = 6	/* Futura, Medium */
} font2d;

/* ------------------------------------ */

struct _render2d;

#define PRIM_STRUCT							\
/*	primt2d tag; */			/* Type of primitive */	\
	int    ix;				/* Index (order added) */ \
	int    ncc;				/* Number of color components */	\
	struct _prim2d *next;	/* Linked list to next primitive */ \
	struct _prim2d *yl0;	/* Previous lines Y list linked list */ \
	struct _prim2d *yl;		/* Active Y list linked list */ \
	struct _prim2d *xl;		/* Active X list linked list */ \
	double x0, y0, x1, y1;	/* Extent, top & left inclusive, bot & right non-inclusive */ \
	void (*del)(struct _prim2d *s);		/* Delete the object */ \
							/* Render the object at location. Return nz if in primitive */ \
	int (*rend)(struct _prim2d *s, color2d rv, double x, double y);

struct _prim2d {
	PRIM_STRUCT
}; typedef struct _prim2d prim2d;

/* ------------------------------------ */
/* Solid rectange primitive */
struct _rect2d {
	PRIM_STRUCT
	double rx0, ry0, rx1, ry1;	/* Rectangle vertices */
	color2d c;					/* Color of rectangle (if dpat == NULL) */
	double (*dpat)[MXPATSIZE][MXPATSIZE][TOTC2D];	/* Special for ChromeCast experiments */
	int dp_w, dp_h;				/* dpat dimensions */
}; typedef struct _rect2d rect2d;

prim2d *new_rect2d(struct _render2d *s, double x, double y, double w, double h, color2d c);

void set_rect2d_dpat(struct _rect2d *s, double (*pat)[MXPATSIZE][MXPATSIZE][TOTC2D], int w, int h);

/* ------------------------------------ */
/* Vertex shaded rectange */
struct _rectvs2d {
	PRIM_STRUCT
	double rx0, ry0, rx1, ry1;	/* Rectangle vertices */
	color2d c[4];	/* Bot left, bot right, top left, top right */
	int x_blend;	/* Blending rule flags, 0 = linear, 1 = spline, 2 = sine */
	int y_blend;
	int y_sine;
}; typedef struct _rectvs2d rectvs2d;

prim2d *new_rectvs2d(struct _render2d *s, double x, double y, double w, double h, color2d c[4]);

/* ------------------------------------ */
/* Vertex shaded triangle */
struct _trivs2d {
	PRIM_STRUCT
	double be[3][3];	/* baricentric equations */
	color2d c[3];		/* Color of each vertex */
}; typedef struct _trivs2d trivs2d;

prim2d *new_trivs2d(struct _render2d *s, double v[3][2], color2d c[3]);

/* ------------------------------------ */
/* Polygon */

/* Solid polygon primitive */
struct _poly2d {
	PRIM_STRUCT
	color2d c;			/* Color of polyangle (if dpat == NULL) */
	int n;				/* Number of vertices */
	double co[1][2];	/* Array of [n][2] vertices */
}; typedef struct _poly2d poly2d;

prim2d *new_poly2d(struct _render2d *s, int n, double v[][2], color2d c);

#ifdef NEVER
int pnpoly(int nvert, float *vertx, float *verty, float testx, float testy) {
  int i, j, c = 0;
  for (i = 0, j = nvert-1; i < nvert; j = i++) {
    if ( ((verty[i]>testy) != (verty[j]>testy)) &&
     (testx < (vertx[j]-vertx[i]) * (testy-verty[i]) / (verty[j]-verty[i]) + vertx[i]) )
       c = !c;
  }
  return c;
}

#endif // NEVER

/* ------------------------------------ */

/* Circular disk/line primitive */
struct _disk2d {
	PRIM_STRUCT
	double cx, cy;				/* Center */
	color2d c;					/* Color of disk/line */
	double orr, irr;			/* Outer radius squared, inner radius squared (0.0 if disk) */
}; typedef struct _disk2d disk2d;

/* Center and radius */
prim2d *new_disk2d(struct _render2d *s, double x, double y, double r, color2d c);

/* Center, radius and line width */
void add_circle2d(struct _render2d *s, double x, double y, double r, double w, color2d c);

/* ------------------------------------ */
/* A single line. */
	
struct _line2d {
	PRIM_STRUCT
	double lx0, ly0, lx1, ly1;	/* Line vertices */
	double ww;					/* half width of line squared */
	int cap;					/* 0 = butt, 1 = round, 2 = square */
	color2d c;					/* Color of the line */
	int t;						/* nz if line is degenerate */
	double vx, vy;				/* Vector relative to x0 y0 */
}; typedef struct _line2d line2d;

prim2d *new_line2d(struct _render2d *s, double x0, double y0, double x1, double y1, double w, int cap, color2d c);

/* ------------------------------------ */
/* Comound primitives */

/* add a dashed line */
void add_dashed_line2d(
struct _render2d *s,
double x0, double y0,
double x1, double y1,
double w,
double on, double off,
int cap,
color2d c);

/* Add a text character at the given location using lines */
void add_char2d(
struct _render2d *s,
double *xinc,		/* Return increment in position for next character */
double *yinc,
font2d fo,			/* Font to use */
char ch,			/* Character code to be printed */
double x, double y,	/* Location of bottom left of normal orientation text */
double h,			/* Height of text in normal orientation */
int or,				/* Orintation, 0 = right, 1 = down, 2 = left, 3 = up */
color2d c			/* Color of text */
);

/* Add a string from the given location using lines. */
void add_string2d(
struct _render2d *s,
double *xinc,		/* Return increment in position for next character */
double *yinc,
font2d fo,			/* Font to use */
char *string,		/* Character code to be printed */
double x, double y,	/* Location of bottom left of normal orientation text */
double h,			/* Height of text in normal orientation */
int or,				/* Orintation, 0 = right, 1 = down, 2 = left, 3 = up */
color2d c			/* Color of text */
);

/* Return the total width of the string without adding it */
void meas_string2d(
struct _render2d *s,
double *xinc,		/* Return increment in position for next character */
double *yinc,
font2d fo,			/* Font to use */
char *string,		/* Character code to be printed */
double h,			/* Height of text in normal orientation */
int or				/* Orintation, 0 = right, 1 = down, 2 = left, 3 = up */
);


/* Convert an angle in degrees (0 = right) */
/* into transform matrix */
void deg2mat(double mat[2][2], double a);

/* Convert a vector into a rotation matrix */
void vec2mat(double mat[2][2], double dx, double dy);

/* Add a text character at the given location using lines */
/* (matrix orientation version) */
void add_char2dmat(
struct _render2d *s,
double *xinc,		/* Return increment in position for next character */
double *yinc,
font2d fo,			/* Font to use */
char ch,			/* Character code to be printed */
double x, double y,	/* Location of bottom left of normal orientation text */
double h,			/* Height of text in normal orientation */
double mat[2][2],	/* Unity orientation matrix */
color2d c			/* Color of text */
);

/* Add a string from the given location using lines. */
/* (matrix orientation version) */
void add_string2dmat(
struct _render2d *s,
double *xinc,		/* Return increment in position for next character */
double *yinc,
font2d fo,			/* Font to use */
char *string,		/* Character code to be printed */
double x, double y,	/* Location of bottom left of normal orientation text */
double h,			/* Height of text in normal orientation */
double mat[2][2],	/* Unity orientation matrix */
color2d c			/* Color of text */
);

/* Return the total width of the string without adding it */
/* (matrix orientation version) */
void meas_string2dmat(
struct _render2d *s,
double *xinc,		/* Return increment in position for next character */
double *yinc,
font2d fo,			/* Font to use */
char *string,		/* Character code to be printed */
double h,			/* Height of text in normal orientation */
double mat[2][2]	/* Unity orientation matrix */
);

/* ------------------------------------ */

/* Type of output to save to. */
typedef enum {
	tiff_file,		/* Write a TIFF format file */
	png_file,		/* Write a PNG format file */
	png_mem,		/* Write a PNG image to a memory buffer */
	mem_rast		/* Write a memory raster */
} rend_format;

/* ------------------------------------ */
/* Render object */

struct _render2d {

/* Private: */
	int ix;					/* Next primitive index */
	double fw, fh;			/* Page size in mm including margines */
	double lm, rm, tm, bm;	/* Page margines in mm */
	double w, h;			/* Page size in mm excluding margines */
	double hres, vres;		/* Page pixel resolution in pixels/mm */
	int pw, ph;				/* Page size in pixels */
	colort2d csp;			/* Color space */
	int      ncc;			/* Number of color components */
	depth2d  dpth;			/* Depth of the components */
	int    dither;     		/* Dither flag, 1 = ordered, 2 = error diffusion */
	int    noavg;     		/* Don't anti-alias or average 4 pixels together */
	int    dithfgo;			/* Dither F.G. only flag */
	void (*quant)(void *qcntx, double *out, double *in); /* optional quantization func. for edith */
	void *qcntx;
	double mxerr;			/* Maximum error diffusion error */

	color2d defc;			/* Default color value */

	void (*bgfunc)(void *cntx, color2d c, double x, double y);	/* BG color function */
	void *cntx;

	prim2d *head;			/* Start of list of primitives in rendering order */
	prim2d *yl;				/* Active Y list linked list head */
	prim2d *xl;				/* Active X list linked list head */

	int ppitch;				/* if mem_rast, pixel pitch in bytes */
	int lpitch;				/* if mem_rast, line pitch in bytes */

/* Public: */
	/* Methods */
	void (*del)(struct _render2d *s);					/* Free ourselves and all primitives */

	void (*set_defc)(struct _render2d *s, color2d c);	/* Set the default/background color */

	void (*set_bg_func)(struct _render2d *s,			/* Set background color function */
		void (*func)(void *cntx, color2d c, double x, double y),	/* Func can choose not to set */
		void *cntx
	);

	void (*add)(struct _render2d *s, prim2d *p);		/* Add a primitive */

	int (*write)(struct _render2d *s, char *filename, int comprn,
		unsigned char **obuf, size_t *olen,
	    rend_format fmt);								/* Render and write to a TIFF or PNG file */

	void (*rast_details)(struct _render2d *s,
		int *width,										/* Return raster width in pixels */
		int *height,									/* Return raster width in pixels */
		int *ppitch,									/* Return pixel pitch in bytes */
		int *lpitch										/* Return line pitch in bytes */
	);

}; typedef struct _render2d render2d;

/* Constructor */
render2d *new_render2d(
	double w,		/* width in mm */
	double h,		/* height in mm */
	double ma[4],	/* Margines, left, right, top, bottom, NULL for zero in mm */
	double hres,	/* horizontal resolution in pixels/mm */
	double vres,	/* horizontal resolution in pixels/mm */
	colort2d csp,	/* Color type */
	int nd,			/* Number of channels if c = ncol */
	depth2d dpth,	/* Pixel depth */
	int dither,		/* Dither flag, 1 = ordered, 2 = error diffusion, | 0x8000 to dither FG only */
					/* | 0x4000 don't anti-alias by averaging pixels together. */
	void (*quant)(void *qcntx, double *out, double *in), /* optional quantization func. for edith */
	void *qcntx,	/* Optional context for quant */
	double mxerr	/* Maximum error diffusion error (0.0 for default) */
);

#endif /* RENDER2D_H */

