aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--generic/Draw.c732
1 files changed, 732 insertions, 0 deletions
diff --git a/generic/Draw.c b/generic/Draw.c
new file mode 100644
index 0000000..f0ace86
--- /dev/null
+++ b/generic/Draw.c
@@ -0,0 +1,732 @@
+/*
+ * 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 <math.h>
+#include <stdarg.h>
+#include <alloca.h>
+
+
+#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(RadarPoint *p1,
+ RadarPoint *p2,
+ unsigned int line_width,
+ LineShape shape,
+ RadarBBox *bbox,
+ RadarList to_points)
+{
+ RadarPoint *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;
+ RadarListAssertSize(to_points, num_points);
+ points = (RadarPoint *) RadarListArray(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;
+ RadarListAssertSize(to_points, num_points);
+ points = (RadarPoint *) RadarListArray(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;
+ RadarListAssertSize(to_points, num_points);
+ points = (RadarPoint *) RadarListArray(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;
+ RadarListAssertSize(to_points, num_points);
+ points = (RadarPoint *) RadarListArray(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(Display *display,
+ Drawable draw_buffer,
+ GC gc,
+ RadarPoint *p,
+ int num_p,
+ LineStyle line_style,
+ RadarColor foreground,
+ unsigned int line_width,
+ LineShape shape)
+{
+ XPoint *xpoints;
+ int i;
+ XGCValues values;
+
+ /*
+ * Setup GC.
+ */
+ SetLineStyle(display, gc, line_style);
+ values.foreground = RadarPixel(foreground);
+ values.line_width = (line_width == 1) ? 0 : line_width;
+ values.fill_style = FillSolid;
+ values.join_style = JoinRound;
+ values.cap_style = CapRound;
+ XChangeGC(display, gc,
+ GCFillStyle | GCLineWidth | GCJoinStyle | GCCapStyle | GCForeground, &values);
+ xpoints = (XPoint *) alloca(num_p * sizeof(XPoint));
+ for (i = 0; i < num_p; i++) {
+ xpoints[i].x = p[i].x;
+ xpoints[i].y = p[i].y;
+ }
+ XDrawLines(display, draw_buffer, gc, xpoints, num_p, CoordModeOrigin);
+}
+
+
+/*
+ **********************************************************************************
+ *
+ * DrawRectangleRelief --
+ * Draw the bevels inside bbox.
+ *
+ **********************************************************************************
+ */
+void
+DrawRectangleRelief(WidgetInfo *wi,
+ ReliefStyle relief,
+ RadarColorGradient gradient,
+ XRectangle *bbox,
+ unsigned int line_width)
+{
+ XGCValues gc_values;
+ XPoint top_points[7];
+
+ /*
+ * 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;
+ }
+
+ gc_values.fill_style = FillSolid;
+ if (relief == RELIEF_BEVEL_IN) {
+ gc_values.foreground = RadarColorGradientPixel(gradient, wi->win, -1);
+ }
+ else {
+ gc_values.foreground = RadarColorGradientPixel(gradient, wi->win, 0);
+ }
+ XChangeGC(wi->dpy, wi->gc, GCFillStyle|GCForeground, &gc_values);
+ XFillRectangle(wi->dpy, wi->draw_buffer, wi->gc,
+ bbox->x, bbox->y + bbox->height - line_width,
+ bbox->width, line_width);
+ XFillRectangle(wi->dpy, wi->draw_buffer, wi->gc,
+ bbox->x + bbox->width - line_width, bbox->y,
+ line_width, bbox->height);
+
+ if (relief == RELIEF_BEVEL_IN) {
+ gc_values.foreground = RadarColorGradientPixel(gradient, wi->win, 0);
+ }
+ else {
+ gc_values.foreground = RadarColorGradientPixel(gradient, wi->win, -1);
+ }
+ XChangeGC(wi->dpy, wi->gc, GCFillStyle|GCForeground, &gc_values);
+ top_points[0].x = top_points[1].x = top_points[6].x = bbox->x;
+ top_points[0].y = top_points[6].y = bbox->y + bbox->height;
+ top_points[1].y = top_points[2].y = bbox->y;
+ top_points[2].x = bbox->x + bbox->width;
+ top_points[3].x = bbox->x + bbox->width - line_width;
+ top_points[3].y = top_points[4].y = bbox->y + line_width;
+ top_points[4].x = top_points[5].x = bbox->x + line_width;
+ top_points[5].y = bbox->y + bbox->height - line_width;
+ XFillPolygon(wi->dpy, wi->draw_buffer, wi->gc, top_points, 7,
+ Nonconvex, CoordModeOrigin);
+}
+
+
+static void
+DoPolygonRelief(RadarPoint *p,
+ int num_points,
+ unsigned int line_width,
+ int what_to_do,
+ ...)
+{
+ int i, j, processed_points, *result;
+ RadarPoint *p1, *p2;
+ RadarPoint pp1, pp2, new_pp1, new_pp2;
+ RadarPoint perp, c, shift1, shift2;
+ RadarPoint bevel_points[4];
+ XPoint bevel_xpoints[4];
+ RadarBool parallel, closed;
+ WidgetInfo *wi = NULL;
+ ReliefStyle relief = 0;
+ RadarColorGradient gradient = NULL;
+ RadarPoint *pp = NULL;
+ double *dist = NULL;
+ RadarBBox *bbox = NULL;
+ va_list var;
+
+ va_start(var, what_to_do);
+ if (what_to_do == POLYGON_RELIEF_DIST) {
+ pp = va_arg(var, RadarPoint *);
+ dist = va_arg(var, double *);
+ *dist = 1.0e40;
+ }
+ if (what_to_do == POLYGON_RELIEF_IN_BBOX) {
+ bbox = va_arg(var, RadarBBox *);
+ result = va_arg(var, int *);
+ }
+ else if (what_to_do == POLYGON_RELIEF_BBOX) {
+ bbox = va_arg(var, RadarBBox *);
+ ResetBBox(bbox);
+ }
+ else if (what_to_do == POLYGON_RELIEF_DRAW) {
+ wi = va_arg(var, WidgetInfo *);
+ relief = va_arg(var, int);
+ gradient = va_arg(var, RadarColorGradient);
+ }
+ 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--;
+ }
+
+ /*
+ * 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 parallel:
+ *
+ * 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, b, corner from pp1, pp2, new_pp1
+ * and new_pp2. We compute the perpendicular to p&,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++, p1 = p2, p2++) {
+ /*
+ * When it is time to wrap, do it
+ */
+ if ((i == -1) || (i == num_points-1)) {
+ p2 = p;
+ }
+
+ /*
+ * Skip over duplicate vertices.
+ */
+ if ((p2->x == p1->x) && (p2->y == p1->y)) {
+ continue;
+ }
+
+ ShiftLine(p1, p2, line_width, &new_pp1, &new_pp2);
+ bevel_points[3] = *p1;
+ parallel = 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 + ((p1-1)->y - p1->y);
+ perp.y = p1->y - ((p1-1)->x - p1->x);
+ IntersectLines(p1, &perp, &pp1, &pp2, &bevel_points[2]);
+ }
+ else if (processed_points >= 1) {
+ parallel = !IntersectLines(&new_pp1, &new_pp2, &pp1, &pp2, &bevel_points[2]);
+ if (parallel) {
+ 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);
+ if (*result == 0) {
+ return;
+ }
+ }
+ else {
+ if (PolygonInBBox(bevel_points, 4, bbox) != *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) {
+ RadarReal dx, dy;
+ RadarBool light_on_left;
+ XGCValues gc_values;
+
+ dx = bevel_points[3].x - bevel_points[0].x;
+ dy = bevel_points[3].y - bevel_points[0].y;
+ if (dx > 0.0) {
+ light_on_left = (dy <= dx);
+ }
+ else {
+ light_on_left = (dy < dx);
+ }
+
+ gc_values.fill_style = FillSolid;
+ if (light_on_left ^ (relief == RELIEF_BEVEL_OUT)) {
+ gc_values.foreground = RadarColorGradientPixel(gradient, wi->win, -1);
+ }
+ else {
+ gc_values.foreground = RadarColorGradientPixel(gradient, wi->win, 0);
+ }
+ 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);
+ }
+ XFillPolygon(wi->dpy, wi->draw_buffer, wi->gc, bevel_xpoints, 4,
+ Convex, CoordModeOrigin);
+ }
+ }
+
+ pp1 = new_pp1;
+ pp2 = new_pp2;
+ bevel_points[0] = bevel_points[3];
+ if (parallel) {
+ 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(RadarList points,
+ unsigned int line_width,
+ RadarBBox *bbox)
+{
+ DoPolygonRelief(RadarListArray(points), RadarListSize(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(RadarList points,
+ unsigned int line_width,
+ RadarBBox *area)
+{
+ int result;
+
+ DoPolygonRelief(RadarListArray(points), RadarListSize(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(RadarList points,
+ unsigned int line_width,
+ RadarPoint *pp)
+{
+ double dist;
+
+ DoPolygonRelief(RadarListArray(points), RadarListSize(points),
+ line_width, POLYGON_RELIEF_DIST, pp, &dist);
+
+ return dist;
+}
+
+
+/*
+ **********************************************************************************
+ *
+ * DrawPolygonRelief --
+ * Draw the bevels around path.
+ *
+ **********************************************************************************
+ */
+void
+DrawPolygonRelief(WidgetInfo *wi,
+ ReliefStyle relief,
+ RadarColorGradient gradient,
+ RadarPoint *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);
+ }
+}
+