diff options
Diffstat (limited to 'src/devices/xditview/draw.c')
-rw-r--r-- | src/devices/xditview/draw.c | 709 |
1 files changed, 709 insertions, 0 deletions
diff --git a/src/devices/xditview/draw.c b/src/devices/xditview/draw.c new file mode 100644 index 0000000..288d98a --- /dev/null +++ b/src/devices/xditview/draw.c @@ -0,0 +1,709 @@ +/* + * draw.c + * + * accept dvi function calls and translate to X + */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <X11/Xos.h> +#include <X11/IntrinsicP.h> +#include <X11/StringDefs.h> +#include <stdio.h> +#include <ctype.h> +#include <math.h> + +/* math.h on a Sequent doesn't define M_PI, apparently */ +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +#include "DviP.h" +#include "draw.h" +#include "font.h" + +#define DeviceToX(dw, n) ((int)((n) * (dw)->dvi.scale_factor + .5)) +#define XPos(dw) (DeviceToX((dw), (dw)->dvi.state->x - \ + (dw)->dvi.text_device_width) + (dw)->dvi.text_x_width) +#define YPos(dw) (DeviceToX((dw), (dw)->dvi.state->y)) + +/* forward reference */ +static int FakeCharacter(DviWidget, char *, int); + +/* shadowed by a macro definition in parse.c, and unused elsewhere */ +#if 0 +static void +HorizontalMove(DviWidget dw, int delta) +{ + dw->dvi.state->x += delta; +} +#endif + +void +HorizontalGoto(DviWidget dw, int NewPosition) +{ + dw->dvi.state->x = NewPosition; +} + +void +VerticalMove(DviWidget dw, int delta) +{ + dw->dvi.state->y += delta; +} + +void +VerticalGoto(DviWidget dw, int NewPosition) +{ + dw->dvi.state->y = NewPosition; +} + +static void +AdjustCacheDeltas (DviWidget dw) +{ + int extra; + int nadj; + int i; + + nadj = 0; + extra = DeviceToX(dw, dw->dvi.text_device_width) + - dw->dvi.text_x_width; + if (extra == 0) + return; + for (i = 0; i <= dw->dvi.cache.index; i++) + if (dw->dvi.cache.adjustable[i]) + ++nadj; + dw->dvi.text_x_width += extra; + if (nadj <= 1) + return; + for (i = 0; i <= dw->dvi.cache.index; i++) + if (dw->dvi.cache.adjustable[i]) { + int x; + int *deltap; + + x = extra/nadj; + deltap = &dw->dvi.cache.cache[i].delta; +#define MIN_DELTA 2 + if (*deltap > 0 && x + *deltap < MIN_DELTA) { + x = MIN_DELTA - *deltap; + if (x <= 0) + *deltap = MIN_DELTA; + else + x = 0; + } + else + *deltap += x; + extra -= x; + --nadj; + dw->dvi.cache.adjustable[i] = 0; + } +} + +void +FlushCharCache (DviWidget dw) +{ + if (dw->dvi.cache.char_index != 0) { + AdjustCacheDeltas (dw); + XDrawText (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC, + dw->dvi.cache.start_x, dw->dvi.cache.start_y, + dw->dvi.cache.cache, dw->dvi.cache.index + 1); + } + dw->dvi.cache.index = 0; + dw->dvi.cache.max = DVI_TEXT_CACHE_SIZE; +#if 0 + if (dw->dvi.noPolyText) + dw->dvi.cache.max = 1; +#endif + dw->dvi.cache.char_index = 0; + dw->dvi.cache.cache[0].nchars = 0; + dw->dvi.cache.start_x = dw->dvi.cache.x = XPos (dw); + dw->dvi.cache.start_y = dw->dvi.cache.y = YPos (dw); +} + +void +Newline (DviWidget dw) +{ + FlushCharCache (dw); + dw->dvi.text_x_width = dw->dvi.text_device_width = 0; + dw->dvi.word_flag = 0; +} + +void +Word (DviWidget dw) +{ + dw->dvi.word_flag = 1; +} + +#define charWidth(fi,c) (\ + (fi)->per_char ?\ + (fi)->per_char[(c) - (fi)->min_char_or_byte2].width\ + :\ + (fi)->max_bounds.width\ +) + + +static +int charExists (XFontStruct *fi, int c) +{ + XCharStruct *p; + + /* 'c' is always >= 0 */ + if (fi->per_char == NULL + || (unsigned int)c < fi->min_char_or_byte2 + || (unsigned int)c > fi->max_char_or_byte2) + return 0; + p = fi->per_char + (c - fi->min_char_or_byte2); + return (p->lbearing != 0 || p->rbearing != 0 || p->width != 0 + || p->ascent != 0 || p->descent != 0 || p->attributes != 0); +} + +/* 'wid' is in device units */ +static void +DoCharacter (DviWidget dw, int c, int wid) +{ + register XFontStruct *font; + register XTextItem *text; + int x, y; + + x = XPos(dw); + y = YPos(dw); + + /* + * quick and dirty extents calculation: + */ + if (!(y + 24 >= dw->dvi.extents.y1 + && y - 24 <= dw->dvi.extents.y2 +#if 0 + && x + 24 >= dw->dvi.extents.x1 + && x - 24 <= dw->dvi.extents.x2 +#endif + )) + return; + + if (y != dw->dvi.cache.y + || dw->dvi.cache.char_index >= DVI_CHAR_CACHE_SIZE) { + FlushCharCache (dw); + x = dw->dvi.cache.x; + dw->dvi.cache.adjustable[dw->dvi.cache.index] = 0; + } + /* + * load a new font, if the current block is not empty, + * step to the next. + */ + if (dw->dvi.cache.font_size != dw->dvi.state->font_size || + dw->dvi.cache.font_number != dw->dvi.state->font_number) + { + FlushCharCache (dw); + x = dw->dvi.cache.x; + dw->dvi.cache.font_size = dw->dvi.state->font_size; + dw->dvi.cache.font_number = dw->dvi.state->font_number; + dw->dvi.cache.font = QueryFont (dw, + dw->dvi.cache.font_number, + dw->dvi.cache.font_size); + if (dw->dvi.cache.cache[dw->dvi.cache.index].nchars != 0) { + ++dw->dvi.cache.index; + if (dw->dvi.cache.index >= dw->dvi.cache.max) + FlushCharCache (dw); + dw->dvi.cache.cache[dw->dvi.cache.index].nchars = 0; + dw->dvi.cache.adjustable[dw->dvi.cache.index] = 0; + } + } + if (x != dw->dvi.cache.x || dw->dvi.word_flag) { + if (dw->dvi.cache.cache[dw->dvi.cache.index].nchars != 0) { + ++dw->dvi.cache.index; + if (dw->dvi.cache.index >= dw->dvi.cache.max) + FlushCharCache (dw); + dw->dvi.cache.cache[dw->dvi.cache.index].nchars = 0; + dw->dvi.cache.adjustable[dw->dvi.cache.index] = 0; + } + dw->dvi.cache.adjustable[dw->dvi.cache.index] + = dw->dvi.word_flag; + dw->dvi.word_flag = 0; + } + font = dw->dvi.cache.font; + text = &dw->dvi.cache.cache[dw->dvi.cache.index]; + if (text->nchars == 0) { + text->chars = &dw->dvi.cache.char_cache[dw->dvi.cache.char_index]; + text->delta = x - dw->dvi.cache.x; + if (font != dw->dvi.font) { + text->font = font->fid; + dw->dvi.font = font; + } else + text->font = None; + dw->dvi.cache.x += text->delta; + } + if (charExists(font, c)) { + int w; + dw->dvi.cache.char_cache[dw->dvi.cache.char_index++] = (char) c; + ++text->nchars; + w = charWidth(font, c); + dw->dvi.cache.x += w; + if (wid != 0) { + dw->dvi.text_x_width += w; + dw->dvi.text_device_width += wid; + } + } +} + +static +int FindCharWidth (DviWidget dw, char *buf, int *widp) +{ + int maxpos; + int i; + + if (dw->dvi.device_font == 0 + || dw->dvi.state->font_number != dw->dvi.device_font_number) { + dw->dvi.device_font_number = dw->dvi.state->font_number; + dw->dvi.device_font + = QueryDeviceFont (dw, dw->dvi.device_font_number); + } + if (dw->dvi.device_font + && device_char_width (dw->dvi.device_font, + dw->dvi.state->font_size, buf, widp)) + return 1; + + maxpos = MaxFontPosition (dw); + for (i = 1; i <= maxpos; i++) { + DeviceFont *f = QueryDeviceFont (dw, i); + if (f && device_font_special (f) + && device_char_width (f, dw->dvi.state->font_size, + buf, widp)) { + dw->dvi.state->font_number = i; + return 1; + } + } + return 0; +} + +/* Return the width of the character in device units. */ + +int +PutCharacter (DviWidget dw, char *buf) +{ + int prevFont; + int c = -1; + int wid = 0; + DviCharNameMap *map; + + if (!dw->dvi.display_enable) + return 0; /* The width doesn't matter in this case. */ + prevFont = dw->dvi.state->font_number; + if (!FindCharWidth (dw, buf, &wid)) + return 0; + map = QueryFontMap (dw, dw->dvi.state->font_number); + if (map) + c = DviCharIndex (map, buf); + if (c >= 0) + DoCharacter (dw, c, wid); + else + (void) FakeCharacter (dw, buf, wid); + dw->dvi.state->font_number = prevFont; + return wid; +} + +/* Return 1 if we can fake it; 0 otherwise. */ + +static +int FakeCharacter (DviWidget dw, char *buf, int wid) +{ + int oldx, oldw; + char ch[2]; + const char *chars = 0; + + if (buf[0] == '\0' || buf[1] == '\0' || buf[2] != '\0') + return 0; +#define pack2(c1, c2) (((c1) << 8) | (c2)) + + switch (pack2(buf[0], buf[1])) { + case pack2('f', 'i'): + chars = "fi"; + break; + case pack2('f', 'l'): + chars = "fl"; + break; + case pack2('f', 'f'): + chars = "ff"; + break; + case pack2('F', 'i'): + chars = "ffi"; + break; + case pack2('F', 'l'): + chars = "ffl"; + break; + } + if (!chars) + return 0; + oldx = dw->dvi.state->x; + oldw = dw->dvi.text_device_width; + ch[1] = '\0'; + for (; *chars; chars++) { + ch[0] = *chars; + dw->dvi.state->x += PutCharacter (dw, ch); + } + dw->dvi.state->x = oldx; + dw->dvi.text_device_width = oldw + wid; + return 1; +} + +void +PutNumberedCharacter (DviWidget dw, int c) +{ + char *name; + int wid; + DviCharNameMap *map; + + if (!dw->dvi.display_enable) + return; + + if (dw->dvi.device_font == 0 + || dw->dvi.state->font_number != dw->dvi.device_font_number) { + dw->dvi.device_font_number = dw->dvi.state->font_number; + dw->dvi.device_font + = QueryDeviceFont (dw, dw->dvi.device_font_number); + } + + if (dw->dvi.device_font == 0 + || !device_code_width (dw->dvi.device_font, + dw->dvi.state->font_size, c, &wid)) + return; + if (dw->dvi.native) { + DoCharacter (dw, c, wid); + return; + } + map = QueryFontMap (dw, dw->dvi.state->font_number); + if (!map) + return; + for (name = device_name_for_code (dw->dvi.device_font, c); + name; + name = device_name_for_code ((DeviceFont *)0, c)) { + int code = DviCharIndex (map, name); + if (code >= 0) { + DoCharacter (dw, code, wid); + break; + } + if (FakeCharacter (dw, name, wid)) + break; + } +} + +/* unused */ +#if 0 +static void +ClearPage (DviWidget dw) +{ + XClearWindow (XtDisplay (dw), XtWindow (dw)); +} +#endif + +static void +setGC (DviWidget dw) +{ + int desired_line_width; + + if (dw->dvi.line_thickness < 0) + desired_line_width = (int)(((double)dw->dvi.device_resolution + * dw->dvi.state->font_size) + / (10.0*72.0*dw->dvi.sizescale)); + else + desired_line_width = dw->dvi.line_thickness; + + if (desired_line_width != dw->dvi.line_width) { + XGCValues values; + values.line_width = DeviceToX(dw, desired_line_width); + if (values.line_width == 0) + values.line_width = 1; + XChangeGC(XtDisplay (dw), dw->dvi.normal_GC, + GCLineWidth, &values); + dw->dvi.line_width = desired_line_width; + } +} + +static void +setFillGC (DviWidget dw) +{ + int fill_type; + unsigned long mask = GCFillStyle | GCForeground; + + fill_type = (dw->dvi.fill * 10) / (DVI_FILL_MAX + 1); + if (dw->dvi.fill_type != fill_type) { + XGCValues values; + if (fill_type <= 0) { + values.foreground = dw->dvi.background; + values.fill_style = FillSolid; + } else if (fill_type >= 9) { + values.foreground = dw->dvi.foreground; + values.fill_style = FillSolid; + } else { + values.foreground = dw->dvi.foreground; + values.fill_style = FillOpaqueStippled; + values.stipple = dw->dvi.gray[fill_type - 1]; + mask |= GCStipple; + } + XChangeGC(XtDisplay (dw), dw->dvi.fill_GC, mask, &values); + dw->dvi.fill_type = fill_type; + } +} + +void +DrawLine (DviWidget dw, int x, int y) +{ + int xp, yp; + + AdjustCacheDeltas (dw); + setGC (dw); + xp = XPos (dw); + yp = YPos (dw); + XDrawLine (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC, + xp, yp, + xp + DeviceToX (dw, x), yp + DeviceToX (dw, y)); +} + +void +DrawCircle (DviWidget dw, int diam) +{ + int d; + + AdjustCacheDeltas (dw); + setGC (dw); + d = DeviceToX (dw, diam); + XDrawArc (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC, + XPos (dw), YPos (dw) - d/2, + d, d, 0, 64*360); +} + +void +DrawFilledCircle (DviWidget dw, int diam) +{ + int d; + + AdjustCacheDeltas (dw); + setFillGC (dw); + d = DeviceToX (dw, diam); + XFillArc (XtDisplay (dw), XtWindow (dw), dw->dvi.fill_GC, + XPos (dw), YPos (dw) - d/2, + d, d, 0, 64*360); + XDrawArc (XtDisplay (dw), XtWindow (dw), dw->dvi.fill_GC, + XPos (dw), YPos (dw) - d/2, + d, d, 0, 64*360); +} + +void +DrawEllipse (DviWidget dw, int a, int b) +{ + AdjustCacheDeltas (dw); + setGC (dw); + XDrawArc (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC, + XPos (dw), YPos (dw) - DeviceToX (dw, b/2), + DeviceToX (dw, a), DeviceToX (dw, b), 0, 64*360); +} + +void +DrawFilledEllipse (DviWidget dw, int a, int b) +{ + AdjustCacheDeltas (dw); + setFillGC (dw); + XFillArc (XtDisplay (dw), XtWindow (dw), dw->dvi.fill_GC, + XPos (dw), YPos (dw) - DeviceToX (dw, b/2), + DeviceToX (dw, a), DeviceToX (dw, b), 0, 64*360); + XDrawArc (XtDisplay (dw), XtWindow (dw), dw->dvi.fill_GC, + XPos (dw), YPos (dw) - DeviceToX (dw, b/2), + DeviceToX (dw, a), DeviceToX (dw, b), 0, 64*360); +} + +void +DrawArc (DviWidget dw, int x_0, int y_0, int x_1, int y_1) +{ + int angle1, angle2; + int rad = (int)((sqrt ((double)x_0*x_0 + (double)y_0*y_0) + + sqrt ((double)x_1*x_1 + (double)y_1*y_1) + + 1.0)/2.0); + if ((x_0 == 0 && y_0 == 0) || (x_1 == 0 && y_1 == 0)) + return; + angle1 = (int)(atan2 ((double)y_0, (double)-x_0)*180.0*64.0/M_PI); + angle2 = (int)(atan2 ((double)-y_1, (double)x_1)*180.0*64.0/M_PI); + + angle2 -= angle1; + if (angle2 < 0) + angle2 += 64*360; + + AdjustCacheDeltas (dw); + setGC (dw); + + rad = DeviceToX (dw, rad); + XDrawArc (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC, + XPos (dw) + DeviceToX (dw, x_0) - rad, + YPos (dw) + DeviceToX (dw, y_0) - rad, + rad*2, rad*2, angle1, angle2); +} + +void +DrawPolygon (DviWidget dw, int *v, int n) +{ + XPoint *p; + int i; + int dx, dy; + + n /= 2; + + AdjustCacheDeltas (dw); + setGC (dw); + p = (XPoint *)XtMalloc((n + 2)*sizeof(XPoint)); + p[0].x = XPos (dw); + p[0].y = YPos (dw); + dx = 0; + dy = 0; + for (i = 0; i < n; i++) { + dx += v[2*i]; + p[i + 1].x = DeviceToX (dw, dx) + p[0].x; + dy += v[2*i + 1]; + p[i + 1].y = DeviceToX (dw, dy) + p[0].y; + } + p[n+1].x = p[0].x; + p[n+1].y = p[0].y; + XDrawLines (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC, + p, n + 2, CoordModeOrigin); + XtFree((char *)p); +} + +void +DrawFilledPolygon (DviWidget dw, int *v, int n) +{ + XPoint *p; + int i; + int dx, dy; + + n /= 2; + if (n < 2) + return; + + AdjustCacheDeltas (dw); + setFillGC (dw); + p = (XPoint *)XtMalloc((n + 2)*sizeof(XPoint)); + p[0].x = p[n+1].x = XPos (dw); + p[0].y = p[n+1].y = YPos (dw); + dx = 0; + dy = 0; + for (i = 0; i < n; i++) { + dx += v[2*i]; + p[i + 1].x = DeviceToX (dw, dx) + p[0].x; + dy += v[2*i + 1]; + p[i + 1].y = DeviceToX (dw, dy) + p[0].y; + } + XFillPolygon (XtDisplay (dw), XtWindow (dw), dw->dvi.fill_GC, + p, n + 1, Complex, CoordModeOrigin); + XDrawLines (XtDisplay (dw), XtWindow (dw), dw->dvi.fill_GC, + p, n + 2, CoordModeOrigin); + XtFree((char *)p); +} + +#define POINTS_MAX 10000 + +static void +appendPoint(XPoint *points, int *pointi, int x, int y) +{ + if (*pointi < POINTS_MAX) { + points[*pointi].x = x; + points[*pointi].y = y; + *pointi += 1; + } +} + +#define FLATNESS 1 + +static void +flattenCurve(XPoint *points, int *pointi, + int x_2, int y_2, int x_3, int y_3, int x_4, int y_4) +{ + int x_1, y_1, dx, dy, n1, n2, n; + + x_1 = points[*pointi - 1].x; + y_1 = points[*pointi - 1].y; + + dx = x_4 - x_1; + dy = y_4 - y_1; + + n1 = dy*(x_2 - x_1) - dx*(y_2 - y_1); + n2 = dy*(x_3 - x_1) - dx*(y_3 - y_1); + if (n1 < 0) + n1 = -n1; + if (n2 < 0) + n2 = -n2; + n = n1 > n2 ? n1 : n2; + + if (n*n / (dy*dy + dx*dx) <= FLATNESS*FLATNESS) + appendPoint (points, pointi, x_4, y_4); + else { + flattenCurve (points, pointi, + (x_1 + x_2)/2, + (y_1 + y_2)/2, + (x_1 + x_2*2 + x_3)/4, + (y_1 + y_2*2 + y_3)/4, + (x_1 + 3*x_2 + 3*x_3 + x_4)/8, + (y_1 + 3*y_2 + 3*y_3 + y_4)/8); + flattenCurve (points, pointi, + (x_2 + x_3*2 + x_4)/4, + (y_2 + y_3*2 + y_4)/4, + (x_3 + x_4)/2, + (y_3 + y_4)/2, + x_4, + y_4); + } +} + +void +DrawSpline (DviWidget dw, int *v, int n) +{ + int sx, sy, tx, ty; + int ox, oy, dx, dy; + int i; + int pointi; + XPoint points[POINTS_MAX]; + + if (n == 0 || (n & 1) != 0) + return; + AdjustCacheDeltas (dw); + setGC (dw); + ox = XPos (dw); + oy = YPos (dw); + dx = v[0]; + dy = v[1]; + sx = ox; + sy = oy; + tx = sx + DeviceToX (dw, dx); + ty = sy + DeviceToX (dw, dy); + + pointi = 0; + + appendPoint (points, &pointi, sx, sy); + appendPoint (points, &pointi, (sx + tx)/2, (sy + ty)/2); + + for (i = 2; i < n; i += 2) { + int ux = ox + DeviceToX (dw, dx += v[i]); + int uy = oy + DeviceToX (dw, dy += v[i+1]); + flattenCurve (points, &pointi, + (sx + tx*5)/6, (sy + ty*5)/6, + (tx*5 + ux)/6, (ty*5 + uy)/6, + (tx + ux)/2, (ty + uy)/2); + sx = tx; + sy = ty; + tx = ux; + ty = uy; + } + + appendPoint (points, &pointi, tx, ty); + + XDrawLines (XtDisplay (dw), XtWindow (dw), dw->dvi.normal_GC, + points, pointi, CoordModeOrigin); +} + + +/* +Local Variables: +c-indent-level: 8 +c-continued-statement-offset: 8 +c-brace-offset: -8 +c-argdecl-indent: 8 +c-label-offset: -8 +c-tab-always-indent: nil +End: +*/ |