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