groff before CVS: release 1.15
[s-roff.git] / xditview / draw.c
blob69e86cd6a6e1a03fce91de56b50e57232a366863
1 /*
2 * draw.c
4 * accept dvi function calls and translate to X
5 */
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>
14 /* math.h on a Sequent doesn't define M_PI, apparently */
15 #ifndef M_PI
16 #define M_PI 3.14159265358979323846
17 #endif
19 #include "DviP.h"
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))
26 static int FakeCharacter();
28 HorizontalMove(dw, delta)
29 DviWidget dw;
30 int delta;
32 dw->dvi.state->x += delta;
35 HorizontalGoto(dw, NewPosition)
36 DviWidget dw;
37 int NewPosition;
39 dw->dvi.state->x = NewPosition;
42 VerticalMove(dw, delta)
43 DviWidget dw;
44 int delta;
46 dw->dvi.state->y += delta;
49 VerticalGoto(dw, NewPosition)
50 DviWidget dw;
51 int NewPosition;
53 dw->dvi.state->y = NewPosition;
56 AdjustCacheDeltas (dw)
57 DviWidget dw;
59 int extra;
60 int nadj;
61 int i;
63 nadj = 0;
64 extra = DeviceToX(dw, dw->dvi.text_device_width)
65 - dw->dvi.text_x_width;
66 if (extra == 0)
67 return;
68 for (i = 0; i <= dw->dvi.cache.index; i++)
69 if (dw->dvi.cache.adjustable[i])
70 ++nadj;
71 if (nadj == 0)
72 return;
73 dw->dvi.text_x_width += extra;
74 for (i = 0; i <= dw->dvi.cache.index; i++)
75 if (dw->dvi.cache.adjustable[i]) {
76 int x;
77 int *deltap;
79 x = extra/nadj;
80 deltap = &dw->dvi.cache.cache[i].delta;
81 #define MIN_DELTA 2
82 if (*deltap > 0 && x + *deltap < MIN_DELTA) {
83 x = MIN_DELTA - *deltap;
84 if (x <= 0)
85 *deltap = MIN_DELTA;
86 else
87 x = 0;
89 else
90 *deltap += x;
91 extra -= x;
92 --nadj;
93 dw->dvi.cache.adjustable[i] = 0;
97 FlushCharCache (dw)
98 DviWidget dw;
100 if (dw->dvi.cache.char_index != 0) {
101 AdjustCacheDeltas (dw);
102 XDrawText (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
103 dw->dvi.cache.start_x, dw->dvi.cache.start_y,
104 dw->dvi.cache.cache, dw->dvi.cache.index + 1);
106 dw->dvi.cache.index = 0;
107 dw->dvi.cache.max = DVI_TEXT_CACHE_SIZE;
108 #if 0
109 if (dw->dvi.noPolyText)
110 dw->dvi.cache.max = 1;
111 #endif
112 dw->dvi.cache.char_index = 0;
113 dw->dvi.cache.cache[0].nchars = 0;
114 dw->dvi.cache.start_x = dw->dvi.cache.x = XPos (dw);
115 dw->dvi.cache.start_y = dw->dvi.cache.y = YPos (dw);
118 Newline (dw)
119 DviWidget dw;
121 FlushCharCache (dw);
122 dw->dvi.text_x_width = dw->dvi.text_device_width = 0;
123 dw->dvi.word_flag = 0;
126 Word (dw)
127 DviWidget dw;
129 dw->dvi.word_flag = 1;
132 #define charWidth(fi,c) (\
133 (fi)->per_char ?\
134 (fi)->per_char[(c) - (fi)->min_char_or_byte2].width\
136 (fi)->max_bounds.width\
140 static
141 int charExists (fi, c)
142 XFontStruct *fi;
143 int c;
145 XCharStruct *p;
147 if (fi->per_char == NULL ||
148 c < fi->min_char_or_byte2 || c > fi->max_char_or_byte2)
149 return 0;
150 p = fi->per_char + (c - fi->min_char_or_byte2);
151 return (p->lbearing != 0 || p->rbearing != 0 || p->width != 0
152 || p->ascent != 0 || p->descent != 0 || p->attributes != 0);
155 static
156 DoCharacter (dw, c, wid)
157 DviWidget dw;
158 int c;
159 int wid; /* width in device units */
161 register XFontStruct *font;
162 register XTextItem *text;
163 int x, y;
165 x = XPos(dw);
166 y = YPos(dw);
169 * quick and dirty extents calculation:
171 if (!(y + 24 >= dw->dvi.extents.y1
172 && y - 24 <= dw->dvi.extents.y2
173 #if 0
174 && x + 24 >= dw->dvi.extents.x1
175 && x - 24 <= dw->dvi.extents.x2
176 #endif
178 return;
180 if (y != dw->dvi.cache.y
181 || dw->dvi.cache.char_index >= DVI_CHAR_CACHE_SIZE) {
182 FlushCharCache (dw);
183 x = dw->dvi.cache.x;
186 * load a new font, if the current block is not empty,
187 * step to the next.
189 if (dw->dvi.cache.font_size != dw->dvi.state->font_size ||
190 dw->dvi.cache.font_number != dw->dvi.state->font_number)
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;
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;
212 dw->dvi.cache.adjustable[dw->dvi.cache.index]
213 = dw->dvi.word_flag;
214 dw->dvi.word_flag = 0;
216 font = dw->dvi.cache.font;
217 text = &dw->dvi.cache.cache[dw->dvi.cache.index];
218 if (text->nchars == 0) {
219 text->chars = &dw->dvi.cache.char_cache[dw->dvi.cache.char_index];
220 text->delta = x - dw->dvi.cache.x;
221 if (font != dw->dvi.font) {
222 text->font = font->fid;
223 dw->dvi.font = font;
224 } else
225 text->font = None;
226 dw->dvi.cache.x += text->delta;
228 if (charExists(font, c)) {
229 int w;
230 dw->dvi.cache.char_cache[dw->dvi.cache.char_index++] = (char) c;
231 ++text->nchars;
232 w = charWidth(font, c);
233 dw->dvi.cache.x += w;
234 if (wid != 0) {
235 dw->dvi.text_x_width += w;
236 dw->dvi.text_device_width += wid;
241 static
242 int FindCharWidth (dw, buf, widp)
243 DviWidget dw;
244 char *buf;
245 int *widp;
247 int maxpos;
248 int i;
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);
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;
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;
271 return 0;
274 /* Return the width of the character in device units. */
276 int PutCharacter (dw, buf)
277 DviWidget dw;
278 char *buf;
280 int prevFont;
281 int c = -1;
282 int wid = 0;
283 DviCharNameMap *map;
285 if (!dw->dvi.display_enable)
286 return 0; /* The width doesn't matter in this case. */
287 prevFont = dw->dvi.state->font_number;
288 if (!FindCharWidth (dw, buf, &wid))
289 return 0;
290 map = QueryFontMap (dw, dw->dvi.state->font_number);
291 if (map)
292 c = DviCharIndex (map, buf);
293 if (c >= 0)
294 DoCharacter (dw, c, wid);
295 else
296 (void) FakeCharacter (dw, buf, wid);
297 dw->dvi.state->font_number = prevFont;
298 return wid;
301 /* Return 1 if we can fake it; 0 otherwise. */
303 static
304 int FakeCharacter (dw, buf, wid)
305 DviWidget dw;
306 char *buf;
307 int wid;
309 int oldx, oldw;
310 char ch[2];
311 char *chars = 0;
313 if (buf[0] == '\0' || buf[1] == '\0' || buf[2] != '\0')
314 return 0;
315 #define pack2(c1, c2) (((c1) << 8) | (c2))
317 switch (pack2(buf[0], buf[1])) {
318 case pack2('f', 'i'):
319 chars = "fi";
320 break;
321 case pack2('f', 'l'):
322 chars = "fl";
323 break;
324 case pack2('f', 'f'):
325 chars = "ff";
326 break;
327 case pack2('F', 'i'):
328 chars = "ffi";
329 break;
330 case pack2('F', 'l'):
331 chars = "ffl";
332 break;
334 if (!chars)
335 return 0;
336 oldx = dw->dvi.state->x;
337 oldw = dw->dvi.text_device_width;
338 ch[1] = '\0';
339 for (; *chars; chars++) {
340 ch[0] = *chars;
341 dw->dvi.state->x += PutCharacter (dw, ch);
343 dw->dvi.state->x = oldx;
344 dw->dvi.text_device_width = oldw + wid;
345 return 1;
348 PutNumberedCharacter (dw, c)
349 DviWidget dw;
350 int c;
352 char *name;
353 int wid;
354 DviCharNameMap *map;
356 if (!dw->dvi.display_enable)
357 return;
359 if (dw->dvi.device_font == 0
360 || dw->dvi.state->font_number != dw->dvi.device_font_number) {
361 dw->dvi.device_font_number = dw->dvi.state->font_number;
362 dw->dvi.device_font
363 = QueryDeviceFont (dw, dw->dvi.device_font_number);
366 if (dw->dvi.device_font == 0
367 || !device_code_width (dw->dvi.device_font,
368 dw->dvi.state->font_size, c, &wid))
369 return;
370 if (dw->dvi.native) {
371 DoCharacter (dw, c, wid);
372 return;
374 map = QueryFontMap (dw, dw->dvi.state->font_number);
375 if (!map)
376 return;
377 for (name = device_name_for_code (dw->dvi.device_font, c);
378 name;
379 name = device_name_for_code ((DeviceFont *)0, c)) {
380 int code = DviCharIndex (map, name);
381 if (code >= 0) {
382 DoCharacter (dw, code, wid);
383 break;
385 if (FakeCharacter (dw, name, wid))
386 break;
390 ClearPage (dw)
391 DviWidget dw;
393 XClearWindow (XtDisplay (dw), XtWindow (dw));
396 static
397 setGC (dw)
398 DviWidget dw;
400 int desired_line_width;
402 if (dw->dvi.line_thickness < 0)
403 desired_line_width = (int)(((double)dw->dvi.device_resolution
404 * dw->dvi.state->font_size)
405 / (10.0*72.0*dw->dvi.sizescale));
406 else
407 desired_line_width = dw->dvi.line_thickness;
409 if (desired_line_width != dw->dvi.line_width) {
410 XGCValues values;
411 values.line_width = DeviceToX(dw, desired_line_width);
412 if (values.line_width == 0)
413 values.line_width = 1;
414 XChangeGC(XtDisplay (dw), dw->dvi.normal_GC,
415 GCLineWidth, &values);
416 dw->dvi.line_width = desired_line_width;
420 static
421 setFillGC (dw)
422 DviWidget dw;
424 int fill_type;
425 unsigned long mask = GCFillStyle | GCForeground;
427 fill_type = (dw->dvi.fill * 10) / (DVI_FILL_MAX + 1);
428 if (dw->dvi.fill_type != fill_type) {
429 XGCValues values;
430 if (fill_type <= 0) {
431 values.foreground = dw->dvi.background;
432 values.fill_style = FillSolid;
433 } else if (fill_type >= 9) {
434 values.foreground = dw->dvi.foreground;
435 values.fill_style = FillSolid;
436 } else {
437 values.foreground = dw->dvi.foreground;
438 values.fill_style = FillOpaqueStippled;
439 values.stipple = dw->dvi.gray[fill_type - 1];
440 mask |= GCStipple;
442 XChangeGC(XtDisplay (dw), dw->dvi.fill_GC, mask, &values);
443 dw->dvi.fill_type = fill_type;
447 DrawLine (dw, x, y)
448 DviWidget dw;
449 int x, y;
451 int xp, yp;
453 AdjustCacheDeltas (dw);
454 setGC (dw);
455 xp = XPos (dw);
456 yp = YPos (dw);
457 XDrawLine (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
458 xp, yp,
459 xp + DeviceToX (dw, x), yp + DeviceToX (dw, y));
462 DrawCircle (dw, diam)
463 DviWidget dw;
464 int diam;
466 int d;
468 AdjustCacheDeltas (dw);
469 setGC (dw);
470 d = DeviceToX (dw, diam);
471 XDrawArc (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
472 XPos (dw), YPos (dw) - d/2,
473 d, d, 0, 64*360);
476 DrawFilledCircle (dw, diam)
477 DviWidget dw;
478 int diam;
480 int d;
482 AdjustCacheDeltas (dw);
483 setFillGC (dw);
484 d = DeviceToX (dw, diam);
485 XFillArc (XtDisplay (dw), XtWindow (dw), dw->dvi.fill_GC,
486 XPos (dw), YPos (dw) - d/2,
487 d, d, 0, 64*360);
488 XDrawArc (XtDisplay (dw), XtWindow (dw), dw->dvi.fill_GC,
489 XPos (dw), YPos (dw) - d/2,
490 d, d, 0, 64*360);
493 DrawEllipse (dw, a, b)
494 DviWidget dw;
495 int a, b;
497 AdjustCacheDeltas (dw);
498 setGC (dw);
499 XDrawArc (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
500 XPos (dw), YPos (dw) - DeviceToX (dw, b/2),
501 DeviceToX (dw, a), DeviceToX (dw, b), 0, 64*360);
504 DrawFilledEllipse (dw, a, b)
505 DviWidget dw;
506 int a, b;
508 AdjustCacheDeltas (dw);
509 setFillGC (dw);
510 XFillArc (XtDisplay (dw), XtWindow (dw), dw->dvi.fill_GC,
511 XPos (dw), YPos (dw) - DeviceToX (dw, b/2),
512 DeviceToX (dw, a), DeviceToX (dw, b), 0, 64*360);
513 XDrawArc (XtDisplay (dw), XtWindow (dw), dw->dvi.fill_GC,
514 XPos (dw), YPos (dw) - DeviceToX (dw, b/2),
515 DeviceToX (dw, a), DeviceToX (dw, b), 0, 64*360);
518 DrawArc (dw, x0, y0, x1, y1)
519 DviWidget dw;
520 int x0, y0, x1, y1;
522 int angle1, angle2;
523 int rad = (int)((sqrt ((double)x0*x0 + (double)y0*y0)
524 + sqrt ((double)x1*x1 + (double)y1*y1) + 1.0)/2.0);
525 if ((x0 == 0 && y0 == 0) || (x1 == 0 && y1 == 0))
526 return;
527 angle1 = (int)(atan2 ((double)y0, (double)-x0)*180.0*64.0/M_PI);
528 angle2 = (int)(atan2 ((double)-y1, (double)x1)*180.0*64.0/M_PI);
530 angle2 -= angle1;
531 if (angle2 < 0)
532 angle2 += 64*360;
534 AdjustCacheDeltas (dw);
535 setGC (dw);
537 rad = DeviceToX (dw, rad);
538 XDrawArc (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
539 XPos (dw) + DeviceToX (dw, x0) - rad,
540 YPos (dw) + DeviceToX (dw, y0) - rad,
541 rad*2, rad*2, angle1, angle2);
544 DrawPolygon (dw, v, n)
545 DviWidget dw;
546 int *v;
547 int n;
549 XPoint *p;
550 int i;
551 int dx, dy;
553 n /= 2;
555 AdjustCacheDeltas (dw);
556 setGC (dw);
557 p = (XPoint *)XtMalloc((n + 2)*sizeof(XPoint));
558 p[0].x = XPos (dw);
559 p[0].y = YPos (dw);
560 dx = 0;
561 dy = 0;
562 for (i = 0; i < n; i++) {
563 dx += v[2*i];
564 p[i + 1].x = DeviceToX (dw, dx) + p[0].x;
565 dy += v[2*i + 1];
566 p[i + 1].y = DeviceToX (dw, dy) + p[0].y;
568 p[n+1].x = p[0].x;
569 p[n+1].y = p[0].y;
570 XDrawLines (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
571 p, n + 2, CoordModeOrigin);
572 XtFree((char *)p);
576 DrawFilledPolygon (dw, v, n)
577 DviWidget dw;
578 int *v;
579 int n;
581 XPoint *p;
582 int i;
583 int dx, dy;
585 n /= 2;
586 if (n < 2)
587 return;
589 AdjustCacheDeltas (dw);
590 setFillGC (dw);
591 p = (XPoint *)XtMalloc((n + 2)*sizeof(XPoint));
592 p[0].x = p[n+1].x = XPos (dw);
593 p[0].y = p[n+1].y = YPos (dw);
594 dx = 0;
595 dy = 0;
596 for (i = 0; i < n; i++) {
597 dx += v[2*i];
598 p[i + 1].x = DeviceToX (dw, dx) + p[0].x;
599 dy += v[2*i + 1];
600 p[i + 1].y = DeviceToX (dw, dy) + p[0].y;
602 XFillPolygon (XtDisplay (dw), XtWindow (dw), dw->dvi.fill_GC,
603 p, n + 1, Complex, CoordModeOrigin);
604 XDrawLines (XtDisplay (dw), XtWindow (dw), dw->dvi.fill_GC,
605 p, n + 2, CoordModeOrigin);
606 XtFree((char *)p);
609 #define POINTS_MAX 10000
611 static
612 appendPoint(points, pointi, x, y)
613 XPoint *points;
614 int *pointi;
615 int x, y;
617 if (*pointi < POINTS_MAX) {
618 points[*pointi].x = x;
619 points[*pointi].y = y;
620 *pointi += 1;
624 #define FLATNESS 1
626 static
627 flattenCurve(points, pointi, x2, y2, x3, y3, x4, y4)
628 XPoint *points;
629 int *pointi;
630 int x2, y2, x3, y3, x4, y4;
632 int x1, y1, dx, dy, n1, n2, n;
634 x1 = points[*pointi - 1].x;
635 y1 = points[*pointi - 1].y;
637 dx = x4 - x1;
638 dy = y4 - y1;
640 n1 = dy*(x2 - x1) - dx*(y2 - y1);
641 n2 = dy*(x3 - x1) - dx*(y3 - y1);
642 if (n1 < 0)
643 n1 = -n1;
644 if (n2 < 0)
645 n2 = -n2;
646 n = n1 > n2 ? n1 : n2;
648 if (n*n / (dy*dy + dx*dx) <= FLATNESS*FLATNESS)
649 appendPoint (points, pointi, x4, y4);
650 else {
651 flattenCurve (points, pointi,
652 (x1 + x2)/2, (y1 + y2)/2,
653 (x1 + x2*2 + x3)/4, (y1 + y2*2 + y3)/4,
654 (x1 +3*x2 + 3*x3 + x4)/8, (y1 +3*y2 + 3*y3 + y4)/8);
655 flattenCurve (points, pointi,
656 (x2 + x3*2 + x4)/4, (y2 + y3*2 + y4)/4,
657 (x3 + x4)/2, (y3 + y4)/2,
658 x4, y4);
663 DrawSpline (dw, v, n)
664 DviWidget dw;
665 int *v;
666 int n;
668 int sx, sy, tx, ty;
669 int ox, oy, dx, dy;
670 int i;
671 int pointi;
672 XPoint points[POINTS_MAX];
674 if (n == 0 || (n & 1) != 0)
675 return;
676 AdjustCacheDeltas (dw);
677 setGC (dw);
678 ox = XPos (dw);
679 oy = YPos (dw);
680 dx = v[0];
681 dy = v[1];
682 sx = ox;
683 sy = oy;
684 tx = sx + DeviceToX (dw, dx);
685 ty = sy + DeviceToX (dw, dy);
687 pointi = 0;
689 appendPoint (points, &pointi, sx, sy);
690 appendPoint (points, &pointi, (sx + tx)/2, (sy + ty)/2);
692 for (i = 2; i < n; i += 2) {
693 int ux = ox + DeviceToX (dw, dx += v[i]);
694 int uy = oy + DeviceToX (dw, dy += v[i+1]);
695 flattenCurve (points, &pointi,
696 (sx + tx*5)/6, (sy + ty*5)/6,
697 (tx*5 + ux)/6, (ty*5 + uy)/6,
698 (tx + ux)/2, (ty + uy)/2);
699 sx = tx;
700 sy = ty;
701 tx = ux;
702 ty = uy;
705 appendPoint (points, &pointi, tx, ty);
707 XDrawLines (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC,
708 points, pointi, CoordModeOrigin);
713 Local Variables:
714 c-indent-level: 8
715 c-continued-statement-offset: 8
716 c-brace-offset: -8
717 c-argdecl-indent: 8
718 c-label-offset: -8
719 c-tab-always-indent: nil
720 End: