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