Home | History | Annotate | Line # | Download | only in xditview
      1 /*	$NetBSD: draw.c,v 1.1.1.1 2016/01/13 18:41:49 christos Exp $	*/
      2 
      3 /*
      4  * draw.c
      5  *
      6  * accept dvi function calls and translate to X
      7  */
      8 
      9 #include <X11/Xos.h>
     10 #include <X11/IntrinsicP.h>
     11 #include <X11/StringDefs.h>
     12 #include <stdio.h>
     13 #include <ctype.h>
     14 #include <math.h>
     15 
     16 /* math.h on a Sequent doesn't define M_PI, apparently */
     17 #ifndef M_PI
     18 #define M_PI	3.14159265358979323846
     19 #endif
     20 
     21 #include "DviP.h"
     22 
     23 #define DeviceToX(dw, n) ((int)((n) * (dw)->dvi.scale_factor + .5))
     24 #define XPos(dw) (DeviceToX((dw), (dw)->dvi.state->x - \
     25                   (dw)->dvi.text_device_width) + (dw)->dvi.text_x_width)
     26 #define YPos(dw) (DeviceToX((dw), (dw)->dvi.state->y))
     27 
     28 static int FakeCharacter(DviWidget, char *, int);
     29 
     30 /* font.c */
     31 extern int MaxFontPosition(DviWidget);
     32 
     33 void
     34 HorizontalMove(DviWidget dw, int delta)
     35 {
     36 	dw->dvi.state->x += delta;
     37 }
     38 
     39 void
     40 HorizontalGoto(DviWidget dw, int NewPosition)
     41 {
     42 	dw->dvi.state->x = NewPosition;
     43 }
     44 
     45 void
     46 VerticalMove(DviWidget dw, int delta)
     47 {
     48 	dw->dvi.state->y += delta;
     49 }
     50 
     51 void
     52 VerticalGoto(DviWidget dw, int NewPosition)
     53 {
     54 	dw->dvi.state->y = NewPosition;
     55 }
     56 
     57 void
     58 AdjustCacheDeltas (DviWidget dw)
     59 {
     60 	int extra;
     61 	int nadj;
     62 	int i;
     63 
     64 	nadj = 0;
     65 	extra = DeviceToX(dw, dw->dvi.text_device_width)
     66 		- dw->dvi.text_x_width;
     67 	if (extra == 0)
     68 		return;
     69 	for (i = 0; i <= dw->dvi.cache.index; i++)
     70 		if (dw->dvi.cache.adjustable[i])
     71 			++nadj;
     72 	dw->dvi.text_x_width += extra;
     73 	if (nadj <= 1)
     74 		return;
     75 	for (i = 0; i <= dw->dvi.cache.index; i++)
     76 		if (dw->dvi.cache.adjustable[i]) {
     77 			int x;
     78 			int *deltap;
     79 
     80 			x = extra/nadj;
     81 			deltap = &dw->dvi.cache.cache[i].delta;
     82 #define MIN_DELTA 2
     83 			if (*deltap > 0 && x + *deltap < MIN_DELTA) {
     84 				x = MIN_DELTA - *deltap;
     85 				if (x <= 0)
     86 					*deltap = MIN_DELTA;
     87 				else
     88 					x = 0;
     89 			}
     90 			else
     91 				*deltap += x;
     92 			extra -= x;
     93 			--nadj;
     94 			dw->dvi.cache.adjustable[i] = 0;
     95 		}
     96 }
     97 
     98 void
     99 FlushCharCache (DviWidget dw)
    100 {
    101 	if (dw->dvi.cache.char_index != 0) {
    102 		AdjustCacheDeltas (dw);
    103 		XDrawText (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
    104 			   dw->dvi.cache.start_x, dw->dvi.cache.start_y,
    105 			   dw->dvi.cache.cache, dw->dvi.cache.index + 1);
    106 	}
    107 	dw->dvi.cache.index = 0;
    108 	dw->dvi.cache.max = DVI_TEXT_CACHE_SIZE;
    109 #if 0
    110 	if (dw->dvi.noPolyText)
    111 	    dw->dvi.cache.max = 1;
    112 #endif
    113 	dw->dvi.cache.char_index = 0;
    114 	dw->dvi.cache.cache[0].nchars = 0;
    115 	dw->dvi.cache.start_x = dw->dvi.cache.x	= XPos (dw);
    116 	dw->dvi.cache.start_y = dw->dvi.cache.y = YPos (dw);
    117 }
    118 
    119 void
    120 Newline (DviWidget dw)
    121 {
    122 	FlushCharCache (dw);
    123 	dw->dvi.text_x_width = dw->dvi.text_device_width = 0;
    124 	dw->dvi.word_flag = 0;
    125 }
    126 
    127 void
    128 Word (DviWidget dw)
    129 {
    130 	dw->dvi.word_flag = 1;
    131 }
    132 
    133 #define charWidth(fi,c) (\
    134     (fi)->per_char ?\
    135 	(fi)->per_char[(c) - (fi)->min_char_or_byte2].width\
    136     :\
    137 	(fi)->max_bounds.width\
    138 )
    139 
    140 
    141 static
    142 int charExists (XFontStruct *fi, int c)
    143 {
    144 	XCharStruct *p;
    145 
    146 	/* `c' is always >= 0 */
    147 	if (fi->per_char == NULL
    148 	    || (unsigned int)c < fi->min_char_or_byte2
    149 	    || (unsigned int)c > fi->max_char_or_byte2)
    150 		return 0;
    151 	p = fi->per_char + (c - fi->min_char_or_byte2);
    152 	return (p->lbearing != 0 || p->rbearing != 0 || p->width != 0
    153 		|| p->ascent != 0 || p->descent != 0 || p->attributes != 0);
    154 }
    155 
    156 /* `wid' is in device units */
    157 static void
    158 DoCharacter (DviWidget dw, int c, int wid)
    159 {
    160 	register XFontStruct	*font;
    161 	register XTextItem	*text;
    162 	int	x, y;
    163 
    164 	x = XPos(dw);
    165 	y = YPos(dw);
    166 
    167 	/*
    168 	 * quick and dirty extents calculation:
    169 	 */
    170 	if (!(y + 24 >= dw->dvi.extents.y1
    171 	      && y - 24 <= dw->dvi.extents.y2
    172 #if 0
    173 	      && x + 24 >= dw->dvi.extents.x1
    174 	      && x - 24 <= dw->dvi.extents.x2
    175 #endif
    176 	    ))
    177 		return;
    178 
    179 	if (y != dw->dvi.cache.y
    180 	    || dw->dvi.cache.char_index >= DVI_CHAR_CACHE_SIZE) {
    181 		FlushCharCache (dw);
    182 		x = dw->dvi.cache.x;
    183 		dw->dvi.cache.adjustable[dw->dvi.cache.index] = 0;
    184 	}
    185 	/*
    186 	 * load a new font, if the current block is not empty,
    187 	 * step to the next.
    188 	 */
    189 	if (dw->dvi.cache.font_size != dw->dvi.state->font_size ||
    190 	    dw->dvi.cache.font_number != dw->dvi.state->font_number)
    191 	{
    192 		FlushCharCache (dw);
    193 		x = dw->dvi.cache.x;
    194 		dw->dvi.cache.font_size = dw->dvi.state->font_size;
    195 		dw->dvi.cache.font_number = dw->dvi.state->font_number;
    196 		dw->dvi.cache.font = QueryFont (dw,
    197 						dw->dvi.cache.font_number,
    198 						dw->dvi.cache.font_size);
    199 		if (dw->dvi.cache.cache[dw->dvi.cache.index].nchars != 0) {
    200 			++dw->dvi.cache.index;
    201 			if (dw->dvi.cache.index >= dw->dvi.cache.max)
    202 				FlushCharCache (dw);
    203 			dw->dvi.cache.cache[dw->dvi.cache.index].nchars = 0;
    204 			dw->dvi.cache.adjustable[dw->dvi.cache.index] = 0;
    205 		}
    206 	}
    207 	if (x != dw->dvi.cache.x || dw->dvi.word_flag) {
    208 		if (dw->dvi.cache.cache[dw->dvi.cache.index].nchars != 0) {
    209 			++dw->dvi.cache.index;
    210 			if (dw->dvi.cache.index >= dw->dvi.cache.max)
    211 				FlushCharCache (dw);
    212 			dw->dvi.cache.cache[dw->dvi.cache.index].nchars = 0;
    213 			dw->dvi.cache.adjustable[dw->dvi.cache.index] = 0;
    214 		}
    215 		dw->dvi.cache.adjustable[dw->dvi.cache.index]
    216 			= dw->dvi.word_flag;
    217 		dw->dvi.word_flag = 0;
    218 	}
    219 	font = dw->dvi.cache.font;
    220 	text = &dw->dvi.cache.cache[dw->dvi.cache.index];
    221 	if (text->nchars == 0) {
    222 		text->chars = &dw->dvi.cache.char_cache[dw->dvi.cache.char_index];
    223 		text->delta = x - dw->dvi.cache.x;
    224 		if (font != dw->dvi.font) {
    225 			text->font = font->fid;
    226 			dw->dvi.font = font;
    227 		} else
    228 			text->font = None;
    229 		dw->dvi.cache.x += text->delta;
    230 	}
    231 	if (charExists(font, c)) {
    232 		int w;
    233 		dw->dvi.cache.char_cache[dw->dvi.cache.char_index++] = (char) c;
    234 		++text->nchars;
    235 		w = charWidth(font, c);
    236 		dw->dvi.cache.x += w;
    237 		if (wid != 0) {
    238 			dw->dvi.text_x_width += w;
    239 			dw->dvi.text_device_width += wid;
    240 		}
    241 	}
    242 }
    243 
    244 static
    245 int FindCharWidth (DviWidget dw, char *buf, int *widp)
    246 {
    247 	int maxpos;
    248 	int i;
    249 
    250 	if (dw->dvi.device_font == 0
    251 	    || dw->dvi.state->font_number != dw->dvi.device_font_number) {
    252 		dw->dvi.device_font_number = dw->dvi.state->font_number;
    253 		dw->dvi.device_font
    254 			= QueryDeviceFont (dw, dw->dvi.device_font_number);
    255 	}
    256 	if (dw->dvi.device_font
    257 	    && device_char_width (dw->dvi.device_font,
    258 				  dw->dvi.state->font_size, buf, widp))
    259 		return 1;
    260 
    261 	maxpos = MaxFontPosition (dw);
    262 	for (i = 1; i <= maxpos; i++) {
    263 		DeviceFont *f = QueryDeviceFont (dw, i);
    264 		if (f && device_font_special (f)
    265 		    && device_char_width (f, dw->dvi.state->font_size,
    266 					  buf, widp)) {
    267 			dw->dvi.state->font_number = i;
    268 			return 1;
    269 		}
    270 	}
    271 	return 0;
    272 }
    273 
    274 /* Return the width of the character in device units. */
    275 
    276 int PutCharacter (DviWidget dw, char *buf)
    277 {
    278 	int		prevFont;
    279 	int		c = -1;
    280 	int		wid = 0;
    281 	DviCharNameMap	*map;
    282 
    283 	if (!dw->dvi.display_enable)
    284 		return 0;	/* The width doesn't matter in this case. */
    285 	prevFont = dw->dvi.state->font_number;
    286 	if (!FindCharWidth (dw, buf, &wid))
    287 		return 0;
    288 	map = QueryFontMap (dw, dw->dvi.state->font_number);
    289 	if (map)
    290 		c = DviCharIndex (map, buf);
    291 	if (c >= 0)
    292 		DoCharacter (dw, c, wid);
    293 	else
    294 		(void) FakeCharacter (dw, buf, wid);
    295 	dw->dvi.state->font_number = prevFont;
    296 	return wid;
    297 }
    298 
    299 /* Return 1 if we can fake it; 0 otherwise. */
    300 
    301 static
    302 int FakeCharacter (DviWidget dw, char *buf, int wid)
    303 {
    304 	int oldx, oldw;
    305 	char ch[2];
    306 	const char *chars = 0;
    307 
    308 	if (buf[0] == '\0' || buf[1] == '\0' || buf[2] != '\0')
    309 		return 0;
    310 #define pack2(c1, c2) (((c1) << 8) | (c2))
    311 
    312 	switch (pack2(buf[0], buf[1])) {
    313 	case pack2('f', 'i'):
    314 		chars = "fi";
    315 		break;
    316 	case pack2('f', 'l'):
    317 		chars = "fl";
    318 		break;
    319 	case pack2('f', 'f'):
    320 		chars = "ff";
    321 		break;
    322 	case pack2('F', 'i'):
    323 		chars = "ffi";
    324 		break;
    325 	case pack2('F', 'l'):
    326 		chars = "ffl";
    327 		break;
    328 	}
    329 	if (!chars)
    330 		return 0;
    331 	oldx = dw->dvi.state->x;
    332 	oldw = dw->dvi.text_device_width;
    333 	ch[1] = '\0';
    334 	for (; *chars; chars++) {
    335 		ch[0] = *chars;
    336 		dw->dvi.state->x += PutCharacter (dw, ch);
    337 	}
    338 	dw->dvi.state->x = oldx;
    339 	dw->dvi.text_device_width = oldw + wid;
    340 	return 1;
    341 }
    342 
    343 void
    344 PutNumberedCharacter (DviWidget dw, int c)
    345 {
    346 	char *name;
    347 	int wid;
    348 	DviCharNameMap	*map;
    349 
    350 	if (!dw->dvi.display_enable)
    351 		return;
    352 
    353 	if (dw->dvi.device_font == 0
    354 	    || dw->dvi.state->font_number != dw->dvi.device_font_number) {
    355 		dw->dvi.device_font_number = dw->dvi.state->font_number;
    356 		dw->dvi.device_font
    357 			= QueryDeviceFont (dw, dw->dvi.device_font_number);
    358 	}
    359 
    360 	if (dw->dvi.device_font == 0
    361 	    || !device_code_width (dw->dvi.device_font,
    362 				   dw->dvi.state->font_size, c, &wid))
    363 		return;
    364 	if (dw->dvi.native) {
    365 		DoCharacter (dw, c, wid);
    366 		return;
    367 	}
    368 	map = QueryFontMap (dw, dw->dvi.state->font_number);
    369 	if (!map)
    370 		return;
    371 	for (name = device_name_for_code (dw->dvi.device_font, c);
    372 	     name;
    373 	     name = device_name_for_code ((DeviceFont *)0, c)) {
    374 		int code = DviCharIndex (map, name);
    375 		if (code >= 0) {
    376 			DoCharacter (dw, code, wid);
    377 			break;
    378 		}
    379 		if (FakeCharacter (dw, name, wid))
    380 			break;
    381 	}
    382 }
    383 
    384 void
    385 ClearPage (DviWidget dw)
    386 {
    387 	XClearWindow (XtDisplay (dw), XtWindow (dw));
    388 }
    389 
    390 static void
    391 setGC (DviWidget dw)
    392 {
    393 	int desired_line_width;
    394 
    395 	if (dw->dvi.line_thickness < 0)
    396 		desired_line_width = (int)(((double)dw->dvi.device_resolution
    397 					    * dw->dvi.state->font_size)
    398 					   / (10.0*72.0*dw->dvi.sizescale));
    399 	else
    400 		desired_line_width = dw->dvi.line_thickness;
    401 
    402 	if (desired_line_width != dw->dvi.line_width) {
    403 		XGCValues values;
    404 		values.line_width = DeviceToX(dw, desired_line_width);
    405 		if (values.line_width == 0)
    406 			values.line_width = 1;
    407 		XChangeGC(XtDisplay (dw), dw->dvi.normal_GC,
    408 			  GCLineWidth, &values);
    409 		dw->dvi.line_width = desired_line_width;
    410 	}
    411 }
    412 
    413 static void
    414 setFillGC (DviWidget dw)
    415 {
    416 	int fill_type;
    417 	unsigned long mask = GCFillStyle | GCForeground;
    418 
    419 	fill_type = (dw->dvi.fill * 10) / (DVI_FILL_MAX + 1);
    420 	if (dw->dvi.fill_type != fill_type) {
    421 		XGCValues values;
    422 		if (fill_type <= 0) {
    423 			values.foreground = dw->dvi.background;
    424 			values.fill_style = FillSolid;
    425 		} else if (fill_type >= 9) {
    426 			values.foreground = dw->dvi.foreground;
    427 			values.fill_style = FillSolid;
    428 		} else {
    429 			values.foreground = dw->dvi.foreground;
    430 			values.fill_style = FillOpaqueStippled;
    431 			values.stipple = dw->dvi.gray[fill_type - 1];
    432 			mask |= GCStipple;
    433 		}
    434 		XChangeGC(XtDisplay (dw), dw->dvi.fill_GC, mask, &values);
    435 		dw->dvi.fill_type = fill_type;
    436 	}
    437 }
    438 
    439 void
    440 DrawLine (DviWidget dw, int x, int y)
    441 {
    442 	int xp, yp;
    443 
    444 	AdjustCacheDeltas (dw);
    445 	setGC (dw);
    446 	xp = XPos (dw);
    447 	yp = YPos (dw);
    448 	XDrawLine (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
    449 		   xp, yp,
    450 		   xp + DeviceToX (dw, x), yp + DeviceToX (dw, y));
    451 }
    452 
    453 void
    454 DrawCircle (DviWidget dw, int diam)
    455 {
    456 	int d;
    457 
    458 	AdjustCacheDeltas (dw);
    459 	setGC (dw);
    460 	d = DeviceToX (dw, diam);
    461 	XDrawArc (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
    462 		  XPos (dw), YPos (dw) - d/2,
    463 		  d, d, 0, 64*360);
    464 }
    465 
    466 void
    467 DrawFilledCircle (DviWidget dw, int diam)
    468 {
    469 	int d;
    470 
    471 	AdjustCacheDeltas (dw);
    472 	setFillGC (dw);
    473 	d = DeviceToX (dw, diam);
    474 	XFillArc (XtDisplay (dw), XtWindow (dw), dw->dvi.fill_GC,
    475 		  XPos (dw), YPos (dw) - d/2,
    476 		  d, d, 0, 64*360);
    477 	XDrawArc (XtDisplay (dw), XtWindow (dw), dw->dvi.fill_GC,
    478 		  XPos (dw), YPos (dw) - d/2,
    479 		  d, d, 0, 64*360);
    480 }
    481 
    482 void
    483 DrawEllipse (DviWidget dw, int a, int b)
    484 {
    485 	AdjustCacheDeltas (dw);
    486 	setGC (dw);
    487 	XDrawArc (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
    488 		  XPos (dw), YPos (dw) - DeviceToX (dw, b/2),
    489 		  DeviceToX (dw, a), DeviceToX (dw, b), 0, 64*360);
    490 }
    491 
    492 void
    493 DrawFilledEllipse (DviWidget dw, int a, int b)
    494 {
    495 	AdjustCacheDeltas (dw);
    496 	setFillGC (dw);
    497 	XFillArc (XtDisplay (dw), XtWindow (dw), dw->dvi.fill_GC,
    498 		  XPos (dw), YPos (dw) - DeviceToX (dw, b/2),
    499 		  DeviceToX (dw, a), DeviceToX (dw, b), 0, 64*360);
    500 	XDrawArc (XtDisplay (dw), XtWindow (dw), dw->dvi.fill_GC,
    501 		  XPos (dw), YPos (dw) - DeviceToX (dw, b/2),
    502 		  DeviceToX (dw, a), DeviceToX (dw, b), 0, 64*360);
    503 }
    504 
    505 void
    506 DrawArc (DviWidget dw, int x_0, int y_0, int x_1, int y_1)
    507 {
    508 	int angle1, angle2;
    509 	int rad = (int)((sqrt ((double)x_0*x_0 + (double)y_0*y_0)
    510 			 + sqrt ((double)x_1*x_1 + (double)y_1*y_1)
    511 			 + 1.0)/2.0);
    512 	if ((x_0 == 0 && y_0 == 0) || (x_1 == 0 && y_1 == 0))
    513 		return;
    514 	angle1 = (int)(atan2 ((double)y_0, (double)-x_0)*180.0*64.0/M_PI);
    515 	angle2 = (int)(atan2 ((double)-y_1, (double)x_1)*180.0*64.0/M_PI);
    516 
    517 	angle2 -= angle1;
    518 	if (angle2 < 0)
    519 		angle2 += 64*360;
    520 
    521 	AdjustCacheDeltas (dw);
    522 	setGC (dw);
    523 
    524 	rad = DeviceToX (dw, rad);
    525 	XDrawArc (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
    526 		  XPos (dw) + DeviceToX (dw, x_0) - rad,
    527 		  YPos (dw) + DeviceToX (dw, y_0) - rad,
    528 		  rad*2, rad*2, angle1, angle2);
    529 }
    530 
    531 void
    532 DrawPolygon (DviWidget dw, int *v, int n)
    533 {
    534 	XPoint *p;
    535 	int i;
    536 	int dx, dy;
    537 
    538 	n /= 2;
    539 
    540 	AdjustCacheDeltas (dw);
    541 	setGC (dw);
    542 	p = (XPoint *)XtMalloc((n + 2)*sizeof(XPoint));
    543 	p[0].x = XPos (dw);
    544 	p[0].y = YPos (dw);
    545 	dx = 0;
    546 	dy = 0;
    547 	for (i = 0; i < n; i++) {
    548 		dx += v[2*i];
    549 		p[i + 1].x = DeviceToX (dw, dx) + p[0].x;
    550 		dy += v[2*i + 1];
    551 		p[i + 1].y = DeviceToX (dw, dy) + p[0].y;
    552 	}
    553 	p[n+1].x = p[0].x;
    554 	p[n+1].y = p[0].y;
    555 	XDrawLines (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
    556 		   p, n + 2, CoordModeOrigin);
    557 	XtFree((char *)p);
    558 }
    559 
    560 void
    561 DrawFilledPolygon (DviWidget dw, int *v, int n)
    562 {
    563 	XPoint *p;
    564 	int i;
    565 	int dx, dy;
    566 
    567 	n /= 2;
    568 	if (n < 2)
    569 		return;
    570 
    571 	AdjustCacheDeltas (dw);
    572 	setFillGC (dw);
    573 	p = (XPoint *)XtMalloc((n + 2)*sizeof(XPoint));
    574 	p[0].x = p[n+1].x = XPos (dw);
    575 	p[0].y = p[n+1].y = YPos (dw);
    576 	dx = 0;
    577 	dy = 0;
    578 	for (i = 0; i < n; i++) {
    579 		dx += v[2*i];
    580 		p[i + 1].x = DeviceToX (dw, dx) + p[0].x;
    581 		dy += v[2*i + 1];
    582 		p[i + 1].y = DeviceToX (dw, dy) + p[0].y;
    583 	}
    584 	XFillPolygon (XtDisplay (dw), XtWindow (dw), dw->dvi.fill_GC,
    585 		      p, n + 1, Complex, CoordModeOrigin);
    586 	XDrawLines (XtDisplay (dw), XtWindow (dw), dw->dvi.fill_GC,
    587 		      p, n + 2, CoordModeOrigin);
    588 	XtFree((char *)p);
    589 }
    590 
    591 #define POINTS_MAX 10000
    592 
    593 static void
    594 appendPoint(XPoint *points, int *pointi, int x, int y)
    595 {
    596 	if (*pointi < POINTS_MAX) {
    597 		points[*pointi].x = x;
    598 		points[*pointi].y = y;
    599 		*pointi += 1;
    600 	}
    601 }
    602 
    603 #define FLATNESS 1
    604 
    605 static void
    606 flattenCurve(XPoint *points, int *pointi,
    607 	     int x_2, int y_2, int x_3, int y_3, int x_4, int y_4)
    608 {
    609 	int x_1, y_1, dx, dy, n1, n2, n;
    610 
    611 	x_1 = points[*pointi - 1].x;
    612 	y_1 = points[*pointi - 1].y;
    613 
    614 	dx = x_4 - x_1;
    615 	dy = y_4 - y_1;
    616 
    617 	n1 = dy*(x_2 - x_1) - dx*(y_2 - y_1);
    618 	n2 = dy*(x_3 - x_1) - dx*(y_3 - y_1);
    619 	if (n1 < 0)
    620 		n1 = -n1;
    621 	if (n2 < 0)
    622 		n2 = -n2;
    623 	n = n1 > n2 ? n1 : n2;
    624 
    625 	if (n*n / (dy*dy + dx*dx) <= FLATNESS*FLATNESS)
    626 		appendPoint (points, pointi, x_4, y_4);
    627 	else {
    628 		flattenCurve (points, pointi,
    629 			      (x_1 + x_2)/2,
    630 			      (y_1 + y_2)/2,
    631 			      (x_1 + x_2*2 + x_3)/4,
    632 			      (y_1 + y_2*2 + y_3)/4,
    633 			      (x_1 + 3*x_2 + 3*x_3 + x_4)/8,
    634 			      (y_1 + 3*y_2 + 3*y_3 + y_4)/8);
    635 		flattenCurve (points, pointi,
    636 			      (x_2 + x_3*2 + x_4)/4,
    637 			      (y_2 + y_3*2 + y_4)/4,
    638 			      (x_3 + x_4)/2,
    639 			      (y_3 + y_4)/2,
    640 			      x_4,
    641 			      y_4);
    642 	}
    643 }
    644 
    645 void
    646 DrawSpline (DviWidget dw, int *v, int n)
    647 {
    648 	int sx, sy, tx, ty;
    649 	int ox, oy, dx, dy;
    650 	int i;
    651 	int pointi;
    652 	XPoint points[POINTS_MAX];
    653 
    654 	if (n == 0 || (n & 1) != 0)
    655 		return;
    656 	AdjustCacheDeltas (dw);
    657 	setGC (dw);
    658 	ox = XPos (dw);
    659 	oy = YPos (dw);
    660 	dx = v[0];
    661 	dy = v[1];
    662 	sx = ox;
    663 	sy = oy;
    664 	tx = sx + DeviceToX (dw, dx);
    665 	ty = sy + DeviceToX (dw, dy);
    666 
    667 	pointi = 0;
    668 
    669 	appendPoint (points, &pointi, sx, sy);
    670 	appendPoint (points, &pointi, (sx + tx)/2, (sy + ty)/2);
    671 
    672 	for (i = 2; i < n; i += 2) {
    673 		int ux = ox + DeviceToX (dw, dx += v[i]);
    674 		int uy = oy + DeviceToX (dw, dy += v[i+1]);
    675 		flattenCurve (points, &pointi,
    676 			       (sx + tx*5)/6, (sy + ty*5)/6,
    677 			       (tx*5 + ux)/6, (ty*5 + uy)/6,
    678 			       (tx + ux)/2, (ty + uy)/2);
    679 		sx = tx;
    680 		sy = ty;
    681 		tx = ux;
    682 		ty = uy;
    683 	}
    684 
    685 	appendPoint (points, &pointi, tx, ty);
    686 
    687 	XDrawLines (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
    688 		   points, pointi, CoordModeOrigin);
    689 }
    690 
    691 
    692 /*
    693 Local Variables:
    694 c-indent-level: 8
    695 c-continued-statement-offset: 8
    696 c-brace-offset: -8
    697 c-argdecl-indent: 8
    698 c-label-offset: -8
    699 c-tab-always-indent: nil
    700 End:
    701 */
    702