/* * 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 #include #define POLYGON_RELIEF_DRAW 0 #define POLYGON_RELIEF_DIST 1 #define POLYGON_RELIEF_BBOX 2 #define POLYGON_RELIEF_IN_BBOX 3 #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(Display *display, GC gc, LineStyle line_style) { 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(display, gc, 0, dashed, 1); break; case LINE_MIXED : XSetDashes(display, gc, 0, mixed, 4); break; case LINE_DOTTED : XSetDashes(display, gc, 0, dotted, 2); break; default: values.line_style = LineSolid; break; } XChangeGC(display, 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->dpy, wi->gc, 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 long ReliefColorOfSegment(ZnReal x1, ZnReal y1, ZnReal x2, ZnReal y2, ReliefStyle relief, ZnColorGradient gradient, WidgetInfo *wi) { ZnReal angle, angle_step, origin; int color_index, num_colors; num_colors = ZnColorGradientSpan(gradient); 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; } } /*printf("color index %d(), angle %g(), origin %g\n", color_index, RadianToDegrees(angle), RadianToDegrees(origin));*/ return ZnPixel(ZnColorGradientColor(wi->win, gradient, color_index)); } /* ********************************************************************************** * * DrawRectangleRelief -- * Draw the bevels inside bbox. * ********************************************************************************** */ void DrawRectangleRelief(WidgetInfo *wi, ReliefStyle relief, ZnColorGradient 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, 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, 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, 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, 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 DoPolygonRelief(ZnPoint *p, int num_points, int line_width, int what_to_do, ...) { int i, j, processed_points, *result=NULL; ZnPoint *p1, *p11=NULL, *p2; ZnPoint pp1, pp2, new_pp1, new_pp2; ZnPoint perp, c, shift1, shift2; ZnPoint bevel_points[4]; XPoint bevel_xpoints[5]; ZnBool folded, closed, colinear; WidgetInfo *wi = NULL; ReliefStyle relief = 0; ZnColorGradient gradient = NULL; ZnPoint *pp = NULL; double *dist = NULL; ZnBBox *bbox = NULL; va_list var; #if 0 ZnBool toggle=True; #endif ZnReal dx, dy; va_start(var, what_to_do); if (what_to_do == POLYGON_RELIEF_DIST) { pp = va_arg(var, ZnPoint *); dist = va_arg(var, double *); *dist = 1.0e40; } if (what_to_do == POLYGON_RELIEF_IN_BBOX) { bbox = va_arg(var, ZnBBox *); result = va_arg(var, int *); } else if (what_to_do == POLYGON_RELIEF_BBOX) { bbox = va_arg(var, ZnBBox *); ResetBBox(bbox); } else if (what_to_do == POLYGON_RELIEF_DRAW) { wi = va_arg(var, WidgetInfo *); relief = va_arg(var, int); gradient = va_arg(var, ZnColorGradient); } va_end(var); /* * 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]); 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 (what_to_do == POLYGON_RELIEF_DIST) { double new_dist; new_dist = PolygonToPointDist(bevel_points, 4, pp); if (new_dist < 0) { new_dist = 0; } *dist = MIN(*dist, new_dist); } else if (what_to_do == POLYGON_RELIEF_IN_BBOX) { if (processed_points <= 2) { *result = PolygonInBBox(bevel_points, 4, bbox, NULL); if (*result == 0) { return; } } else { if (PolygonInBBox(bevel_points, 4, bbox, NULL) != *result) { *result = 0; return; } } } else if (what_to_do == POLYGON_RELIEF_BBOX) { int i; for (i = 0; i < 4; i++) { AddPointToBBox(bbox, bevel_points[i].x, bevel_points[i].y); } } else if (what_to_do == POLYGON_RELIEF_DRAW) { XGCValues gc_values; #if 1 gc_values.foreground = ReliefColorOfSegment(bevel_points[0].x, bevel_points[0].y, bevel_points[3].x, bevel_points[3].y, relief, gradient, wi); #endif #if 0 gc_values.foreground = toggle ? WhitePixelOfScreen(wi->screen):BlackPixelOfScreen(wi->screen); #endif #if 0 gc_values.foreground = colinear ? WhitePixelOfScreen(wi->screen):BlackPixelOfScreen(wi->screen); #endif #if 0 toggle = !toggle; #endif gc_values.fill_style = FillSolid; XChangeGC(wi->dpy, wi->gc, GCFillStyle|GCForeground, &gc_values); for (j = 0; j < 4; j++) { bevel_xpoints[j].x = REAL_TO_INT(bevel_points[j].x); bevel_xpoints[j].y = REAL_TO_INT(bevel_points[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]; gc_values.line_width = 0; XChangeGC(wi->dpy, wi->gc, GCLineWidth, &gc_values); XDrawLines(wi->dpy, wi->draw_buffer, wi->gc, bevel_xpoints, 5, CoordModeOrigin); #endif } } 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++; } } /* ********************************************************************************** * * GetPolygonReliefBBox -- * Returns the bevelled polygon bounding box. * ********************************************************************************** */ void GetPolygonReliefBBox(ZnPoint *points, int num_points, int line_width, ZnBBox *bbox) { DoPolygonRelief(points, num_points, line_width, POLYGON_RELIEF_BBOX, bbox); } /* ********************************************************************************** * * PolygonReliefInBBox -- * Returns (-1) if the relief is entirely outside the bbox, (1) if it is * entirely inside or (0) if in between * ********************************************************************************** */ int PolygonReliefInBBox(ZnPoint *points, int num_points, int line_width, ZnBBox *area) { int result; DoPolygonRelief(points, num_points, line_width, POLYGON_RELIEF_IN_BBOX, area, &result); return result; } /* ********************************************************************************** * * PolygonReliefToPointDist -- * Returns the distance between the given point and * the bevelled polygon. * ********************************************************************************** */ double PolygonReliefToPointDist(ZnPoint *points, int num_points, int line_width, ZnPoint *pp) { double dist; DoPolygonRelief(points, num_points, line_width, POLYGON_RELIEF_DIST, pp, &dist); return dist; } /* ********************************************************************************** * * DrawPolygonRelief -- * Draw the bevels around path. * ********************************************************************************** */ void DrawPolygonRelief(WidgetInfo *wi, ReliefStyle relief, ZnColorGradient gradient, ZnPoint *points, int num_points, int line_width) { /* * 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)) { DoPolygonRelief(points, num_points, line_width, POLYGON_RELIEF_DRAW, wi, (int) (relief==RELIEF_GROOVE)?RELIEF_BEVEL_OUT:RELIEF_BEVEL_IN, gradient); DoPolygonRelief(points, num_points, line_width/2, POLYGON_RELIEF_DRAW, wi, (int) (relief==RELIEF_GROOVE)?RELIEF_BEVEL_IN:RELIEF_BEVEL_OUT, gradient); } else { DoPolygonRelief(points, num_points, line_width, POLYGON_RELIEF_DRAW, wi, (int) relief, gradient); } } void DrawPolygonGradient(struct _WidgetInfo *wi, ZnGradientGeom grad_geom, ZnColorGradient grad_color, ZnPoly *poly, ZnBBox *bbox) { ZnBBox lbbox; XRectangle r; int i; /* * The polygon has to be reduced for this to give meaningful * results. */ if (!bbox) { ResetBBox(&lbbox); for (i = 0; i < poly->num_contours; i++) { if (!poly->holes[i]) { AddPointsToBBox(&lbbox, poly->contours[i].points, poly->contours[i].num_points); } } bbox = &lbbox; } BBox2XRect(bbox, &r); ITEM_P.PushClip(wi, poly, False, True); DrawRectangleGradient(wi, grad_geom, grad_color, &r); ITEM_P.PopClip(wi, True); } void DrawRectangleGradient(struct _WidgetInfo *wi, ZnGradientGeom grad_geom, ZnColorGradient grad_color, XRectangle *bbox) { int steps = ZnColorGradientSpan(grad_color); ZnReal d1, d2; int angle = grad_geom->angle; int x, y, c, y_d2, w, h, i, j; int *optr, *hptr; short yorig, ycorner; ZnBool dir; /* * We can only hangle 0, 90, 180, 270. */ angle = (angle / 90) * 90; x = bbox->x; y = bbox->y; if ((angle == 90) || (angle == 270)) { optr = &x; yorig = bbox->x; ycorner = bbox->x+bbox->width; h = bbox->height; hptr = &w; d1 = grad_geom->d1*bbox->width/100; d2 = grad_geom->d2*bbox->width/100; } else { optr = &y; yorig = bbox->y; ycorner = bbox->y+bbox->height; w = bbox->width; hptr = &h; d1 = grad_geom->d1*bbox->height/100.0; d2 = bbox->height*(1.0-grad_geom->d2/100.0); } dir = True; if ((angle == 180) || (angle == 270)) { dir = False; } #if 1 XSetFillStyle(wi->dpy, wi->gc, FillSolid); c = ycorner; for (i = 2, j = steps-1; i < steps; i += 2, j--) { *optr = REAL_TO_INT(ycorner - (i * d2 / steps)); *hptr = c - *optr; XSetForeground(wi->dpy, wi->gc, ZnPixel(ZnColorGradientColor(wi->win, grad_color, dir?j:steps-1-j))); XFillRectangle(wi->dpy, wi->draw_buffer, wi->gc, x, y, w, h); c = *optr; } y_d2 = *optr; *optr = yorig; for (i = 2, j = 0; i < steps; i += 2, j++) { c = REAL_TO_INT(yorig + (i * d1 / steps)); *hptr = c - *optr; XSetForeground(wi->dpy, wi->gc, ZnPixel(ZnColorGradientColor(wi->win, grad_color, dir?j:steps-1-j))); XFillRectangle(wi->dpy, wi->draw_buffer, wi->gc, x, y, w, h); *optr = c; } XSetForeground(wi->dpy, wi->gc, ZnPixel(ZnColorGradientMidColor(wi->win, grad_color))); *hptr = y_d2 - *optr; XFillRectangle(wi->dpy, wi->draw_buffer, wi->gc, x, y, w, h); #endif /* * Draw over with a 50% gray pattern to smooth the color * waves. */ XSetFillStyle(wi->dpy, wi->gc, FillStippled); XSetStipple(wi->dpy, wi->gc, wi->alpha_stipples[7]); c = REAL_TO_INT(ycorner - d2 / steps); for (i = 3, j = steps-2; i <= steps; i += 2, j--) { *optr = REAL_TO_INT(ycorner - (i * d2 / steps)); *hptr = c - *optr; XSetForeground(wi->dpy, wi->gc, ZnPixel(ZnColorGradientColor(wi->win, grad_color, dir?j:steps-1-j))); XFillRectangle(wi->dpy, wi->draw_buffer, wi->gc, x, y, w, h); c = *optr; } *optr = REAL_TO_INT(yorig + d1 / steps); for (i = 3, j = 1; i <= steps; i += 2, j++) { c = REAL_TO_INT(yorig + (i * d1 / steps)); *hptr = c - *optr; XSetForeground(wi->dpy, wi->gc, ZnPixel(ZnColorGradientColor(wi->win, grad_color, dir?j:steps-1-j))); XFillRectangle(wi->dpy, wi->draw_buffer, wi->gc, x, y, w, h); *optr = c; } }