/*
 *  TEX Device Driver
 *  tpic.c:		tpic specials (version 2.2) support routines
 *  			Based on the specifications included in transfig package
 *  			by Oh-Yeah?		15 July 1993
 *  			Modified (dashed, dotted, spline) by A. Nakabayashi
 *  			Modified by I. Matsuda
 */

#ifndef NOTPIC	/* include the whole file */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

#define _DEF_STDIO_H_
#include "dd.h"
#include "err.h"
#include "inter.h"

#define DEFAULT_PENSIZE 5	/* in milli-inches */

#define SIGNED_PIXEL int	/* signed pixel type for raster graphics */
#define TPIC_MI long	/* type of tpic milli-inches (x, y) */
 /* #define TWO_PAI_MORE 6.2833 *//* for drawing an arc: 2pai + eps */
#define TWO_PAI_LESS 6.2831	/* for drawing an arc: 2pai - eps */

static char token_sep[] = " ";

#ifndef NOTPIC_EXTENSION
/* bitblt.c */
void alter_bitblt_rt(BOOL flag);
void set_scl_matrix(PIXEL origin_x, PIXEL origin_y, double h, double v);
void set_rot_matrix(PIXEL origin_x, PIXEL origin_y, double angle);
void set_lrot_matrix(PIXEL origin_x, PIXEL origin_y, double angle);
void set_affine_matrix(PIXEL origin_x, PIXEL origin_y, 
	double a_00, double a_01, double a_10, double a_11);
void save_rot_matrix(void);
void restore_rot_matrix(void);
int get_rot_matrix(int);
int add_rot_level(int);
#endif

/* bitmap.c */
void write_rule(PIXEL h, PIXEL v, PIXEL a, PIXEL b);
void init_tile(double grayscale);	/* initialize overriding for tiles */
void fin_tile(void);	/* finish tiling. recover original data */
void write_tile(PIXEL h, PIXEL v, PIXEL a, PIXEL b);

/* buffer.c */
void *marea(unsigned int size);
void clear_buf(BUF_INFO *);

/* sptopxl.asm or size.c (prtsize.c) */
PIXEL sptopixel(SCALED_PT sp);
PIXEL vtopixel(SCALED_PT sp);

#define FILL_EDGE 0x01	/* pixel point status flags */
#define VISIBLE   0x02

typedef struct {
	PIXEL x, y;
} pixelpoint;

typedef struct pixel_link_st {
	PIXEL p;
	unsigned char status;
	struct pixel_link_st *next;
} pixel_link;


/* pret.c */
PIXEL get_current_x(void);
PIXEL get_current_y(void);
pixelpoint relmi_to_abs(TPIC_MI x, TPIC_MI y);

/* called by initialize() in init.c, prtinit.c */
void tpic_init(PIXEL buf_height);

/* called by xxx1() in pret.c. interface is func ptr (tpic_special) */
static BOOL tpic_pret(char *special);	/* interpret special. Ret TRUE if found */
static BOOL tpic_ignore(char *dummy);	/* ignore special. Ret FALSE, always */

/* ptr to either function. activated when tpic_init() is called */
BOOL (*tpic_special) (char *special) = tpic_ignore;

static void tpic_pn(char *s);
static void tpic_pa(char *s);
static void tpic_fp(char *dummy);
static void tpic_ip(char *dummy);
static void tpic_da(char *s);
static void tpic_dt(char *s);
static void tpic_sp(char *s);
static PIXEL tpic_interval(double inch);
/* common part for calc an interval length */
static void tpic_ar(char *s);
static void tpic_ia(char *s);
static void tpic_ar_and_ia(TPIC_MI x, TPIC_MI y, TPIC_MI rx, TPIC_MI ry,
					double start, double end);	/* common part */
static void tpic_sh(char *s);
static void tpic_wh(char *dummy);
static void tpic_bk(char *dummy);
static void tpic_tx(char *dummy);

#ifndef NOTPIC_EXTENSION
void tpic_rt(char *s);
void tpic_rt0(char *s);
#endif

void set_grayscale(double gray);
static double get_grayscale(void);
static void flush_line(void);
static void flush_spline(void);
static void flush_arc(pixelpoint origin, pixelpoint radius, 
					double start, double end);
static void flush_raster(void);
void rasterize_line(pixelpoint start, pixelpoint end, BOOL mask_head,
					BOOL mask_tail);
static void draw_line(pixelpoint st, pixelpoint ed, PIXEL thick);
static void rasterize_arc(pixelpoint origin, pixelpoint radius, double start,
				   double end);
static void rasterize_quadarc(pixelpoint origin, pixelpoint radius, 
					pixelpoint init, pixelpoint fin, int plane);

#ifndef TPIC_OLD
static void set_visible_discrete(PIXEL visible, PIXEL invisible);
static void adjust_visible_discrete(pixelpoint start, pixelpoint end);

#endif

void set_visible(PIXEL visible, PIXEL invisible);
static void init_visible(BOOL is_discrete);	/* initialize visible pattern */
static void append_visible(PIXEL visible, PIXEL invisible, int repeat);
static BOOL is_visible(void);
static BOOL is_visible_discrete(void);
void raster_draw(void);	/* draw pixels defined in stack */
void set_pensize(TPIC_MI pensize);
static PIXEL get_h_width(void);	/* for line thickness */
static PIXEL get_v_width(void);	/* for line thickness */
static void write_point(PIXEL x, PIXEL y);
void raster_fill(void);
static void push_path(pixelpoint p);	/* push path p */
static pixelpoint pop_path(void);	/* just take out */
static void flush_path(void);	/* flush path */
static int get_path_n(void);
static void set_path_n(int);
static BOOL is_path_closed(void);

#ifndef TPIC_NSPL
static void spl_table(void);
static void spl_clean_table(void);	/* clean table */
static pixelpoint spl_get(double index);
static void spl_each_table(PIXEL x[], PIXEL y[], int n, double sder[]);
static void spl_each_periodic(PIXEL x[], PIXEL y[], int n, double sder[]);
static double spl_each_get(PIXEL x[], PIXEL y[], int n, double sder[],
					double xint, int i);
static void flush_spline_old(void);
static void tpic_bz(char *s);
/* init, prtinit */
extern  int f_tpic_turn_on;	/* default is 1 */
#endif

static void push_point(PIXEL x, PIXEL y, BOOL fill_edge, BOOL visible);
static void init_bucket(PIXEL buf_height);	/* make buckets */
void clean_bucket(void);
static pixel_link **get_bucket(PIXEL y);
static PIXEL get_bucket_min(void);
static PIXEL get_bucket_max(void);
static PIXEL get_vmax(void);

#ifndef NOTPIC_EXTENSION
static pixel_link **get_extra_bucket(PIXEL y);
static void clean_extra_bucket(void);
/* epsbox.c */
extern BOOL f_gow;
void set_black(int);
/* winjtt.c */
void PrintBitmapBuf(int mode);
#endif

static pixel_link *push_pixel(PIXEL p);
static void clear_pixel_stack(void);
static void get_current(void);	/* get current point (as pixelpoint) */
static pixelpoint mi_to_pixel(TPIC_MI x, TPIC_MI y); 
	/* milli-inches to pixel */

#ifdef VFTPIC
/* called by draw_character() in vfont.c */
void set_grayscale(double gray);
void rasterize_line(pixelpoint start, pixelpoint end, BOOL mask_head,
					BOOL mask_tail);
void set_visible(PIXEL visible, PIXEL invisible);
void raster_draw(void);	/* draw pixels defined in stack */
void set_pensize(TPIC_MI pensize);
void raster_fill(void);
void clean_bucket(void);
void freeze_tpic_status(void);
void melt_tpic_status(void);

#endif
#define f_tpic_tate ((ptex_d&1)&&(f_tpic_turn_on&16))
extern char ptex_d;

void tpic_init(PIXEL buf_height)
{	   /* alloc y-bucket[vmax] at the beginning */
	ENTER("tpic_init");
	tpic_special = (buf_height)?tpic_pret:tpic_ignore;	
			/* activate/inactivate interpretation */
	init_bucket(buf_height);	/* make vmax y-buckets */
	if(buf_height)
		set_pensize((TPIC_MI) DEFAULT_PENSIZE);
	END();
}

static BOOL tpic_pret(char *special)
{	   /* interpret special. Ret TRUE if found */
	static struct {
		char str[2];
		void (*func) (char *);
	} *cmd_ptr, commands[] = {
	{"pn", tpic_pn},
	{"pa", tpic_pa},
	{"fp", tpic_fp},
	{"ip", tpic_ip},
	{"da", tpic_da},
	{"dt", tpic_dt},
	{"sp", tpic_sp},
	{"ar", tpic_ar},
	{"ia", tpic_ia},
	{"sh", tpic_sh},
	{"wh", tpic_wh},
	{"bk", tpic_bk},
	{"tx", tpic_tx},
#ifndef NOTPIC_EXTENSION
	{"rt", tpic_rt0},	/* this is UNOFFICIAL, not portable */
#endif
#ifndef TPIC_NSPL
	{"Bz", tpic_bz},
#endif
	{"",   NULL}};

	BOOL found = FALSE;
	char *s;

	ENTER("tpic_pret");

	cmd_ptr = commands;			/* point to top */

	while (*(s = cmd_ptr->str) != 0) {
		if (special[0] == s[0] && special[1] == s[1] && special[2] <= ' ') {
			(cmd_ptr->func) (special + 2);	/* forward str */
			found = TRUE;		/* found */
			break;
		}
		cmd_ptr++;
	}

	RETURN(found);
}

static BOOL tpic_ignore(char *dummy)
{	   /* ignore special. Ret FALSE, always */
	ENTER("tpic_ignore");

	/*	error( WARNING, "tpic special encountered but skipped ... " ); */

	RETURN(FALSE);
}

/* tpic commands */

/* pn s		Sets the pen size to s milli-inches. */
static void tpic_pn(char *s)
{
	ENTER("tpic_pn");
	set_pensize((TPIC_MI) atol(s));
	END();
}

/* pa x y	Add point (x,y) to a path which is being stored.
			x and y are in milli-inches. */
static void tpic_pa(char *s)
{
	TPIC_MI x, y;

	ENTER("tpic_pa");
	get_current();				/* get current point */
	x = (TPIC_MI) atol(strtok(s, token_sep));
	y = (TPIC_MI) atol(strtok(NULL, token_sep));
	push_path(f_tpic_tate?relmi_to_abs(-y,x):relmi_to_abs(x, y));
	END();
}

/* fp	Flush (using the current pen size) a previously defined path.
		The number of path elements is reset to zero.  If shading has been
		specified and path is closed, the area should be shaded. */
static void tpic_fp(char *dummy)
{
	int tmp_path_n;

	ENTER("tpic_fp");
	if (get_grayscale() == 0) {
		tmp_path_n = get_path_n();
		set_visible(0, 1);		/* all invisible */
		flush_line();
		set_path_n(tmp_path_n);
	}
	set_visible(1, 0);			/* all visible */
	flush_line();
	flush_path();
	END();
}

/* ip	Same as fp except that the path is not drawn.
		Shading of the region is performed if it has been specified. */
static void tpic_ip(char *dummy)
{
	ENTER("tpic_ip");
	set_visible(0, 1);			/* all invisible */
	flush_line();
	flush_path();
	END();
}

/* da f		Same as fp except that the line is drawn as dashed with f inches
			per dash (f is a real number). */
/* UNOFFICIAL extension: accepts two or more real args as black and white
lengths (like Postscript setdash), and draws the pattern continuously through
the points (i.e. dashes and dots are not necessarily on the points). */
static void tpic_da(char *s)
{
	PIXEL interval, next;
	char *token;
	int tmp_path_n;

	ENTER("tpic_da");
	if (get_grayscale() == 0) {
		tmp_path_n = get_path_n();
		set_visible(0, 1);		/* all invisible */
		flush_line();
		set_path_n(tmp_path_n);
	}
	interval = tpic_interval(atof(strtok(s, token_sep)));
#ifndef NOTPIC_EXTENSION
	if ((token = strtok(NULL, token_sep)) != NULL) {
		/* UNOFFICIAL: two or more args, black white [black white ...] */
		init_visible(FALSE);	/* continuous */
		next = tpic_interval(atof(token));
		append_visible(interval, next, 1);
		while ((token = strtok(NULL, token_sep)) != NULL) {
			interval = tpic_interval(atof(token));
			next = tpic_interval(atof(strtok(NULL, token_sep)));
			append_visible(interval, next, 1);
		}
	}
	else						/* standard: one arg */
#endif
#ifndef TPIC_OLD
		set_visible_discrete(interval, interval);
#else
		set_visible(interval, interval);	/* continuous */
#endif
	flush_line();
	flush_path();
	END();
}

/* dt f		Same as da except that a dotted line is drawn, with f inches
			between each dot (f is a real number). */
static void tpic_dt(char *s)
{
	PIXEL interval, dotsize;
	int tmp_path_n;

	ENTER("tpic_dt");
	if (get_grayscale() == 0) {
		tmp_path_n = get_path_n();
		set_visible(0, 1);		/* all invisible */
		flush_line();
		set_path_n(tmp_path_n);
	}
	interval = tpic_interval(atof(s));
	dotsize = (get_h_width() + get_v_width()) / 2;	/* use pen size */
	dotsize = max(dotsize, 1);
#ifndef TPIC_OLD
	set_visible_discrete(dotsize, max(interval - dotsize, 1));
#else
	set_visible(dotsize, max(interval - dotsize, 1));	/* continuous */
#endif
	flush_line();
	flush_path();
	END();
}

/* sp d		Same as fp except that a spline is drawn through the points.
			The spline is guaranteed to go through only the first and last
			points, depending on the relative positions of any intermediate
			points.  d is an optional real value; if d = 0 or the argument
			is omitted, the spline is drawn normally, as a solid line.  If
			d > 0, it is dashed with d as the dashwid, else if d < 0, the
			spline is dotted with -d as the dotwid. */
static void tpic_sp(char *s)
{
	double d;
	PIXEL interval;
	int tmp_path_n;

	ENTER("tpic_sp");
	if (get_grayscale() == 0) {
		tmp_path_n = get_path_n();
		set_visible(0, 1);		/* all invisible */
		flush_spline();
		set_path_n(tmp_path_n);
	}
	s = strtok(s, token_sep);
	if (s == NULL || (d = atof(s)) == 0) {	/* solid */
		set_visible(1, 0);
	}
	else if (d > 0) {			/* dashed */
		interval = tpic_interval(d);
		set_visible(interval, interval);
	}
	else {						/* dotted */
		interval = tpic_interval(-d);
		set_visible(1, max(interval - 1, 1));
	}
	flush_spline();
	flush_path();
	END();
}

static PIXEL tpic_interval(double inch)
{	   /* common part for calc an interval length */
	TPIC_MI mi;
	pixelpoint p;

	ENTER("tpic_interval");
	mi = (TPIC_MI) (inch * 1000);
	p = mi_to_pixel(mi, mi);	/* only horizontal value is used ... */
	RETURN(max(p.x, 1));		/* minimum value is unity */
}

/* ar x y rx ry s e		Draw an arc with center at (x,y), from starting
						angle s to ending angle e.  If it's a complete
						circle or ellipse, then rx and ry determine the
						x and y radii, respectively.  Otherwise, rx = ry
						will hold, and an arc from s to e should be drawn. */
static void tpic_ar(char *s)
{
	TPIC_MI x, y, rx, ry;
	double start, end;
	ENTER("tpic_ar");

	x = (TPIC_MI) atol(strtok(s, token_sep));
	y = (TPIC_MI) atol(strtok(NULL, token_sep));
	rx = (TPIC_MI) atol(strtok(NULL, token_sep));
	ry = (TPIC_MI) atol(strtok(NULL, token_sep));
	start = atof(strtok(NULL, token_sep));
	end = atof(strtok(NULL, token_sep));
	if (get_grayscale() == 0) {
		set_visible(0, 1);		/* all invisible */
		tpic_ar_and_ia(x, y, rx, ry, start, end);
	}
	set_visible(1, 0);			/* all visible */
	tpic_ar_and_ia(x, y, rx, ry, start, end);
	END();
}

/* ia x y rx ry s e		The ia command is to ar what ip is to fp. */
static void tpic_ia(char *s)
{
	TPIC_MI x, y, rx, ry;
	double start, end;
	ENTER("tpic_ia");

	x = (TPIC_MI) atol(strtok(s, token_sep));
	y = (TPIC_MI) atol(strtok(NULL, token_sep));
	rx = (TPIC_MI) atol(strtok(NULL, token_sep));
	ry = (TPIC_MI) atol(strtok(NULL, token_sep));
	start = atof(strtok(NULL, token_sep));
	end = atof(strtok(NULL, token_sep));
	set_visible(0, 1);			/* all invisible */
	tpic_ar_and_ia(x, y, rx, ry, start, end);
	END();
}

static void tpic_ar_and_ia(TPIC_MI x, TPIC_MI y, TPIC_MI rx, TPIC_MI ry,
					double start, double end)
{	   /* common part */
	pixelpoint origin, radius;
	TPIC_MI	ttmp;
	
	if(f_tpic_tate){
		ttmp = x;
		x = -y;
		y = ttmp;
		ttmp = rx;
		rx = ry;
		ry = ttmp;
		start += 1.57079633;
		end += 1.57079633;
	}
	ENTER("tpic_ar_and_ia");
	get_current();				/* get current point */

	origin = relmi_to_abs(x, y);

	/*	if (fabs(end - start) < TWO_PAI_LESS) ry = rx; */

	radius = mi_to_pixel(abs(rx), abs(ry));	/* defence from neg rad (gpic) */
	radius.x = max(radius.x, 1);/* minimum value is unity */
	radius.y = max(radius.y, 1);
	flush_arc(origin, radius, start, end);
	END();
}

/* sh s		Shade the interior of the next closed figure defined with three
			or more pa commands followed by a fp or ip command, or by an ar
			or ia command.  s is a floating point number from 0 to 1.  Zero
			implies a white interior (including erasing anything underneath),
			while 1 implies black.  A value of 0.5 is a default gray shading.
			If s is not specified, a value of 0.5 should be used.  Shading
			applies to the interior only; the borders are still drawn using
			the current pen.  Pixels generated by the shading value should be
			ANDed with the pixels already in the area.  Subsequent text pixels
			should be ORed.  Thus, text followed by a sh 0 command covering
			the same area will disappear.  Any shading value greater than zero
			should add the shading to the existing text, with the text
			remaining visible.  Of course, a shading value of one will be
			completely black, so any text won't be distinguishable. */
static void tpic_sh(char *s)
{
	double gray;

	ENTER("tpic_sh");
	s = strtok(s, token_sep);
	gray = (s != NULL) ? atof(s) : 0.5;	/* if no spec, take default */
	set_grayscale(gray);
	END();
}

/* wh	This is the same as 'sh 0' */
static void tpic_wh(char *dummy)
{
	ENTER("tpic_wh");
	tpic_sh(" 0");
	END();
}

/* bk	This is the same as 'sh 1' */
static void tpic_bk(char *dummy)
{
	ENTER("tpic_bk");
	tpic_sh(" 1");
	END();
}

/* tx	This command was used to specify a texture in shading, and very
		device-specific.  Its implementation is discouraged. */
static void tpic_tx(char *dummy)
{
	ENTER("tpic_tx");
	END();						/* ignore */
}

#ifndef NOTPIC_EXTENSION

static BOOL is_rotated = FALSE;
static BOOL f_tpic_rt = FALSE;
  
void tpic_rt0(char * s)
{
	f_tpic_rt = TRUE;
	tpic_rt(s);
	f_tpic_rt = FALSE;
}


/* rt x y f		UNOFFICIAL: Rotate everything hereafter around point (x,y) by
angle f.  x and y (integers) are in milli-inches, and f is real in radians.
rotation is not accumulative.  rt x y 0 resets to default (no rotation). */
void tpic_rt(char *s)
{
	static BOOL f_ow;
	TPIC_MI x, y, ttmp;
	double u, v, w, z, dtmp;
	pixelpoint origin;
	int	count, level;
	char *pt[5];

	ENTER("tpic_rt");

	pt[0] = strtok(s, token_sep);
	for(count = 0; pt[count] && count < 5; )
		pt[++count] = strtok(NULL, token_sep);
	switch(count){
		case 1:
			switch(atoi(pt[0])){
				case -1:				/* pop */
					restore_rot_matrix();
					level = add_rot_level(-1);
					goto chk;
				case  0:				/* restore */
					goto restore;
				case  1:				/* push */
					save_rot_matrix();
					add_rot_level(1);
					break;
				case  2:				/* overwrite */
					f_ow = f_gow;
					f_gow = FALSE;
					break;
				case  3:				/* merge */
					f_ow = f_gow;
					f_gow = TRUE;
					break;
				case 4:					/* recover overwrite/merge */
					f_gow = f_ow;
					break;
			}
			return;
		case 4:							/* matrix */
			w = atof(pt[2]);
			z = atof(pt[3]);
		case 2:							/* scale */
			u = atof(pt[0]);
			v = atof(pt[1]);
			if(f_tpic_tate){
				dtmp = u;
				u = v;
				v = dtmp;
				w = dtmp;
				z = w;
				w = -dtmp;
			}
			if(u == 1 && v == 1){
				if(count == 2 || w == 0 && z == 0)
					goto restore;
			}
			x = y = 0;
			break;
		case 3:							/* rotate */
		case 5:
			x = (TPIC_MI) atol(pt[0]);
			y = (TPIC_MI) atol(pt[1]);
			w = atof(pt[2]);
			if(f_tpic_tate){
				ttmp = x;
				x = -y;
				y = ttmp;
			}
			if(w == 0){
restore:		restore_rot_matrix();
				level = add_rot_level(0);
chk:			if(!level && !get_rot_matrix(-2)){
					alter_bitblt_rt(is_rotated = FALSE);
					return;
				}
			}
			break;
	}
	if(f_tpic_rt)
		restore_rot_matrix();
	get_current();						/* get current point */
	origin = relmi_to_abs(x, y);
	switch(count){
		case 2:
			set_scl_matrix(origin.x, origin.y, u, v);
			break;
		case 3:
			set_rot_matrix(origin.x, origin.y, w);
			break;
		case 4:
			set_affine_matrix(origin.x, origin.y, u, w, z, v);
			break;
		case 5:
			set_lrot_matrix(origin.x, origin.y, w);
			break;
	}
#if 0
	if(is_rotated) {					/* debugged by 松田(3 lines) */
			alter_bitblt_rt(FALSE); 	/* 一旦通常の trimming に戻す */
	}
#endif
	alter_bitblt_rt(is_rotated = TRUE);	/* rotation on */
	END();
}

#endif

/* general functions for raster graphics */
/* See D. F. Rogers, "Procedural Elements for Computer Graphics," McGraw-Hill,
New York (1985)(セイコー訳「実践コンピュータグラフィックス」日刊工業新聞社) */

static BOOL shade_on = FALSE;
static BOOL is_closed = FALSE;
static double grayscale = 0.5;

void set_grayscale(double gray)
{
	ENTER("set_grayscale");
	shade_on = TRUE;
	grayscale = gray;
	END();
}

static double get_grayscale(void)
{
	ENTER("get_grayscale");
	if (shade_on == FALSE) RETURN(-1);
	RETURN(grayscale);
}

static void flush_line(void)
{
	int i, n;
	pixelpoint p1, p2;
	BOOL mask_head, mask_tail;	/* flags for masking ends of a thick line */
	BOOL is_discrete;

	ENTER("flush_line");

	if ((n = get_path_n()) >= 2) {	/* enough points, Okay */
		is_closed = is_path_closed();
		is_discrete = is_visible_discrete();
		p1 = pop_path();
		mask_head = !is_closed;	/* if closed, do not mask */
		for (i = 1; i < n; i++) {
			p2 = pop_path();
#ifndef TPIC_OLD
			if (is_discrete)
				adjust_visible_discrete(p1, p2);	/* renew */
#endif
			mask_tail = (i == n - 1) ? (!is_closed) : FALSE;
			rasterize_line(p1, p2, mask_head, mask_tail);
			p1 = p2;
			mask_head = FALSE;
		}
		flush_raster();
	}
	END();
}

#define SPL_RESOLUTION 10

/* Here's contribution by A. Nakabayashi (Thanks!) */

static void flush_spline(void)
	/* Bezier curve guided by a set of straight lines. */
	/* It begins at the same place, ends at the same place, and */
	/* in between is tangent to the mid-point of each guiding line */
{
	int i, j, n;
	pixelpoint p1, p2, p3, lastp, newp;
	double w, t1, t2, t3;

	ENTER("flush_spline");

#ifndef	TPIC_NSPL
	if ((f_tpic_turn_on&3) == 2){
		flush_spline_old();
		return;
	}
#endif
	if ((n = get_path_n()) >= 3) {	/* enough points, Okay */
		is_closed = is_path_closed();
		p3 = pop_path();
		p2 = pop_path();
		lastp.x = (PIXEL)((p2.x + p3.x) / 2 + 0.5);
		lastp.y = (PIXEL)((p2.y + p3.y) / 2 + 0.5);
		rasterize_line(p3, lastp, !is_closed, FALSE);	/* head end */
		/* if closed, do not mask */
		for (i = 1; i < n - 1; i++) {
			p1 = pop_path();
			for (j = 0; j < SPL_RESOLUTION; j++) {
				w = (double)(j + 1) / SPL_RESOLUTION;
				t1 = 0.5 * w * w;
				w -= 0.5;
				t2 = 0.75 - w * w;
				w -= 0.5;
				t3 = 0.5 * w * w;
				newp.x = (PIXEL)(t1 * p1.x + t2 * p2.x + t3 * p3.x + 0.5);
				newp.y = (PIXEL)(t1 * p1.y + t2 * p2.y + t3 * p3.y + 0.5);
				rasterize_line(lastp, newp, FALSE, FALSE);
				lastp = newp;
			}
			p3 = p2;
			p2 = p1;
		}
		rasterize_line(lastp, p1, FALSE, !is_closed);	/* tail end */
		flush_raster();
	}

	END();
}

#ifndef	TPIC_NSPL
 /* Here's the old version of spline */

static void flush_spline_old(void)
	/* spline goes through path points */
{
	pixelpoint p1, p2;
	int i, j, n;
	static double tick = 1.0 / SPL_RESOLUTION;
	double t;
	BOOL mask_head, mask_tail;	/* flags for masking ends of a thick line */

	ENTER("flush_spline (old)");

	if ((n = get_path_n()) >= 3) {	/* enough points, Okay */
		is_closed = is_path_closed();
		spl_table();
		t = 0.0;
		p1 = spl_get(t);
		mask_head = !is_closed;	/* if closed, do not mask */
		for (i = 0; i < n - 1; i++) {
			for (j = 0; j < SPL_RESOLUTION; j++) {
				t += tick;
				p2 = spl_get(t);
				mask_tail = (i == n - 2 && j == SPL_RESOLUTION - 1) ?
							(!is_closed) : FALSE;
				rasterize_line(p1, p2, mask_head, mask_tail);
				p1 = p2;
				mask_head = FALSE;
			}
		}
		flush_raster();
		spl_clean_table();
	}
	flush_path();
	END();
}

static void tpic_bz(char *s)
{
	f_tpic_turn_on = (atoi(strtok(s, token_sep)) == 0)?2:1;
}

#endif /* end of ifndef TPIC_NSPL */

static void flush_arc(pixelpoint origin, pixelpoint radius, 
	double start, double end)
{
	ENTER("flush_arc");

	is_closed = (fabs(end - start) >= TWO_PAI_LESS) ? TRUE : FALSE;
	rasterize_arc(origin, radius, start, end);
	flush_raster();

	END();
}

static void flush_raster(void)
{
	ENTER("flush_raster");

	if (shade_on && is_closed) {
		raster_fill();
		shade_on = FALSE;
	}
	raster_draw();
	clean_bucket();
	END();
}

#define sign(A) ( ((A) > 0) ? 1 : (((A) < 0) ? -1 : 0) )
#define swap(x, y, tmp) tmp = x, x = y, y = tmp

void rasterize_line(pixelpoint start, pixelpoint end, BOOL mask_head,
					BOOL mask_tail)
	/* get pixels representing a straight line by Bresenham method */
	/* Two BOOLeans are for masking ends of thick lines; otherwise */
	/* they would be a little bit too long */
	/* Note. edge selection is only necessary for filling; if speed is a
	critical problem, rewrite codes to skip it in drawing-only cases.  */
{
	PIXEL x, y, temp, *x_ptr, *y_ptr, y_prev, thick;
	SIGNED_PIXEL dx, dy, sx, sy, tems, e, i;
	BOOL edge, visible, down, f_visi;
	pixelpoint st, ed;

	ENTER("rasterize_line");

	x = start.x;
	y = start.y;
	dx = end.x - start.x;
	dy = end.y - start.y;
	sx = sign(dx);
	sy = sign(dy);
	dx = abs(dx);
	dy = abs(dy);

	down = (sy < 0) ? TRUE : FALSE;
	if (dy > dx) {				/* swap x and y */
		swap(dx, dy, tems);
		swap(sx, sy, tems);
		swap(x, y, temp);
		x_ptr = &y;				/* ptrs to actual x and y */
		y_ptr = &x;
		thick = get_h_width();
	}
	else {
		x_ptr = &x;
		y_ptr = &y;
		thick = get_v_width();
	}

	if (dx == 0) {
		/* start point == end point. just put one point and exit */
		push_point(*x_ptr, *y_ptr, FALSE, is_visible());
		END();
	}

	edge = down;
	/* if line goes up, do not count start point as edge; if goes down,
	count it.  this assures correct counting of vertices as fill edges:
	bottom vertex, 0; top vertex, 2; in-between vertex 1. */

	f_visi = FALSE;
	e = 2 * dy - dx;
	for (i = 0; i < dx; i++) {
		visible = is_visible();	/* ask 'em anyway */
		if (i == 0 && !mask_head && (dx == 0 || dy == 0)) {
			push_point(*x_ptr, *y_ptr, edge, visible);
		} else {
			push_point(*x_ptr, *y_ptr, edge, FALSE);
		}
		if (visible && !f_visi) {
			st.x = *x_ptr;
			st.y = *y_ptr;
		}
		if (!visible && f_visi) {
			ed.x = *x_ptr;
			ed.y = *y_ptr;
			draw_line(st, ed, thick);
		}
		f_visi = visible;
		y_prev = *y_ptr;
		while (e >= 0) {
			y += sy;
			e -= 2 * dx;
		}
		x += sx;
		e += 2 * dy;
		edge = (*y_ptr != y_prev) ? TRUE : FALSE;
		/* half scanline. if point goes over y + 1/2, count it as edge */
	}
	if (f_visi) {
		ed.x = *x_ptr;
		ed.y = *y_ptr;
		draw_line(st, ed, thick);
	}
	visible = is_visible();		/* ask 'em anyway */
	if (mask_tail || (dx != 0 && dy != 0)) {
		visible = FALSE;
	}
	push_point(*x_ptr, *y_ptr, edge, visible);	/* last point */

	if (down)
		push_point(*x_ptr, *y_ptr, TRUE, FALSE);
	/* tricky: if line goes down, add an extra vertex on the last point to
	assure correct fill-edge counting; ignore bottom, count top */

	END();
}

/* 太さを持った直線の rasterize */
static void draw_line(pixelpoint st, pixelpoint ed, PIXEL thick)
{
	SIGNED_PIXEL xd, yd, xw, yh, i, tmp;
	SIGNED_PIXEL y0, y1, y2, y3, x0, x1, w;
	BOOL turn = FALSE;
	long d;
	ENTER("draw_line");

#ifdef	WIN32G
	if (f_ttout){
		WriteTTRule(st.x, st.y, ed.x, ed.y, thick);
		END();
	}
#endif
	xd = ed.x - st.x;
	yd = ed.y - st.y;
	if(max(st.y, ed.y) < -thick || min(st.y, ed.y) > get_vmax() + thick) {
#ifndef NOTPIC_EXTENSION
		if (!is_rotated)
#endif
			END();
	}	/* trimming */
	if (abs(xd) <= 1 && abs(yd) <= 1) {
		write_point(st.x, st.y);
		END();
	}
	if (st.x == ed.x) {
		write_rule(st.x - thick/2, max(st.y, ed.y), abs(yd) + 1, thick);
		END();
	}
	if (st.y == ed.y) {
		write_rule(min(st.x, ed.x), st.y + thick/2, thick, abs(xd) + 1);
		END();
	}
	if (abs(xd) > abs(yd)) {	/* x 軸と y 軸をひっくり返す */
		swap(xd, yd, tmp);
		swap(st.x, st.y, tmp);
		swap(ed.x, ed.y, tmp);
		turn = TRUE;
	}
	if (st.y > ed.y) {	/* 上から下の直線とみなす */
		swap(st.x, ed.x, tmp);
		swap(st.y, ed.y, tmp);
		xd = -xd;
		yd = -yd;
	}
	d = hypot(xd, yd) * 1000;
	/* x 軸を切り取る幅 */
	xw = ((long)thick * d + labs(yd) * 500) / (labs(yd) * 1000);
	if (xw <= 0) xw = 1;
	yh = ((long)thick * labs(xd) * 1000) / d;	/* 切り口の高さ */
	y0 = st.y - yh / 2;
	y1 = y0 + yh;
	y2 = ed.y - yh / 2;
	y3 = y2 + yh;
	for(i = y0; i <= y3; i++) {
		d = (long)(i - st.y) * xd;
		if (d >= 0) {
			x0 = st.x - xw/2 + (d + (yd - 1) / 2) / yd;
		} else {
			x0 = st.x - xw/2 + (d - yd / 2) / yd;
		}
		x1 = x0 + xw;
		if(i < y1) {
			w = xw * (i - y0 + 1) / (yh + 1);
			if(xd > 0) {
				x0 += xw - w;
			} else {
				x1 -= xw - w;
			}
		}
		if(i > y2) {
			w = xw * (y3 - i + 1) / (yh + 1);
			if(xd < 0) {
				x0 += xw - w;
			} else {
				x1 -= xw - w;
			}
		}
		if (x0 < x1) {
			if (turn) {
				write_rule(i, x1 - 1, x1 - x0, 1);
			} else {
				write_rule(x0, i, 1, x1 - x0);
			}
		}
	}
	END();
}

static void rasterize_arc(pixelpoint origin, pixelpoint radius, double start,
				   double end)
	/* get pixels representing an arc by Bresenham method */
{
	pixelpoint top, bottom, init[4], fin[4], temp_init, temp_fin;
	PIXEL rx, ry;
	double sx, sy, ex, ey;
	BOOL is_full, is_top_to_tail = FALSE;
	int i, j, plane_init, plane_fin;

	ENTER("rasterize_arc");

	rx = radius.x;
	ry = radius.y;
	/* start on +y axis, down clockwise to +x axis (1st plane) */
	top.x = 0;
	top.y = ry;
	bottom.x = rx;
	bottom.y = 0;

	is_full = (fabs(end - start) >= TWO_PAI_LESS) ? TRUE : FALSE;
	if (is_full) {				/* full circle or ellipse */
		rasterize_quadarc(origin, radius, top, bottom, 4);	/* all plane */
	}
	else {						/* arc */

		/* fill by default (full), clockwise */
		init[0] = fin[1] = init[2] = fin[3] = bottom;
		fin[0] = init[1] = fin[2] = init[3] = top;

		sx = rx * cos(start);
		sy = ry * sin(start);
		/*		plane_init = (int)(floor(start * 4 /
			((start > 0) ? TWO_PAI_LESS : TWO_PAI_MORE)) * 1.01);
		plane_init = (unsigned int)plane_init % 4; */
		plane_init = (sy > 0) ? ((sx > 0) ? 0 : 1) :
			((sy < 0) ? ((sx < 0) ? 2 : 3) : ((sx > 0) ? 0 : 2));
		/* bound belongs to the latter region, anti-clockwise */
		ex = rx * cos(end);
		ey = ry * sin(end);
		/*		plane_fin = (int)(ceil(end * 4 /
			((end > 0) ? TWO_PAI_MORE : TWO_PAI_LESS)) * 1.01) - 1;
		plane_fin = (unsigned int)plane_fin % 4; */
		plane_fin = (ey > 0) ? ((ex >= 0) ? 0 : 1) :
			((ey < 0) ? ((ex <= 0) ? 2 : 3) : ((ex > 0) ? 3 : 1));
		/* bound belongs to the former region, anti-clockwise */

		if (plane_init == plane_fin &&
			((plane_init <= 1) ? (sx < ex) : (sx > ex))) {
			/* 0-1-2-3-0 etc */
			is_top_to_tail = TRUE;
			temp_init = init[plane_init];	/* keep */
			temp_fin = fin[plane_fin];	/* keep */
		}
		init[plane_init].x = (PIXEL)(fabs(sx) + 0.5);	/* round 'em */
		init[plane_init].y = (PIXEL)(fabs(sy) + 0.5);	/* by OkI (Thanks!) */
		fin[plane_fin].x = (PIXEL)(fabs(ex) + 0.5);
		fin[plane_fin].y = (PIXEL)(fabs(ey) + 0.5);

		i = plane_init;

		if (is_top_to_tail) {	/* 0-1-2-3-0 etc */
			rasterize_quadarc(origin, radius, init[i], temp_fin, i);
			init[i] = temp_init;
			if (++i > 3)
				i = 0;
		}
		for (j = 0; j < 4; j++) {
			rasterize_quadarc(origin, radius, init[i], fin[i], i);
			if (i == plane_fin)
				break;
			if (++i > 3)
				i = 0;
		}

	}

	END();
}

static void rasterize_quadarc(pixelpoint origin, pixelpoint radius, 
				pixelpoint init, pixelpoint fin, int plane)
	/* get pixels representing 1/4 arc by clockwise Bresenham method */
	/* init and fin points should be in 1st plane (x > 0, y > 0) */
	/* if plane > 3, all planes are selected */
	/* Note. edge selection is only necessary for filling; if speed is a
	critical problem, rewrite codes to skip it in drawing-only cases.  */
{
	/*	SIGNED_PIXEL x, y, del, sdel, lim_x, lim_y, rx, ry, rx2, ry2; */
	SIGNED_PIXEL xx, yy;
	long x, y, lim_x, lim_y, rx, ry;
	double del, sdel, rx2, ry2;

	/* (x, y), real pixel; (f(x) = x * ry, g(y) = y * rx), transformed point */
	/* transformed circle: f(x)^2 + g(y)^2 = (rx * ry)^2 */
	BOOL active_plane[4], edge, visible;
	static SIGNED_PIXEL tr_mat[4][2] =
	{
		{ 1,  1},
		{-1,  1},
		{-1, -1},
		{ 1, -1}
	};

	/* sign of x and y for each plane */
	int i;

	ENTER("rasterize_quadarc");

	rx = radius.x;
	ry = radius.y;
	rx2 = rx * rx;
	ry2 = ry * ry;

	if (plane < 0 || plane > 3) {
		for (i = 0; i < 4; i++)
			active_plane[i] = TRUE;	/* all planes */
	}
	else {						/* only one plane */
		for (i = 0; i < 4; i++)
			active_plane[i] = FALSE;
		active_plane[plane] = TRUE;
	}

	if (init.y > fin.y) {		/* 1st plane, clockwise: y decreasing ... */
		x = init.x;
		y = init.y;
		lim_x = fin.x;
		lim_y = fin.y;
	}
	else {
		x = fin.x;
		y = fin.y;
		lim_x = init.x;
		lim_y = init.y;
	}
	del = ry2 * (x + 1) * (x + 1) + rx2 * ((y - 1) * (y - 1) - ry2);
	/* del = f(x + 1)^2 + g(y - 1)^2 - rxy^2 */

	edge = FALSE;				/* do not count start point as edge */
	while (TRUE) {
		if( y < lim_y || x > lim_x )	/* bug fix by Otobe Jan/2004 */
			break;
		visible = is_visible();
		for (i = 0; i < 4; i++) {
			if (active_plane[i]) {
				xx = (PIXEL)(origin.x + tr_mat[i][0] * x);
				yy = (PIXEL)(origin.y + tr_mat[i][1] * y);
				push_point(xx, yy, edge, FALSE);
				if (visible) write_point(xx, yy);
			}
		}
		if (y <= lim_y && x == lim_x)	/* related to bug fix by Otobe Jan/2004 */
			break;
		if (del < 0) {			/* right-down(RD) point < r */
			sdel = 2 * del - rx2 * (1 - 2 * y);
			/* sdel = f(x + 1)^2 + g(y)^2 - rxy^2
				+ f(x + 1)^2 + g(y - 1)^2 - rxy^2 */
			if (sdel <= 0) {	/* select right(R) point */
				x++;
				del += ry2 * (1 + 2 * x);
				edge = FALSE;	/* not edge */
				continue;
			}
		}
		else if (del > 0) {		/* RD point > r */
			sdel = 2 * del - ry2 * (1 + 2 * x);
			/* sdel = f(x + 1)^2 + g(y - 1)^2 - rxy^2
				+ f(x)^2 + g(y - 1)^2 - rxy^2 */
			if (sdel > 0) {		/* select down(D) point */
				y--;
				del += rx2 * (1 - 2 * y);
				edge = TRUE;	/* edge */
				continue;
			}
		}
		x++;					/* select RD point */
		y--;
		del += ry2 * (1 + 2 * x) + rx2 * (1 - 2 * y);
		edge = TRUE;
		/* half scanline. if point goes over y + 1/2, count it as edge */
	}

	for (i = 0; i < 2; i++) {
		/* tricky: for (x, y) and (-x, y) planes, add extra vertices to assure
		correct fill-edge counting; ignore bottom, count top */
		if (active_plane[i]) {
			push_point((PIXEL)(origin.x + tr_mat[i][0] * init.x),
					(PIXEL)(origin.y + tr_mat[i][1] * init.y), TRUE, FALSE);
			push_point((PIXEL)(origin.x + tr_mat[i][0] * fin.x),
					 (PIXEL)(origin.y + tr_mat[i][1] * fin.y), TRUE, FALSE);
		}
	}

	END();
}

/* visible/invisible functions */

#ifndef TPIC_OLD	/* Here's the new version of dashed and dotted lines */

static PIXEL discrete_visible, discrete_invisible;

static void set_visible_discrete(PIXEL visible, PIXEL invisible)
{
	ENTER("set_visible_discrete");
	init_visible(TRUE);
	discrete_visible = visible;
	discrete_invisible = invisible;
	END();
}

static void adjust_visible_discrete(pixelpoint start, pixelpoint end)
	/* set discrete visible pattern for a dashed or dotted line */
	/* the dots will be that far apart. the dashes will be that long, and */
	/* the intervening spaces will be as close as possible to that size */
{
	PIXEL pixels, period, black, white, residue, adjust, exdot;
	int repeat, extra, exhalf;
	double dx, dy, dist;

	ENTER("adjust_visible_discrete");
	dx = start.x - end.x;
	dy = start.y - end.y;
	dx = abs(dx);
	dy = abs(dy);
	pixels = (int)max(dx, dy) + 1;	/* total count */
	dist = sqrt(dx * dx + dy * dy) + 1;
	period = (PIXEL)(discrete_visible + discrete_invisible) * (pixels / dist);
	period = max(period, 1);	/* black + white count */

	init_visible(TRUE);
	black = (PIXEL)(discrete_visible * (pixels / dist));	/* black count */
	if (discrete_visible > 0)
		black = max(black, 1);	/* keep non-zero */
	white = period - black;
	repeat = (int)((pixels - black) / period);
	residue = pixels - black - repeat * period;
	if (residue > black) {	/* enough, put one more period and shrink white */
		repeat++;
		residue -= period;
	}
	if (repeat > 0) {	/* long enough */
		adjust = (PIXEL)(residue / repeat);	/* more white to adjust */
		extra = (int)(residue - repeat * adjust);	/* further more white */
		exdot = (PIXEL)((extra >= 0) ? 1 : -1);
		extra = abs(extra);
		exhalf = extra / 2;	/* sandwich by two exdot regions */
		append_visible(black, white + adjust + exdot, exhalf);
		append_visible(black, white + adjust, repeat - extra);
		append_visible(black, white + adjust + exdot, extra - exhalf);
	}
	append_visible(pixels, 0, 1);	/* the last one (rest) */

	END();
}

#endif /* end of ifndef TPIC_OLD */

void set_visible(PIXEL visible, PIXEL invisible)
	/* set a simple pattern (one sequence, continuous) for visibility */
{
	ENTER("set_visible");
	if (invisible == 0)			/* just reducing overheads in is_visible() */
		visible = 1000;
	else if (visible == 0)
		invisible = 1000;

	init_visible(FALSE);		/* continuous */
	append_visible(visible, invisible, 1000);
	END();
}

/* visible/invisible primitives */

#define VIS_PATTERN_SIZE 20	/* maximum */

static BOOL is_vis_discrete = FALSE;	/* for dashed and dotted lines */
typedef struct {
	PIXEL pixels[2];
	int repeat;
} vis_sequence;

static vis_sequence vis_pattern[VIS_PATTERN_SIZE];
static int vis_repeat, vis_index, vis_index_max;
static PIXEL vis_count, vis_turn;
static BOOL vis_flag;

static void init_visible(BOOL is_discrete)
{	   /* initialize visible pattern */
	ENTER("init_visible");
	is_vis_discrete = is_discrete;
	vis_repeat = vis_index = vis_index_max = 0;
	vis_count = vis_turn = 0;
	vis_flag = TRUE;
	END();
}

static void append_visible(PIXEL visible, PIXEL invisible, int repeat)
	/* append a period for visibility.  repeat this pattern <repeat> times */
{
	ENTER("append_visible");
	if (vis_index_max >= VIS_PATTERN_SIZE) {
		error(WARNING, "tpic: visible pattern exceeds max ... v(^^;)v");
		END();
	}

	if (repeat <= 0)
		END();					/* ignore */
	vis_pattern[vis_index_max].pixels[TRUE] = visible;
	vis_pattern[vis_index_max].pixels[FALSE] = invisible;
	vis_pattern[vis_index_max].repeat = repeat;
	vis_index_max++;
	if (vis_index_max == 1)
		vis_turn = visible;		/* first turn */

	END();
}

static BOOL is_visible(void)
{	   /* ret visible status */
	ENTER("is_visible");

	if (++vis_count > vis_turn) {
		vis_count = 1;			/* reset and count one */
		do {
			vis_flag = !vis_flag;	/* flip */
			if (vis_flag && ++vis_repeat >= vis_pattern[vis_index].repeat) {
				vis_repeat = 0;	/* reset */
				if (++vis_index >= vis_index_max)
					vis_index = 0;
			}
			vis_turn = vis_pattern[vis_index].pixels[vis_flag];
		} while (vis_turn <= 0);/* go on for non-zero turn */
	}

	RETURN(vis_flag);
}

static BOOL is_visible_discrete(void)
{
	return (is_vis_discrete);
}

/* raster draw/fill functions */

void raster_draw(void)
{	   /* draw pixels defined in stack */
	PIXEL y, ymin, ymax;
	pixel_link *xlink;

	ENTER("raster_draw");
	ymin = get_bucket_min();
	ymax = get_bucket_max();

	for (y = ymin; y <= ymax; y++) {
		xlink = *get_bucket(y);

		while (xlink != NULL) {
			if (xlink->status & VISIBLE)
				write_point(xlink->p, y);
			xlink = xlink->next;
		}
	}
	END();
}

static PIXEL h_width, h_half, v_width, v_half;	/* pensize */

void set_pensize(TPIC_MI pensize)
{
	pixelpoint p;

	ENTER("set_pensize");

	if (pensize == 0) {			/* zero-width */
		h_half = h_width = v_half = v_width = 0;
	}
	else {						/* minimum width is unity */
		p = mi_to_pixel(pensize, pensize);
		h_half = (h_width = max(p.x, 1)) / 2;
		v_half = (v_width = max(p.y, 1)) / 2;
	}

	END();
}

static PIXEL get_h_width(void)
{	   /* for line thickness */
	RETURN(h_width);
}

static PIXEL get_v_width(void)
{	   /* for line thickness */
	RETURN(v_width);
}

static void write_point(PIXEL x, PIXEL y)
{	   /* write on a bitmap */
	ENTER("write_point");

	write_rule(x - h_half, y + v_half, v_width, h_width);
	/* left-bottom point should be specified */
	/* x is not trimmed. y is trimmed if not rotated, not trimmed if rotated.
	i.e., write_rule() should trim x always, and trim (rotated) y correctly */
	/* caution: zero-width should be handled safely in write_rule() */
	/* just a rule is written as a point. this causes some overheads.
	also, a circle instead of a rule would be better, but ..... (^^;) */

	END();
}

void raster_fill(void)
	/* fill the area defined in stack by ordered edge-list algorithm */
	/* edges should be properly selected in advance */
{
	PIXEL y, x[2], ymin, ymax;
	SIGNED_PIXEL dx;
	pixel_link *xlink;
	int i;
#ifdef	WIN32G
	int old_ttout;
#endif

	ENTER("raster_fill");

#ifdef	WIN32G
	if((old_ttout = f_ttout) != 0){
		set_black(1);
		init_tile((grayscale<=0)?1:grayscale);
		clear_buf(bitmap_buf_pointer);
	}else
#endif
		init_tile(grayscale);		/* select tiling pattern */
	ymin = get_bucket_min();
	ymax = get_bucket_max();

	for (y = ymin; y <= ymax; y++) {
		xlink = *get_bucket(y);

		while (xlink != NULL && !(xlink->status & FILL_EDGE))
			xlink = xlink->next;	/* skip non-edge points */

		i = 0;
		while (xlink != NULL) {	/* search pairs (xmin, xmax) */

			x[i++] = xlink->p;	/* edge found */
			xlink = xlink->next;

			if (i < 2)
				continue;		/* go on to get a pair */

			/* pair found. identical and adjacent points are also counted
			as a pair */
			/* for (x = xmin + 1; x < xmax; x++) put_pixel(x, y); */
			/* if ((dx = x[1] - x[0] - 1) > 0)
				write_tile(x[0] + 1, y, 1, dx); */ /* inside only */

			if ((dx = x[1] - x[0] + 1) > 0)
				write_tile(x[0], y, 1, dx);	/* inside + edges */
				/* Here, x is not trimmed; y is trimmed if not rotated, 
				not trimmed if rotated. thus, write_tile() should trim x 
				always, and trim (rotated) y correctly */

			i = 0;				/* reset and search a new pair */
		}

		/* if not a pair, just ignore it */

	}
	fin_tile();					/* recover black tile */
#ifdef	WIN32G
	if(old_ttout){
		set_black(-1);
		PrintBitmapBuf((grayscale<=0)?1:0);
	}
#endif
	END();
}

/* functions to push paths and calc spline interpolation */

#define PATH_ARRAY_SIZE 100	/* cluster size (not maximum size) */

static PIXEL *path_x = NULL, *path_y = NULL;
static int path_n = 0;

static void push_path(pixelpoint p)
{	   /* push path p */
	static int array_size = 0;

	ENTER("push_path");

	if (path_n > 0 &&
		p.x == path_x[path_n - 1] && p.y == path_y[path_n - 1])
		END();
	/* ignore the same point. this is necessary for spline interpolation */

	if (path_n >= array_size) {	/* not enough, re-allocate */
		array_size += PATH_ARRAY_SIZE;
		if ((path_x = (PIXEL *)realloc(path_x, sizeof(PIXEL) * array_size))
			== NULL ||
			 (path_y = (PIXEL *)realloc(path_y, sizeof(PIXEL) * array_size))
			== NULL) {
			error(NO_MEMORY, "tpic: path stack full ... v(^^;)v");
			END();
		}
	}
	path_x[path_n] = p.x;
	path_y[path_n++] = p.y;
	END();
}

static pixelpoint pop_path(void)
{	   /* just take out */
	static pixelpoint p;

	ENTER("pop_path");

	if (path_n == 0) {			/* empty */
		error(WARNING, "tpic: path stack empty ... v(^^;)v");
		p.x = p.y = 0;
	}
	else {
		p.x = path_x[--path_n];
		p.y = path_y[path_n];
	}

	RETURN(p);
}

static void flush_path(void)
{	   /* flush path */
	ENTER("flush_path");

	path_n = 0;

/*	array_size = 0;
	Free(path_x);
	Free(path_y);
	path_x = path_y = NULL *//* not necessary ... */

	END();
}

static int get_path_n(void)
{
	ENTER("get_path_n");
	RETURN(path_n);
}

static void set_path_n(int n)
{
	ENTER("set_path_n");
	path_n = n;
	END();
}

static BOOL is_path_closed(void)
{
	BOOL flag;

	ENTER("is_path_closed");

	if (path_n < 4)
		RETURN(FALSE);

	flag = (path_x[0] == path_x[path_n - 1] &&
			path_y[0] == path_y[path_n - 1]) ? TRUE : FALSE;
	/* top point == last point */

	RETURN(flag);
}

#ifndef TPIC_NSPL	/* These routines are for the old version of spline */

static PIXEL *spl_t;	/* intermediate variable: sum of distances */
static double *spl_x;	/* 1/6 of Spline 2nd derivatives */
static double *spl_y;

static void spl_table(void)
	/* make tables for a pair of natural or periodic cubic splines:
	x = f(t), y = g(t); t = sum[sqrt(dx * dx + dy * dy)] */
{
	int i;
	double dx, dy;
	PIXEL distance, sum;

	ENTER("spl_table");

	if ((spl_t = (PIXEL *)marea(sizeof(PIXEL) * path_n)) == NULL ||
			(spl_x = (double *)marea(sizeof(double) * path_n)) == NULL ||
			(spl_y = (double *)marea(sizeof(double) * path_n)) == NULL)
			END();

	spl_t[0] = sum = (PIXEL)0;
	for (i = 1; i < path_n; i++) {
		dx = path_x[i] - path_x[i - 1];
		dy = path_y[i] - path_y[i - 1];
		distance = (PIXEL)sqrt(dx * dx + dy * dy);
		sum += max(distance, 1);
		/* assure that spl_t[i] > spl_t[i - 1], but interpolation may
		look weird, anyway */
		spl_t[i] = sum;			/* intermediate variable */
	}

	/* check if closed. presently identical as is_path_closed() */
	/*	if (path_x[0] == path_x[path_n - 1] &&
		path_y[0] == path_y[path_n - 1]) { */

	if (is_path_closed()) {		/* closed. periodic */
		spl_each_periodic(spl_t, path_x, path_n, spl_x);
		spl_each_periodic(spl_t, path_y, path_n, spl_y);
	}
	else {						/* open. natural */
		spl_each_table(spl_t, path_x, path_n, spl_x);
		spl_each_table(spl_t, path_y, path_n, spl_y);
	}

	END();
}

static void spl_clean_table(void)
{	   /* clean table */
	ENTER("spl_clean_table");
	Free(spl_t);
	Free(spl_x);
	Free(spl_y);
	END();
}

static pixelpoint spl_get(double index)
	/* get an interpolated point between path_?[i] and path_?[i + 1].
	i = (int)index */
{
	int i;
	static pixelpoint p;
	double distance;

	ENTER("spl_get");

	if ((i = (int)index) >= path_n - 1) {
		/* out of range, return end point */
		p.x = path_x[path_n - 1];
		p.y = path_y[path_n - 1];
	}
	else {
		distance = (double)spl_t[i]
			+ (index - (double)i) * (double)(spl_t[i + 1] - spl_t[i]);
		p.x = spl_each_get(spl_t, path_x, path_n, spl_x, distance, i);
		p.y = spl_each_get(spl_t, path_y, path_n, spl_y, distance, i);
	}

	RETURN(p);
}

static void spl_each_table(PIXEL x[], PIXEL y[], int n, double sder[])
	/* make a table for natural cubic spline. n >= 3, x[i] < x[i + 1] */
	/* see: W. H. Press et al., "Numerical Recipes in C," Cambridge Univ.
	Press(1988); 奥村晴彦「C 言語による最新アルゴリズム事典」技術評論社 
	(1991) */
{
	double *h;	/* h[i] = x[i + 1] - x[i] */
	double temp, *b;
	int i;

	ENTER("spl_each_table");

	if ((h = (double *)marea(sizeof(double) * n)) == NULL ||
			(b = (double *)marea(sizeof(double) * n)) == NULL)
			END();

	for (i = 0; i < n - 1; i++) {
		h[i] = (double)(x[i + 1] - x[i]);
		sder[i] = (double)(y[i + 1] - y[i]) / h[i];
	}
	for (i = n - 2; i > 0; i--) {
		b[i] = 2 * (double)(x[i + 1] - x[i - 1]);
		sder[i] -= sder[i - 1];
	}

	sder[0] = sder[n - 1] = 0;
	/* solve tridiagonal equations for sder[1], .., sder[n - 2] */
	for (i = 1; i < n - 2; i++) {
		temp = h[i] / b[i];
		b[i + 1] -= temp * h[i];
		sder[i + 1] -= temp * sder[i];
	}
	sder[n - 2] /= b[n - 2];
	for (i = n - 3; i >= 1; i--)
		sder[i] = (sder[i] - h[i] * sder[i + 1]) / b[i];

	Free(h);
	Free(b);
	END();
}

static void spl_each_periodic(PIXEL x[], PIXEL y[], int n, double sder[])
	/* make a table for periodic cubic spline. n >= 3, x[i] < x[i + 1],
	y[0] = y[n - 1] */
{
	double *h;	/* h[i] = x[i + 1] - x[i] */
	double temp, *b, *raux;
	int i;

	ENTER("spl_each_periodic");

	if ((h = (double *)marea(sizeof(double) * n)) == NULL ||
			(b = (double *)marea(sizeof(double) * n)) == NULL ||
			(raux = (double *)marea(sizeof(double) * n)) == NULL)
			END();

	for (i = 0; i < n - 1; i++) {
		h[i] = (double)(x[i + 1] - x[i]);
		sder[i] = (double)(y[i + 1] - y[i]) / h[i];
	}
	b[n - 1] = 2 * (h[n - 2] + h[0]);	/* periodic boundary condition */
	sder[n - 1] = sder[0] - sder[n - 2];
	for (i = n - 2; i > 0; i--) {
		b[i] = 2 * (double)(x[i + 1] - x[i - 1]);
		sder[i] -= sder[i - 1];
	}

	for (i = 2; i < n - 2; i++)
		raux[i] = 0;			/* auxiliary for periodic cond */
	raux[1] = h[0];
	raux[n - 2] = h[n - 2];
	raux[n - 1] = b[n - 1];

	/* solve tridiagonal equations for both sder and raux (1, .., n - 1) */
	for (i = 1; i < n - 1; i++) {
		temp = h[i] / b[i];
		b[i + 1] -= temp * h[i];
		sder[i + 1] -= temp * sder[i];
		raux[i + 1] -= temp * raux[i];
	}
	sder[0] = sder[n - 1];
	raux[0] = raux[n - 1];
	for (i = n - 3; i >= 0; i--) {
		temp = h[i] / b[i + 1];
		sder[i] -= temp * sder[i + 1];
		raux[i] -= temp * raux[i + 1];
	}
	temp = sder[0] / raux[0];
	sder[0] = sder[n - 1] = temp;
	for (i = 1; i < n - 1; i++)
		sder[i] = (sder[i] - raux[i] * temp) / b[i];

	Free(h);
	Free(b);
	Free(raux);
	END();
}

static double spl_each_get(PIXEL x[], PIXEL y[], int n, double sder[],
					double xint, int i)
	/* get an interpolated value yint. x[i] < xint < x[i + 1] */
{
	double h, d, yint;

	ENTER("spl_each_get");

	if (i < 0 || i >= n - 1) {
		error(WARNING, "tpic: spline interpolation out of range ...v(:_;)v");
		RETURN(0.0);
	}
	h = (double)(x[i + 1] - x[i]);
	d = xint - (double)x[i];
	/*	yint = (double)y[i] + (double)(y[i + 1] - y[i]) * d / h
		- (1.0 / 6.0) * (h - d)
		* ((h + d) * sder[i + 1] + (2 * h - d) * sder[i]) * d / h; */
	yint = ((double)(y[i + 1] - y[i])
		- (h - d) * ((h + d) * sder[i + 1] + (2 * h - d) * sder[i])) * d / h
		+ (double)y[i];

	RETURN(yint);
}

#endif /* TPIC_NSPL */

/* function to push pixel points */

static void push_point(PIXEL x, PIXEL y, BOOL fill_edge, BOOL visible)
	/* push x, put and sort it in y-bucket */
	/* Note. y-bucket sort is only necessary for filling; if speed is a 
	critical problem, rewrite codes to skip it in drawing-only cases.  */
{
	pixel_link *x_ptr, **y_bucket_ptr, *ptr;

	ENTER("push_point");

	if ((y_bucket_ptr = get_bucket(y)) == NULL)
		END();					/* out of range */
	if ((x_ptr = push_pixel(x)) == NULL)
		END();					/* memory error */

	x_ptr->status = (fill_edge) ? FILL_EDGE : 0;
	/* used for filling as edges */
	if (visible)
		x_ptr->status |= VISIBLE;

	if ((ptr = *y_bucket_ptr) == NULL || !fill_edge)
		goto link_it;
	/* if bucket is empty or it is not edge, just put it on the top.
	otherwise, search for proper position */

	while (!(ptr->status & FILL_EDGE)) {	/* skip non-edge points */
		y_bucket_ptr = &(ptr->next);	/* ptr to addr of next ptr */
		ptr = *y_bucket_ptr;	/* next ptr itself */
		if (ptr == NULL)
			goto link_it;		/* tail */
	}
	while (x > ptr->p) {
		/* x sort. simple but enough, mostly 2 points */
		y_bucket_ptr = &(ptr->next);	/* ptr to addr of next ptr */
		ptr = *y_bucket_ptr;
		if (ptr == NULL)
			break;				/* tail */
	}

	/* resulting sequence:
	y-bucket top -> non-edges -> edges (x sorted) -> NULL */

  link_it:
	x_ptr->next = ptr;
	*y_bucket_ptr = x_ptr;

	END();
}

/* y-bucket functions */

static pixel_link **bucket = NULL;	/* y-bucket */
static PIXEL vmax;	/* bitmap height = number of buckets */
static PIXEL bucket_min, bucket_max;	/* span of buckets being used */

static void init_bucket(PIXEL buf_height)
{	   /* make buckets */
	static int old_height;

	ENTER("init_bucket");

	if (old_height != buf_height){
		Free0(bucket);
		if((old_height = buf_height) != 0)
			bucket = (pixel_link **) marea(sizeof(pixel_link *) * buf_height);
	}
	if((vmax = buf_height)!= 0){
	/* maximum span for clean_bucket(): clean all buckets */
		bucket_min = 0;
		bucket_max = vmax - 1;
		clean_bucket();
	}
	END();
}

#define MAX_PIXEL 32766	/* max value of type int */

void clean_bucket(void)
{
	PIXEL i, imin, imax;

	ENTER("clean_bucket");

	imin = max(bucket_min, 0);
	imax = min(bucket_max, vmax - 1);
	for (i = imin; i <= imax; i++)
		bucket[i] = NULL;		/* make 'em empty */

	bucket_min = MAX_PIXEL;		/* span re-initialize */
	bucket_max = -MAX_PIXEL;

#ifndef NOTPIC_EXTENSION
	if (is_rotated)
		clean_extra_bucket();
#endif
	clear_pixel_stack();		/* clear pixel stack */

	END();
}

static pixel_link **get_bucket(PIXEL y)
	/* ret ptr to y-bucket (ptr to first x_link is in it). bucket_min, max
	are adjusted */
{
	pixel_link **answer;

	ENTER("get_bucket");

	if (y < 0 || y >= vmax) {	/* out of range */
#ifndef NOTPIC_EXTENSION
		if (!is_rotated)
			RETURN(NULL);		/* trim y direction */
		answer = get_extra_bucket(y);	/* no trimming, get bucket anyhow */
#else
		RETURN(NULL);
#endif
	}
	else {
		answer = bucket + y;
	}

	bucket_min = min(bucket_min, y);	/* record span */
	bucket_max = max(bucket_max, y);
	RETURN(answer);
}

static PIXEL get_bucket_min(void)
{
	ENTER("get_bucket_min");
	RETURN(bucket_min);
}

static PIXEL get_bucket_max(void)
{
	ENTER("get_bucket_max");
	RETURN(bucket_max);
}

static PIXEL get_vmax(void)
{
	ENTER("get_vmax");
	RETURN(vmax);
}

#ifndef NOTPIC_EXTENSION
/* rotation-supporting buckets */

#define EXTRA_BUCKET_SIZE 300	/* cluster size (not maximum size) */
typedef struct extra_bucket_st {
	PIXEL y_offset;	/* y of top bucket */
	pixel_link *bucket[EXTRA_BUCKET_SIZE];	/* bucket array */
	struct extra_bucket_st *next;
} extra_bucket_link;

static extra_bucket_link *extra_bucket = NULL;	/* ptr to extra buckets */

static pixel_link **get_extra_bucket(PIXEL y)
	/* ret ptr to out-of-range y-bucket (ptr to first x_link is in it) */
{
	extra_bucket_link **before, *ptr;
	PIXEL y_offset, i;

	ENTER("get_extra_bucket");
	before = &extra_bucket;		/* ptr to var for appending a new cluster */
	ptr = extra_bucket;
	while (ptr != NULL) {
		y_offset = ptr->y_offset;
		if (y >= y_offset && y < y_offset + EXTRA_BUCKET_SIZE)	/* found */
			RETURN(ptr->bucket + (y - y_offset));	/* ret ptr to bucket */
		before = &(ptr->next);
		ptr = ptr->next;
	}

	if ((ptr = (extra_bucket_link *) marea(sizeof(extra_bucket_link)))
		== NULL)
		RETURN(NULL);			/* memory error */
	if (y >= 0) {
		/* vmax + 0 (== offset), 1, 2, .., EXTRA_BUCKET_SIZE - 1 */
		y_offset = y - vmax;
		ptr->y_offset = y_offset - y_offset % EXTRA_BUCKET_SIZE + vmax;
	}
	else {
		/* -EXTRA_BUCKET_SIZE (== offset), .., -2, -1 */
		y_offset = (y + 1) % EXTRA_BUCKET_SIZE + EXTRA_BUCKET_SIZE - 1;
		/* e.g. EBS = 4: y = -1, -2, -3, -4, -5 -> 3, 2, 1, 0, 3 */
		ptr->y_offset = y - y_offset;
	}
	for (i = 0; i < EXTRA_BUCKET_SIZE; i++)
		ptr->bucket[i] = NULL;	/* create empty buckets */
	ptr->next = NULL;			/* tail */
	*before = ptr;				/* link it */
	RETURN(ptr->bucket + (y - ptr->y_offset));	/* ret ptr to bucket */
}

static void clean_extra_bucket(void)
{
	extra_bucket_link *work;

	ENTER("clean_extra_bucket");
	while (extra_bucket != NULL) {
		work = extra_bucket->next;
		Free(extra_bucket);
		extra_bucket = work;
	}
	END();
}

#endif

/* pixel points stack functions */

#define PIXEL_ARRAY_SIZE 300	/* cluster size (not maximum size) */

typedef struct pixel_array_st {
	int number;
	pixel_link array[PIXEL_ARRAY_SIZE];
	struct pixel_array_st *next;
} pixel_array;

static pixel_array *pixel_stack = NULL;

static pixel_link *push_pixel(PIXEL p)
	/* ret link ptr; flag and next ptr are not defined yet */
{
	pixel_array *new;
	pixel_link *link;

	ENTER("push_pixel");
	if (pixel_stack == NULL ||
		pixel_stack->number == PIXEL_ARRAY_SIZE) {	/* make a new array */
		if ((new = (pixel_array *) marea(sizeof(pixel_array))) == NULL)
			RETURN(NULL);		/* memory error */
		new->number = 0;
		new->next = pixel_stack;
		pixel_stack = new;
	}
	link = &(pixel_stack->array[pixel_stack->number++]);
	link->p = p;
	RETURN(link);
}

static void clear_pixel_stack(void)
{
	pixel_array *work;

	ENTER("clear_pixel_stack");

	while (pixel_stack != NULL) {
		work = pixel_stack->next;
		Free(pixel_stack);
		pixel_stack = work;
	}

	END();
}

/* scale conversion functions */

static pixelpoint current;	/* current point */

static void get_current(void)
{	   /* get current point (as pixelpoint) */
	ENTER("get_current");
	current.x = get_current_x();/* get as PIXEL */
	current.y = get_current_y();
	END();
}

/* 1 milli-inch = 72.27 * 65536 / 1000 scaled-points */
#define MI_SP 4736.28672

static pixelpoint mi_to_pixel(TPIC_MI x, TPIC_MI y)
{	   /* milli-inches to pixel */
	static pixelpoint p;

	ENTER("mi_to_pixel");
	p.x = sptopixel((SCALED_PT)(x * MI_SP));
	p.y = vtopixel((SCALED_PT)(y * MI_SP));
	RETURN(p);
}

#ifdef VFTPIC
/* functions to freeze and melt tpic status, for overriding in vfont.c */

/* these are just temp_staffs. (ha-ha) */
static BOOL temp_shade_on;
static BOOL temp_is_closed;
static double temp_grayscale;
static PIXEL temp_h_width, temp_h_half, temp_v_width, temp_v_half;

/* the followings (discrete visible pattern, path, spline, y-bucket,
pixel stack and current position) are currently either unused for overriding
or used immediately in tpic, so that they are unnecessary to be frozen.
Future use is possible; stay tuned */
/*
#ifndef NOTPIC_EXTENSION
static BOOL temp_is_rotated;
#endif
static PIXEL temp_discrete_visible, temp_discrete_invisible;
static BOOL temp_is_vis_discrete;
static vis_sequence temp_vis_pattern[VIS_PATTERN_SIZE];
static int temp_vis_repeat, temp_vis_index, temp_vis_index_max;
static PIXEL temp_vis_count, temp_vis_turn;
static BOOL temp_vis_flag;

static PIXEL *temp_path_x, *temp_path_y;
static int   temp_path_n;

#ifdef TPIC_OLD
static PIXEL  *temp_spl_t;
static double *temp_spl_x;
static double *temp_spl_y;
#endif

static pixel_link **temp_bucket;
static PIXEL temp_vmax;
static PIXEL temp_bucket_min, temp_bucket_max;
#ifndef NOTPIC_EXTENSION
static extra_bucket_link *temp_extra_bucket;
#endif
static pixel_array *temp_pixel_stack;
static pixelpoint temp_current;
*/

void freeze_tpic_status(void)
{
	ENTER("freeze_tpic_status");

	temp_shade_on = shade_on;
	temp_is_closed = is_closed;
	temp_grayscale = grayscale;
	temp_h_width = h_width;
	temp_h_half = h_half;
	temp_v_width = v_width;
	temp_v_half = v_half;
#ifndef NOTPIC_EXTENSION
	if (is_rotated)
		alter_bitblt_rt(FALSE);	/* no rotation in vfont proc */
#endif
	END();
}

void melt_tpic_status(void)
{
	ENTER("melt_tpic_status");

	shade_on = temp_shade_on;
	is_closed = temp_is_closed;
	grayscale = temp_grayscale;
	h_width = temp_h_width;
	h_half = temp_h_half;
	v_width = temp_v_width;
	v_half = temp_v_half;
#ifndef NOTPIC_EXTENSION
	if (is_rotated)
		alter_bitblt_rt(TRUE);	/* recover rotation */
	/* it is assumed that rot matrix is intact in vfont proc */
#endif
	END();
}

#endif /* end of ifdef VFTPIC */

#endif /* end of ifndef NOTPIC */

/* end of all */
