/* * Draw.c -- Implementation of common drawing routines. * * Authors : Patrick Lecoanet. * Creation date : Sat Dec 10 12:51:30 1994 * * $Id$ */ /* * Copyright (c) 1993 - 1999 CENA, Patrick Lecoanet -- * * This code is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This code is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this code; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ /* ********************************************************************************** * * The algorihms used to draw the arrows, to do the 3d effects and to * smooth the polygons are adapted from Tk. * ********************************************************************************** */ #include "config.h" #include "Types.h" #include "Draw.h" #include "Geo.h" #include "List.h" #include "WidgetInfo.h" #include "Image.h" #include #include #define POLYGON_RELIEF_DRAW 0 #define POLYGON_RELIEF_RENDER 1 #define POLYGON_RELIEF_DIST 2 #define POLYGON_RELIEF_BBOX 3 #define POLYGON_RELIEF_IN_BBOX 4 #define TOP_CONTRAST 13 #define BOTTOM_CONTRAST 6 #define MAX_INTENSITY 65535 #define ARROW_SHAPE_B 10.0 #define ARROW_SHAPE_C 5.0 #define OPEN_ARROW_SHAPE_A 4.0 #define CLOSED_ARROW_SHAPE_A ARROW_SHAPE_B #define LIGHTNING_SHAPE_A_RATIO 10.0 #define LIGHTNING_SHAPE_B_RATIO 8.0 /* ********************************************************************************** * * SetLineStyle -- * ********************************************************************************** */ void SetLineStyle(WidgetInfo *wi, LineStyle line_style) { if (wi->render) { #ifdef GLX switch (line_style) { case LINE_DASHED : glLineStipple(1, 0xF0F0); glEnable(GL_LINE_STIPPLE); break; case LINE_MIXED : glLineStipple(1, 0x27FF); glEnable(GL_LINE_STIPPLE); break; case LINE_DOTTED : glLineStipple(1, 0x18C3); glEnable(GL_LINE_STIPPLE); break; default: glDisable(GL_LINE_STIPPLE); } #endif } else { XGCValues values; static const char dashed[] = { 8 }; static const char dotted[] = { 2, 5 }; static const char mixed[] = { 8, 5, 2, 5 }; values.line_style = LineOnOffDash; switch (line_style) { case LINE_DASHED : XSetDashes(wi->dpy, wi->gc, 0, dashed, 1); break; case LINE_MIXED : XSetDashes(wi->dpy, wi->gc, 0, mixed, 4); break; case LINE_DOTTED : XSetDashes(wi->dpy, wi->gc, 0, dotted, 2); break; default: values.line_style = LineSolid; break; } XChangeGC(wi->dpy, wi->gc, GCLineStyle, &values); } } /* ********************************************************************************** * * GetLineShape -- * Compute the points describing the given line shape between point p1 and p2. * If bbox is non null, it is filled with the bounding box of the shape. * * For the time being this procedure handles straight lines, right and left * lightnings, right and left corners, right and left double corners.. * * * Here are the parameters for lightnings: * * ******* * ******* * * ****** * * ****** ******+ * * ****** ****** * *| * ****** ****** * * | LIGHTNING_SHAPE_A * ****** ****** * * | * ****** * * | * ..******.........................+.+.*........................******.. * | * * ****** * | * * ****** ****** * | * * ****** ****** * | * * ****** ****** * | * ******* ****** * | * ****** * | * ****** * | ******** * | | | | * | |----| | LIGHTNING_SHAPE_B * | | * |--------------------------------| LENGTH / 2 * ********************************************************************************** */ void GetLineShape(ZnPoint *p1, ZnPoint *p2, unsigned int line_width, LineShape shape, ZnBBox *bbox, ZnList to_points) { ZnPoint *points; int num_points, i; /* * Compute all line points according to shape. */ if ((shape == LINE_LEFT_LIGHTNING) || (shape == LINE_RIGHT_LIGHTNING)) { double alpha, theta; double length, length2; double shape_a, shape_b; double dx, dy; double temp; num_points = LIGHTNING_POINTS; ZnListAssertSize(to_points, num_points); points = (ZnPoint *) ZnListArray(to_points); points[0] = *p1; points[3] = *p2; dx = p2->x - p1->x; dy = p2->y - p1->y; length = hypot(dx, dy); shape_a = length/LIGHTNING_SHAPE_A_RATIO + ((double) line_width)/2; shape_b = length/LIGHTNING_SHAPE_B_RATIO + ((double) line_width)/2; if (shape == LINE_LEFT_LIGHTNING) alpha = atan2(shape_a, shape_b); else alpha = -atan2(shape_a, shape_b); length2 = hypot(shape_a, shape_b); theta = atan2(-dy, dx); dx = p1->x + dx/2; dy = p1->y + dy/2; temp = cos(theta + alpha) * length2; points[1].x = dx + temp; points[2].x = dx - temp; temp = sin(theta + alpha) * length2; points[1].y = dy - temp; points[2].y = dy + temp; } else if (shape == LINE_LEFT_CORNER || shape == LINE_RIGHT_CORNER) { num_points = CORNER_POINTS; ZnListAssertSize(to_points, num_points); points = (ZnPoint *) ZnListArray(to_points); points[0] = *p1; points[2] = *p2; if (shape == LINE_LEFT_CORNER) { points[1].x = p1->x; points[1].y = p2->y; } else { points[1].x = p2->x; points[1].y = p1->y; } } else if (shape == LINE_DOUBLE_LEFT_CORNER || shape == LINE_DOUBLE_RIGHT_CORNER) { int dx, dy; num_points = DOUBLE_CORNER_POINTS; ZnListAssertSize(to_points, num_points); points = (ZnPoint *) ZnListArray(to_points); points[0] = *p1; points[3] = *p2; if (shape == LINE_DOUBLE_LEFT_CORNER) { dy = p2->y - p1->y; points[1].x = p1->x; points[2].x = p2->x; points[1].y = points[2].y = p1->y + dy/2; } else { dx = p2->x - p1->x; points[1].x = points[2].x = p1->x + dx/2; points[1].y = p1->y; points[2].y = p2->y; } } else /* if (shape) == LINE_STRAIGHT) */ { num_points = STRAIGHT_POINTS; ZnListAssertSize(to_points, num_points); points = (ZnPoint *) ZnListArray(to_points); points[0] = *p1; points[1] = *p2; } /* * Fill in the bbox, if requested. */ if (bbox) { ResetBBox(bbox); for (i = 0; i < num_points; i++) { AddPointToBBox(bbox, points[i].x, points[i].y); } /* Enlarge to take line_width into account. */ if (line_width > 1) { int lw_2 = (line_width+1)/2; bbox->orig.x -= lw_2; bbox->orig.y -= lw_2; bbox->corner.x += lw_2; bbox->corner.y += lw_2; } } } /* ********************************************************************************** * * DrawLineShape -- * Draw a line given the points describing its path. It is designed to work * with GetLineShape albeit it does fairly trivial things. In the future some * shapes might need cooperation between the two and the clients will be ready * for that. * * ********************************************************************************** */ void DrawLineShape(WidgetInfo *wi, ZnPoint *p, int num_p, LineStyle line_style, ZnColor foreground, unsigned int line_width, LineShape shape) { XPoint *xpoints; int i; XGCValues values; /* * Setup GC. */ SetLineStyle(wi, line_style); values.foreground = ZnPixel(foreground); values.line_width = (line_width == 1) ? 0 : line_width; values.fill_style = FillSolid; values.join_style = JoinRound; values.cap_style = CapRound; XChangeGC(wi->dpy, wi->gc, GCFillStyle|GCLineWidth|GCJoinStyle|GCCapStyle|GCForeground, &values); ZnListAssertSize(wi->work_xpts, num_p); xpoints = (XPoint *) ZnListArray(wi->work_xpts); for (i = 0; i < num_p; i++) { xpoints[i].x = p[i].x; xpoints[i].y = p[i].y; } XDrawLines(wi->dpy, wi->draw_buffer, wi->gc, xpoints, num_p, CoordModeOrigin); } /* * ReliefIndexOfSegment -- */ static XColor * ReliefColorOfSegment(ZnReal x1, ZnReal y1, ZnReal x2, ZnReal y2, ReliefStyle relief, ZnGradient *gradient, WidgetInfo *wi) { ZnReal angle, angle_step, origin, position; int num_colors, color_index; num_colors = RELIEF_STEPS*2+1; angle_step = M_PI / (num_colors-1); origin = -(DegreesToRadian(wi->light_angle))-(angle_step/2.0); if (relief == RELIEF_BEVEL_IN) { origin += M_PI; } angle = ProjectionToAngle(y1 - y2, x2 - x1) + M_PI - origin; while (angle < 0.0) { angle += 2*M_PI; } while (angle > 2*M_PI) { angle -= 2*M_PI; } color_index = (int) (angle/angle_step); if (color_index > num_colors-1) { color_index = 2*(num_colors-1)-color_index; } if ((color_index < 0) || (color_index >= num_colors)) { printf("Color index out of gradient (should not happen).\n"); if (color_index < 0) { color_index = 0; } if (color_index >= num_colors) { color_index = num_colors-1; } } position = 100*color_index/num_colors; /*printf("position %g, angle %g(%g), origin %g\n", position, RadianToDegrees(angle), angle, RadianToDegrees(origin));*/ return ZnGetGradientColor(gradient, position, NULL); } /* ********************************************************************************** * * DrawRectangleRelief -- * Draw the bevels inside bbox. * ********************************************************************************** */ void DrawRectangleRelief(WidgetInfo *wi, ReliefStyle relief, ZnGradient *gradient, XRectangle *bbox, unsigned int line_width) { XPoint bevel[4]; /* * If we haven't enough space to draw, exit. */ if ((bbox->width < 2*line_width) || (bbox->height < 2*line_width)) { return; } /* * Grooves and ridges are drawn with two recursives calls with * half the width of the original one. */ if ((relief == RELIEF_RIDGE) || (relief == RELIEF_GROOVE)) { unsigned int new_line_width; int offset; XRectangle internal_bbox; new_line_width = line_width/2; offset = line_width - new_line_width; DrawRectangleRelief(wi, (relief==RELIEF_GROOVE)?RELIEF_BEVEL_IN:RELIEF_BEVEL_OUT, gradient, bbox, new_line_width); internal_bbox = *bbox; internal_bbox.x +=offset; internal_bbox.y += offset; internal_bbox.width -= offset*2; internal_bbox.height -= offset*2; DrawRectangleRelief(wi, (relief==RELIEF_GROOVE)?RELIEF_BEVEL_OUT:RELIEF_BEVEL_IN, gradient, &internal_bbox, new_line_width); return; } XSetFillStyle(wi->dpy, wi->gc, FillSolid); bevel[0].x = bbox->x; bevel[0].y = bevel[1].y = bbox->y; bevel[1].x = bbox->x + bbox->width; bevel[2].y = bevel[3].y = bbox->y + line_width; bevel[2].x = bevel[1].x - line_width; bevel[3].x = bevel[0].x + line_width; XSetForeground(wi->dpy, wi->gc, ZnPixel(ReliefColorOfSegment(bevel[1].x, bevel[1].y, bevel[0].x, bevel[0].y, relief, gradient, wi))); XFillPolygon(wi->dpy, wi->draw_buffer, wi->gc, bevel, 4, Convex, CoordModeOrigin); bevel[0] = bevel[1]; bevel[3] = bevel[2]; bevel[1].y += bbox->height; bevel[2].y = bevel[1].y - line_width; XSetForeground(wi->dpy, wi->gc, ZnPixel(ReliefColorOfSegment(bevel[1].x, bevel[1].y, bevel[0].x, bevel[0].y, relief, gradient, wi))); XFillPolygon(wi->dpy, wi->draw_buffer, wi->gc, bevel, 4, Convex, CoordModeOrigin); bevel[0] = bevel[1]; bevel[3] = bevel[2]; bevel[1].x -= bbox->width; bevel[2].x = bevel[1].x + line_width; XSetForeground(wi->dpy, wi->gc, ZnPixel(ReliefColorOfSegment(bevel[1].x, bevel[1].y, bevel[0].x, bevel[0].y, relief, gradient, wi))); XFillPolygon(wi->dpy, wi->draw_buffer, wi->gc, bevel, 4, Convex, CoordModeOrigin); bevel[0] = bevel[1]; bevel[3] = bevel[2]; bevel[1].x = bbox->x; bevel[1].y = bbox->y; bevel[2].x = bevel[3].x; bevel[2].y = bbox->y + line_width; XSetForeground(wi->dpy, wi->gc, ZnPixel(ReliefColorOfSegment(bevel[1].x, bevel[1].y, bevel[0].x, bevel[0].y, relief, gradient, wi))); XFillPolygon(wi->dpy, wi->draw_buffer, wi->gc, bevel, 4, Convex, CoordModeOrigin); } static void DoPolygon(ZnPoint *p, int num_points, int line_width, ZnBool (*cb)(ZnPoint *bevels, int proc_points, void *closure), void *closure) { int i, processed_points; ZnPoint *p1, *p11=NULL, *p2; ZnPoint pp1, pp2, new_pp1, new_pp2; ZnPoint perp, c, shift1, shift2; ZnPoint bevel_points[4]; ZnBool folded, closed, colinear; ZnReal dx, dy; /* * If the polygon is closed (last point is the same as first) open it by * dropping the last point. The algorithm closes the path automatically. * We remember this to decide if we draw the last bevel or not and if we * need to generate ends perpendicular to the path.. */ closed = False; if ((p->x == p[num_points-1].x) && (p->y == p[num_points-1].y)) { closed = True; num_points--; } /*printf("num_points=%d(%s)\n", num_points, closed?"closed":"");*/ /* * We loop on all vertices of the polygon. * At each step we try to compute the corresponding border * corner `corner'. Then we build a polygon for the bevel. * Things look like this: * * bevel[1] / * * / * | / * | / * pp1 * * p[i-1] * | | bevel[0] * | | * | | * | | bevel[3] * | | p[i] * | | p1 p2 * pp2 * *--------------------* * | * | * corner *----*--------------------* * bevel[2] new_pp1 new_pp2 * * pp1 and pp2 are the ends of a segment // to p1 p2 at line_width * from it. These points are *NOT* necessarily on the perpendicular * going through p1 or p2. * This loop needs a bootstrap phase of two iterations (i.e we need to * process two points). This is why we start at the point before the last * and then wrap to the first point. * The algorithm discards any duplicate contiguous points. * It makes a special case if two consecutives edges are folded: * * bevel[1] pp1 pp2 a bevel[2] * *-----------*--------------*----------* * \ * \ * p[i-1] \ bevel[3] * *--------*-------------------------*---* corner * bevel[0] p2 p1 / * / * / * ----------*-----------*-------------* * new_pp1 new_pp2 c * * In such a case we need to compute a, c, corner from pp1, pp2, new_pp1 * and new_pp2. We compute the perpendicular to p1,p2 through p1, intersect * it with pp1,pp2 to obtain a, intersect it with new_pp1, new_pp2 to * obtain c, shift a,c and intersect it with p1,p2 to obtain corner. * */ processed_points = 0; if (!closed) { i = 0; p1 = p; } else { i = -2; p1 = &p[num_points-2]; } for (p2 = p1+1; i < num_points; i++, p2++) { /* * When it is time to wrap, do it */ if ((i == -1) || (i == num_points-1)) { p2 = p; } /* * Skip over close vertices. */ dx = p2->x - p1->x; dy = p2->y - p1->y; if ((ABS(dx) < 1.0) && (ABS(dy) < 1.0)) { continue; } ShiftLine(p1, p2, line_width, &new_pp1, &new_pp2); bevel_points[3] = *p1; folded = False; colinear = False; /* * The first two cases are for `open' polygons. We compute * a bevel closure that is perpendicular to the path. */ if ((processed_points == 0) && !closed) { perp.x = p1->x + (p2->y - p1->y); perp.y = p1->y - (p2->x - p1->x); IntersectLines(p1, &perp, &new_pp1, &new_pp2, &bevel_points[2]); } else if ((processed_points == num_points-1) && !closed) { perp.x = p1->x + (p11->y - p1->y); perp.y = p1->y - (p11->x - p1->x); IntersectLines(p1, &perp, &pp1, &pp2, &bevel_points[2]); } else if (processed_points >= 1) { ZnReal dotp, dist, odx, ody; /* * The dot product of the two faces tell if the are * folded or colinear. The */ odx = p11->x - p1->x; ody = p11->y - p1->y; dotp = odx*dx + ody*dy; dist = LineToPointDist(p11, p2, p1); if ((dist < 4.0) && (dotp <= 0)) { perp.x = p1->x + (p2->y - p1->y); perp.y = p1->y - (p2->x - p1->x); IntersectLines(p1, &perp, &new_pp1, &new_pp2, &bevel_points[2]); colinear = True; } else { folded = !IntersectLines(&new_pp1, &new_pp2, &pp1, &pp2, &bevel_points[2]); /*printf("new_pp1 %g@%g, new_pp2 %g@%g, pp1 %g@%g, pp2 %g@%g, inter %g@%g\n", new_pp1.x, new_pp1.y, new_pp2.x, new_pp2.y, pp1.x, pp1.y, pp2.x, pp2.y, bevel_points[2].x, bevel_points[2].y);*/ folded = folded && (dotp < 0); if (folded) { /*printf("DoPolygonRelief: folded edges detected, %g@%g, %g@%g, %g@%g, %g@%g\n", pp1.x, pp1.y, pp2.x, pp2.y, new_pp1.x, new_pp1.y, new_pp2.x, new_pp2.y);*/ perp.x = p1->x + (p2->y - p1->y); perp.y = p1->y - (p2->x - p1->x); IntersectLines(p1, &perp, &pp1, &pp2, &bevel_points[2]); IntersectLines(p1, &perp, &new_pp1, &new_pp2, &c); ShiftLine(p1, &perp, line_width, &shift1, &shift2); IntersectLines(p1, p2, &shift1, &shift2, &bevel_points[3]); } } } if ((processed_points >= 2) || (!closed && (processed_points == 1))) { if ((*cb)(bevel_points, processed_points, closure)) { return; } } p11 = p1; p1 = p2; pp1 = new_pp1; pp2 = new_pp2; bevel_points[0] = bevel_points[3]; if (folded) { bevel_points[1] = c; } else if ((processed_points >= 1) || !closed) { bevel_points[1] = bevel_points[2]; } processed_points++; } } typedef struct { WidgetInfo *wi; ZnPoint *pp; double dist; ZnBBox *bbox; ReliefStyle relief; ZnGradient *gradient; int alpha; ZnBool smooth; int result; ZnBool toggle; } PolygonData; /* ********************************************************************************** * * GetPolygonReliefBBox -- * Returns the bevelled polygon bounding box. * ********************************************************************************** */ static ZnBool PolygonBBoxCB(ZnPoint *bevels, int proc_points, void *closure) { ZnBBox *bbox = closure; int i; for (i = 0; i < 4; i++) { AddPointToBBox(bbox, bevels[i].x, bevels[i].y); } return 0; } void GetPolygonReliefBBox(ZnPoint *points, int num_points, int line_width, ZnBBox *bbox) { ResetBBox(bbox); DoPolygon(points, num_points, line_width, PolygonBBoxCB, bbox); } /* ********************************************************************************** * * PolygonReliefInBBox -- * Returns (-1) if the relief is entirely outside the bbox, (1) if it is * entirely inside or (0) if in between * ********************************************************************************** */ static ZnBool PolygonInBBoxCB(ZnPoint *bevels, int proc_points, void *closure) { PolygonData *pd = closure; if (proc_points <= 2) { pd->result = PolygonInBBox(bevels, 4, pd->bbox, NULL); if (pd->result == 0) { return 1; } } else { if (PolygonInBBox(bevels, 4, pd->bbox, NULL) != pd->result) { pd->result = 0; return 1; } } return 0; } int PolygonReliefInBBox(ZnPoint *points, int num_points, int line_width, ZnBBox *area) { PolygonData pd; pd.bbox = area; DoPolygon(points, num_points, line_width, PolygonInBBoxCB, &pd); return pd.result; } /* ********************************************************************************** * * PolygonReliefToPointDist -- * Returns the distance between the given point and * the bevelled polygon. * ********************************************************************************** */ static ZnBool PolygonDistCB(ZnPoint *bevels, int proc_points, void *closure) { PolygonData *pd = closure; double new_dist; new_dist = PolygonToPointDist(bevels, 4, pd->pp); if (new_dist < 0.0) { new_dist = 0.0; } if (new_dist < pd->dist) { pd->dist = new_dist; } return 0; } double PolygonReliefToPointDist(ZnPoint *points, int num_points, int line_width, ZnPoint *pp) { PolygonData pd; pd.dist = 1.0e40; pd.pp = pp; DoPolygon(points, num_points, line_width, PolygonDistCB, &pd); return pd.dist; } /* ********************************************************************************** * * DrawPolygonRelief -- * Draw the bevels around path. * ********************************************************************************** */ static ZnBool PolygonDrawCB(ZnPoint *bevels, int proc_points, void *closure) { PolygonData *pd = closure; WidgetInfo *wi = pd->wi; XPoint bevel_xpoints[5]; XGCValues values; int j; #if 1 values.foreground = ZnPixel(ReliefColorOfSegment(bevels[0].x, bevels[0].y, bevels[3].x, bevels[3].y, pd->relief, pd->gradient, pd->wi)); #endif #if 0 values.foreground = toggle ? WhitePixelOfScreen(wi->screen):BlackPixelOfScreen(wi->screen); #endif #if 0 values.foreground = colinear ? WhitePixelOfScreen(wi->screen):BlackPixelOfScreen(wi->screen); #endif #if 0 toggle = !toggle; #endif values.fill_style = FillSolid; XChangeGC(wi->dpy, wi->gc, GCFillStyle|GCForeground, &values); for (j = 0; j < 4; j++) { bevel_xpoints[j].x = REAL_TO_INT(bevels[j].x); bevel_xpoints[j].y = REAL_TO_INT(bevels[j].y); } #if 1 XFillPolygon(wi->dpy, wi->draw_buffer, wi->gc, bevel_xpoints, 4, Convex, CoordModeOrigin); #endif #if 0 bevel_xpoints[4] = bevel_xpoints[0]; values.line_width = 0; XChangeGC(wi->dpy, wi->gc, GCLineWidth, &values); XDrawLines(wi->dpy, wi->draw_buffer, wi->gc, bevel_xpoints, 5, CoordModeOrigin); #endif return 0; } void DrawPolygonRelief(WidgetInfo *wi, ReliefStyle relief, ZnGradient *gradient, ZnPoint *points, int num_points, int line_width) { PolygonData pd; pd.wi = wi; pd.gradient = gradient; /* * Grooves and ridges are drawn with two calls. The first * with the original width, the second with half the width. */ if ((relief == RELIEF_RIDGE) || (relief == RELIEF_GROOVE)) { pd.relief = (relief==RELIEF_GROOVE)?RELIEF_BEVEL_OUT:RELIEF_BEVEL_IN; DoPolygon(points, num_points, line_width, PolygonDrawCB, &pd); pd.relief = (relief==RELIEF_GROOVE)?RELIEF_BEVEL_IN:RELIEF_BEVEL_OUT; DoPolygon(points, num_points, line_width/2, PolygonDrawCB, &pd); } else { pd.relief = relief; DoPolygon(points, num_points, line_width, PolygonDrawCB, &pd); } } /* ********************************************************************************** * * RenderPolygonRelief -- * Draw the bevels around path using alpha enabled rendering. * ********************************************************************************** */ #ifdef GLX static ZnBool PolygonRenderCB(ZnPoint *bevels, int proc_points, void *closure) { PolygonData *pd = closure; int i, a, b; XColor *color = ReliefColorOfSegment(bevels[0].x, bevels[0].y, bevels[3].x, bevels[3].y, pd->relief, pd->gradient, pd->wi); glColor4us(color->red, color->green, color->blue, pd->alpha); glBegin(GL_QUADS); for (i = 0; i < 4; i++) { a = REAL_TO_INT(bevels[i].x); b = REAL_TO_INT(bevels[i].y); glVertex2f(a, b); } glEnd(); return 0; } void RenderPolygonRelief(WidgetInfo *wi, ReliefStyle relief, ZnGradient *gradient, int alpha, ZnBool smooth, ZnPoint *points, int num_points, int line_width) { PolygonData pd; pd.wi = wi; pd.gradient = gradient; pd.alpha = ZnComposeAlpha(alpha, wi->alpha); pd.smooth = smooth; glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); /* * Grooves and ridges are drawn with two calls. The first * with the original width, the second with half the width. */ if ((relief == RELIEF_RIDGE) || (relief == RELIEF_GROOVE)) { pd.relief = (relief==RELIEF_GROOVE)?RELIEF_BEVEL_OUT:RELIEF_BEVEL_IN; DoPolygon(points, num_points, line_width, PolygonRenderCB, &pd); pd.relief = (relief==RELIEF_GROOVE)?RELIEF_BEVEL_IN:RELIEF_BEVEL_OUT; DoPolygon(points, num_points, line_width/2, PolygonRenderCB, &pd); } else { pd.relief = relief; DoPolygon(points, num_points, line_width, PolygonRenderCB, &pd); } } void RenderPolyline(WidgetInfo *wi, ZnPoint *points, int num_points, int line_width, ZnBool closed, LineStyle line_style, int cap_style, int join_style, ZnLineEnd first_end, ZnLineEnd last_end, ZnGradient *gradient) { int num_clips = ZnListSize(wi->clip_stack); ZnPoint end_points[LINE_END_POINTS]; ZnBool need_rcaps, thin; int pass, i, k, m; ZnPoint c1, c2; XColor *color; int alpha; /* * The code below draws curves thiner than the min * of GL_ALIASED_LINE_WIDTH_RANGE and GL_ALIASED_POINT_SIZE_RANGE * with a mix of anti-aliased lines and points. The curves that * are thicker are drawn using regular polygons. * The joints are drawn only rounded, the caps can be either round * or butt (but not projecting). */ thin = ((line_width <= wi->max_line_width) && (line_width <= wi->max_point_width)); color = ZnGetGradientColor(gradient, 0, &alpha); alpha = ZnComposeAlpha(alpha, wi->alpha); glColor4us(color->red, color->green, color->blue, alpha); SetLineStyle(wi, line_style); glLineWidth(line_width); glPointSize(line_width); for (pass = 0; pass < 2; pass++) { if (pass == 0) { GLX_START_CLIP(num_clips, True); } else { GLX_RESTORE_STENCIL(num_clips, False); } if (first_end) { GetLineEnd(&points[0], &points[1], line_width, cap_style, first_end, end_points); glBegin(GL_TRIANGLE_FAN); for (m = 0; m < LINE_END_POINTS; m++) { glVertex2f(end_points[m].x, end_points[m].y); } glEnd(); } if (last_end) { GetLineEnd(&points[num_points-1], &points[num_points-2], line_width, cap_style, last_end, end_points); glBegin(GL_TRIANGLE_FAN); for (m = 0; m < LINE_END_POINTS; m++) { glVertex2f(end_points[m].x, end_points[m].y); } glEnd(); } if (thin) { glBegin(GL_LINE_STRIP); for (i = 0; i < num_points; i++) { glVertex2f(points[i].x, points[i].y); } glEnd(); } else { glBegin(GL_QUADS); for (i = 0; i < num_points-1; i++) { GetButtPoints(&points[i+1], &points[i], line_width, False, &c1, &c2); glVertex2f(c1.x, c1.y); glVertex2f(c2.x, c2.y); GetButtPoints(&points[i], &points[i+1], line_width, False, &c1, &c2); glVertex2f(c1.x, c1.y); glVertex2f(c2.x, c2.y); } glEnd(); } if (pass == 0) { GLX_RENDER_CLIPPED(); } else { GLX_END_CLIP(num_clips); return; } need_rcaps = ((line_width > 1) && (cap_style == CapRound) && !closed); i = 0; k = num_points; if (closed) { k--; } if (!need_rcaps || first_end) { i++; } if (!need_rcaps || last_end) { k--; } if (thin) { glBegin(GL_POINTS); for ( ; i < k; i++) { glVertex2f(points[i].x, points[i].y); } glEnd(); } else { int num_cpoints; ZnReal lw_2 = line_width / 2.0; ZnPoint *cpoints = GetCirclePoints(3, ZN_CIRCLE_COARSE, 0, 360, &num_cpoints, NULL); for ( ; i < k; i++) { glBegin(GL_TRIANGLE_FAN); glVertex2f(points[i].x, points[i].y); for (m = 0; m < num_cpoints; m++) { glVertex2f(points[i].x + cpoints[m].x*lw_2, points[i].y + cpoints[m].y*lw_2); } glEnd(); } } } } void RenderImage(WidgetInfo *wi, ImageBits *image, /* ImageBits or BitmapBits */ ZnGradient *gradient, ZnPoint *origin, ZnBool modulate) { ZnReal nx, ny; XColor *color; int alpha; color = ZnGetGradientColor(gradient, 0, &alpha); alpha = ZnComposeAlpha(alpha, wi->alpha); nx = origin->x + image->width; ny = origin->y + image->height; glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, image->texture); if (modulate) { glColor4us(color->red, color->green, color->blue, alpha); } else { glColor4us(65535, 65535, 65535, alpha); } glBegin(GL_QUADS); glTexCoord2f(0.0, 0.0); glVertex2f(origin->x, origin->y); glTexCoord2f(0.0, image->t); glVertex2f(origin->x, ny); glTexCoord2f(image->s, image->t); glVertex2f(nx, ny); glTexCoord2f(image->s, 0.0); glVertex2f(nx, origin->y); glEnd(); glDisable(GL_TEXTURE_2D); } void RenderTile(struct _WidgetInfo *wi, ImageBits *tile, ZnGradient *gradient, void cb(void *), void *closure, ZnPoint *quad) /* Right now it's a ZnBBox */ { ZnReal x, y, nx, ny, lx, ly, s, t; int alpha, num_clips = ZnListSize(wi->clip_stack); if (gradient) { ZnGetGradientColor(gradient, 0.0, &alpha); alpha = ZnComposeAlpha(alpha, wi->alpha); } else { alpha = ZnComposeAlpha(100, wi->alpha); } if (cb) { /* * Setup the stencil buffer with the shape to be drawn. */ GLX_START_CLIP(num_clips, False); (*cb)(closure); GLX_RESTORE_STENCIL(num_clips, True); } /* * Then texture map the quad through the shape. * The rectangle is drawn using quads, each * quad matching the size of the texture tile. */ glEnable(GL_TEXTURE_2D); glColor4us(65535, 65535, 65535, alpha); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glBindTexture(GL_TEXTURE_2D, tile->texture); y = quad[0].y; lx = quad[1].x; ly = quad[1].y; glBegin(GL_QUADS); do { x = quad[0].x; t = 1.0; ny = y + tile->height; if (ny > ly) { ny = ly; t = (ly - y) / (ZnReal) tile->height; } t *= tile->t; do { s = 1.0; nx = x + tile->width; if (nx > lx) { nx = lx; s = (lx - x) / (ZnReal) tile->width; } s *= tile->s; glTexCoord2f(0.0, 0.0); glVertex2f(x, y); glTexCoord2f(0.0, t); glVertex2f(x, ny); glTexCoord2f(s, t); glVertex2f(nx, ny); glTexCoord2f(s, 0.0); glVertex2f(nx, y); x = nx; } while (x != lx); y = ny; } while (y != ly); glEnd(); if (cb) { GLX_END_CLIP(num_clips); } glDisable(GL_TEXTURE_2D); } void ComputeAxialGradient(WidgetInfo *wi, ZnPoly *shape, int angle, ZnPoint *grad_geo) { ZnTransfo *transfo1, *transfo2; ZnBool *holes; ZnContour *c; ZnBBox bbox; ZnPoint *points, p[4]; int i; transfo1 = ZnTransfoNew(); transfo2 = ZnTransfoNew(); ZnRotateDeg(transfo1, angle); ZnRotateDeg(transfo2, -angle); holes = shape->holes; c = shape->contours; ResetBBox(&bbox); for (i = 0; i < shape->num_contours; i++, c++, holes++) { if (*holes) { continue; } ZnListAssertSize(wi->work_pts, c->num_points); points = ZnListArray(wi->work_pts); ZnTransformPoints(transfo1, c->points, points, c->num_points); AddPointsToBBox(&bbox, points, c->num_points); } p[0] = bbox.orig; p[2] = bbox.corner; p[1].x = p[2].x; p[1].y = p[0].y; p[3].x = p[0].x; p[3].y = p[2].y; ZnTransfoSetIdentity(transfo1); ZnTransfoCompose(transfo1, transfo2, wi->current_transfo); ZnTransformPoints(transfo1, p, grad_geo, 4); ZnTransfoFree(transfo1); ZnTransfoFree(transfo2); } void ComputeRadialGradient(WidgetInfo *wi, ZnPoly *shape, ZnBBox *bbox, ZnPoint *center, ZnPoint *grad_geo) { ZnReal dist, new, x, y, fact; ZnBool *holes; ZnContour *c; ZnPoint *points; ZnTransfo *transfo1; int i, j; ZnTransformPoint(wi->current_transfo, center, &grad_geo[0]); transfo1 = ZnTransfoNew(); ZnTranslate(transfo1, -grad_geo[0].x, -grad_geo[0].y); fact = (bbox->corner.x-bbox->orig.x)/(bbox->corner.y-bbox->orig.y); ZnScale(transfo1, 1.0, fact); dist = 0.0; holes = shape->holes; c = shape->contours; for (j = 0; j < shape->num_contours; j++, c++, holes++) { if (*holes) { continue; } ZnListAssertSize(wi->work_pts, c->num_points); points = ZnListArray(wi->work_pts); ZnTransformPoints(transfo1, c->points, points, c->num_points); for (i = 0; i < c->num_points; i++, points++) { x = points->x; y = points->y; new = x*x+y*y; if (new > dist) { dist = new; } } } grad_geo[1].x = sqrt(dist) + 2; /* Max radius plus a fuzz factor */ grad_geo[1].y = grad_geo[1].x / fact; ZnTransfoFree(transfo1); } void RenderGradient(struct _WidgetInfo *wi, ZnGradient *gradient, /* The grdient to be drawn (static * parameters). */ void cb(void *), /* A callback called to clip the shape * containing the gradient. */ void *closure, /* The callback parameter. */ ZnPoint *quad, /* The gradient geometric parameters * (dynamic). */ ZnPoly *poly /* Used only by ZN_PATH_GRADIENT */ ) { int alpha, angle, i, j; int type = gradient->type; XColor *color; ZnPoint p; ZnPoint dposa, dposb, dposc, dposd; ZnPoint dcontrol; ZnReal npos, pos, control; int num_clips = ZnListSize(wi->clip_stack); ZnPoint iquad[4]; if (!cb && (type == ZN_AXIAL_GRADIENT)) { /* Render an aligned * axial gradient in the quad */ angle = gradient->g.angle; /* * Adjust the quad for 90 180 and 270 degrees axial * gradients. Other angles not supported. */ switch (angle) { case 90: iquad[0] = quad[3]; iquad[3] = quad[2]; iquad[2] = quad[1]; iquad[1] = quad[0]; quad = iquad; break; case 180: iquad[0] = quad[2]; iquad[3] = quad[1]; iquad[2] = quad[0]; iquad[1] = quad[3]; quad = iquad; break; case 270: iquad[0] = quad[1]; iquad[3] = quad[0]; iquad[2] = quad[3]; iquad[1] = quad[2]; quad = iquad; break; } } if (cb) { /* * Draw the gradient shape in the stencil using the provided * callback (clipping). */ GLX_START_CLIP(num_clips, False); (*cb)(closure); GLX_RESTORE_STENCIL(num_clips, True); } if (type == ZN_AXIAL_GRADIENT) { /* * Then fill the axial gradient using the provided * quad and colors. The stencil will be restored * to its previous state in the process. */ glBegin(GL_QUAD_STRIP); for (i = 0; i < gradient->num_colors; i++) { color = gradient->colors[i].rgb; alpha = ZnComposeAlpha(gradient->colors[i].alpha, wi->alpha); glColor4us(color->red, color->green, color->blue, alpha); pos = gradient->colors[i].position; control = gradient->colors[i].control; dposa.x = (quad[1].x - quad[0].x)*pos/100.0; dposa.y = (quad[1].y - quad[0].y)*pos/100.0; p.x = quad[0].x + dposa.x; p.y = quad[0].y + dposa.y; glVertex2f(p.x, p.y); dposb.x = (quad[2].x - quad[3].x)*pos/100.0; dposb.y = (quad[2].y - quad[3].y)*pos/100.0; p.x = quad[3].x + dposb.x; p.y = quad[3].y + dposb.y; glVertex2f(p.x, p.y); if ((control != 50.0) && (i != gradient->num_colors-1)) { color = gradient->colors[i].mid_rgb; alpha = ZnComposeAlpha(gradient->colors[i].mid_alpha, wi->alpha); glColor4us(color->red, color->green, color->blue, alpha); npos = gradient->colors[i+1].position; dposc.x = (quad[1].x - quad[0].x)*npos/100.0; dposc.y = (quad[1].y - quad[0].y)*npos/100.0; dcontrol.x = (dposc.x - dposa.x)*control/100.0; dcontrol.y = (dposc.y - dposa.y)*control/100.0; p.x = quad[0].x + dposa.x + dcontrol.x; p.y = quad[0].y + dposa.y + dcontrol.y; glVertex2f(p.x, p.y); dposd.x = (quad[2].x - quad[3].x)*npos/100.0; dposd.y = (quad[2].y - quad[3].y)*npos/100.0; dcontrol.x = (dposd.x - dposb.x)*control/100.0; dcontrol.y = (dposd.y - dposb.y)*control/100.0; p.x = quad[3].x + dposb.x + dcontrol.x; p.y = quad[3].y + dposb.y + dcontrol.y; glVertex2f(p.x, p.y); } } glEnd(); } else if (type == ZN_RADIAL_GRADIENT) { ZnReal x, radiusx, radius2x, radius3x; ZnReal y, radiusy, radius2y, radius3y; int num_p, alpha2; ZnPoint *genarc; XColor *color2; genarc = GetCirclePoints(3, ZN_CIRCLE_FINE, 0, 360, &num_p, NULL); radiusx = 0; radiusy = 0; color = gradient->colors[0].rgb; alpha = ZnComposeAlpha(gradient->colors[0].alpha, wi->alpha); control = gradient->colors[0].control; for (j = 1; j < gradient->num_colors; j++) { radius2x = quad[1].x*gradient->colors[j].position/100.0; radius2y = quad[1].y*gradient->colors[j].position/100.0; if ((control != 50) && (j != gradient->num_colors-1)) { glBegin(GL_QUAD_STRIP); color2 = gradient->colors[j-1].mid_rgb; alpha2 = ZnComposeAlpha(gradient->colors[j-1].mid_alpha, wi->alpha); radius3x = radiusx + (radius2x-radiusx)*control/100.0; radius3y = radiusy + (radius2y-radiusy)*control/100.0; for (i = 0; i < num_p; i++) { x = quad[0].x+genarc[i].x*radiusx; y = quad[0].y+genarc[i].y*radiusy; glColor4us(color->red, color->green, color->blue, alpha); glVertex2f(x, y); x = quad[0].x+genarc[i].x*radius3x; y = quad[0].y+genarc[i].y*radius3y; glColor4us(color2->red, color2->green, color2->blue, alpha); glVertex2f(x, y); } radiusx = radius3x; radiusy = radius3y; color = color2; alpha = alpha2; glEnd(); } glBegin(GL_QUAD_STRIP); color2 = gradient->colors[j].rgb; alpha2 = ZnComposeAlpha(gradient->colors[j].alpha, wi->alpha); for (i = 0; i < num_p; i++) { x = quad[0].x+genarc[i].x*radiusx; y = quad[0].y+genarc[i].y*radiusy; glColor4us(color->red, color->green, color->blue, alpha); glVertex2f(x, y); x = quad[0].x+genarc[i].x*radius2x; y = quad[0].y+genarc[i].y*radius2y; glColor4us(color2->red, color2->green, color2->blue, alpha2); glVertex2f(x, y); } glEnd(); radiusx = radius2x; radiusy = radius2y; color = color2; alpha = alpha2; control = gradient->colors[j].control; } } else if (type == ZN_PATH_GRADIENT) { ZnPoint p, pp, p2, pp2, p3, pp3; int num_p, k, ii; ZnPoint *points; ZnReal position; for (k = 0; k < poly->num_contours; k++) { if (poly->holes[k]) { continue; } points = poly->contours[k].points; num_p = poly->contours[k].num_points; for (i = 0; i < num_p; i++) { if (i == num_p-1) { ii = 0; } else { ii = i+1; } glBegin(GL_QUAD_STRIP); p.x = p.y = pp.x = pp.y = 0; control = gradient->colors[0].control; position = gradient->colors[0].position; alpha = ZnComposeAlpha(gradient->colors[0].alpha, wi->alpha); color = gradient->colors[0].rgb; glColor4us(color->red, color->green, color->blue, alpha); glVertex2f(quad[0].x+p.x, quad[0].y+p.y); glVertex2f(quad[0].x+pp.x, quad[0].y+pp.y); for (j = 0; j < gradient->num_colors-1; j++) { position = gradient->colors[j+1].position; p2.x = (points[i].x-quad[0].x)*position/100.0; p2.y = (points[i].y-quad[0].y)*position/100.0; pp2.x = (points[ii].x-quad[0].x)*position/100.0; pp2.y = (points[ii].y-quad[0].y)*position/100.0; if (control != 50) { color = gradient->colors[j].mid_rgb; alpha = ZnComposeAlpha(gradient->colors[j].mid_alpha, wi->alpha); p3.x = p.x+(p2.x-p.x)*control/100.0; p3.y = p.y+(p2.y-p.y)*control/100.0; pp3.x = pp.x+(pp2.x-pp.x)*control/100.0; pp3.y = pp.y+(pp2.y-pp.y)*control/100.0; glColor4us(color->red, color->green, color->blue, alpha); glVertex2f(quad[0].x+p3.x, quad[0].y+p3.y); glVertex2f(quad[0].x+pp3.x, quad[0].y+pp3.y); } control = gradient->colors[j+1].control; alpha = ZnComposeAlpha(gradient->colors[j+1].alpha, wi->alpha); color = gradient->colors[j+1].rgb; p = p2; pp = pp2; glColor4us(color->red, color->green, color->blue, alpha); glVertex2f(quad[0].x+p.x, quad[0].y+p.y); glVertex2f(quad[0].x+pp.x, quad[0].y+pp.y); } glEnd(); } } } if (cb) { /* * Restore the previous GL state. */ GLX_END_CLIP(num_clips); } } void RenderHollowDot(struct _WidgetInfo *wi, ZnPoint *p, ZnReal size) { int num_clips = ZnListSize(wi->clip_stack); GLX_START_CLIP(num_clips, False); glPointSize(size-2); glBegin(GL_POINTS); glVertex2f(p->x, p->y); glEnd(); GLX_RENDER_CLIPPED(); glPointSize(size); glBegin(GL_POINTS); glVertex2f(p->x, p->y); glEnd(); GLX_RESTORE_STENCIL(num_clips, False); glBegin(GL_POINTS); glVertex2f(p->x, p->y); glEnd(); GLX_END_CLIP(num_clips); } #endif #ifdef GLX /* Copyright (c) Mark J. Kilgard, 1997. */ /* This program is freely distributable without licensing fees and is provided without guarantee or warrantee expressed or implied. This program is -not- in the public domain. */ #include static TexGlyphVertexInfo * getTCVI(TexFont *txf, int c) { TexGlyphVertexInfo *tgvi; /* Automatically substitute uppercase letters with lowercase if not uppercase available (and vice versa). */ if ((c >= txf->min_glyph) && (c < txf->min_glyph + txf->range)) { tgvi = txf->lut[c - txf->min_glyph]; if (tgvi) { return tgvi; } if (islower(c)) { c = toupper(c); if ((c >= txf->min_glyph) && (c < txf->min_glyph + txf->range)) { return txf->lut[c - txf->min_glyph]; } } if (isupper(c)) { c = tolower(c); if ((c >= txf->min_glyph) && (c < txf->min_glyph + txf->range)) { return txf->lut[c - txf->min_glyph]; } } } /* ZnWarning("Tried to access unavailable texture font character");*/ printf("Tried to access unavailable texture font character '%c'(\\0%o)\n", c, c); return txf->lut[(int)'!' - txf->min_glyph]; } void txfRenderGlyph(TexFont *txf, int c) { TexGlyphVertexInfo *tgvi; tgvi = getTCVI(txf, c); glBegin(GL_QUADS); glTexCoord2fv(tgvi->t0); glVertex2sv(tgvi->v0); glTexCoord2fv(tgvi->t1); glVertex2sv(tgvi->v1); glTexCoord2fv(tgvi->t2); glVertex2sv(tgvi->v2); glTexCoord2fv(tgvi->t3); glVertex2sv(tgvi->v3); glEnd(); glTranslatef(tgvi->advance, 0.0, 0.0); } void txfRenderString(TexFont *txf, unsigned char *string, int len) { int i; for (i = 0; i < len; i++) { txfRenderGlyph(txf, string[i]); } } enum { MONO, TOP_BOTTOM, LEFT_RIGHT, FOUR }; void txfRenderFancyString(TexFont *txf, unsigned char *string, int len) { TexGlyphVertexInfo *tgvi; GLubyte c[4][3]; int mode = MONO; int i; for (i = 0; i < len; i++) { if (string[i] == 27) { switch (string[i + 1]) { case 'M': mode = MONO; glColor3ubv((GLubyte *) & string[i + 2]); i += 4; break; case 'T': mode = TOP_BOTTOM; memcpy(c, &string[i + 2], 6); i += 7; break; case 'L': mode = LEFT_RIGHT; memcpy(c, &string[i + 2], 6); i += 7; break; case 'F': mode = FOUR; memcpy(c, &string[i + 2], 12); i += 13; break; } } else { switch (mode) { case MONO: txfRenderGlyph(txf, string[i]); break; case TOP_BOTTOM: tgvi = getTCVI(txf, string[i]); glBegin(GL_QUADS); glColor3ubv(c[0]); glTexCoord2fv(tgvi->t0); glVertex2sv(tgvi->v0); glTexCoord2fv(tgvi->t1); glVertex2sv(tgvi->v1); glColor3ubv(c[1]); glTexCoord2fv(tgvi->t2); glVertex2sv(tgvi->v2); glTexCoord2fv(tgvi->t3); glVertex2sv(tgvi->v3); glEnd(); glTranslatef(tgvi->advance, 0.0, 0.0); break; case LEFT_RIGHT: tgvi = getTCVI(txf, string[i]); glBegin(GL_QUADS); glColor3ubv(c[0]); glTexCoord2fv(tgvi->t0); glVertex2sv(tgvi->v0); glColor3ubv(c[1]); glTexCoord2fv(tgvi->t1); glVertex2sv(tgvi->v1); glColor3ubv(c[1]); glTexCoord2fv(tgvi->t2); glVertex2sv(tgvi->v2); glColor3ubv(c[0]); glTexCoord2fv(tgvi->t3); glVertex2sv(tgvi->v3); glEnd(); glTranslatef(tgvi->advance, 0.0, 0.0); break; case FOUR: tgvi = getTCVI(txf, string[i]); glBegin(GL_QUADS); glColor3ubv(c[0]); glTexCoord2fv(tgvi->t0); glVertex2sv(tgvi->v0); glColor3ubv(c[1]); glTexCoord2fv(tgvi->t1); glVertex2sv(tgvi->v1); glColor3ubv(c[2]); glTexCoord2fv(tgvi->t2); glVertex2sv(tgvi->v2); glColor3ubv(c[3]); glTexCoord2fv(tgvi->t3); glVertex2sv(tgvi->v3); glEnd(); glTranslatef(tgvi->advance, 0.0, 0.0); break; } } } } int txfInFont(TexFont * txf, int c) { /* NOTE: No uppercase/lowercase substituion. */ if ((c >= txf->min_glyph) && (c < txf->min_glyph + txf->range)) { if (txf->lut[c - txf->min_glyph]) { return 1; } } return 0; } #endif