From a4b439eb69710a3c2053e68f7716bbf76b5ceb7f Mon Sep 17 00:00:00 2001 From: Carl Worth Date: Tue, 28 Jan 2003 13:49:57 +0000 Subject: [PATCH] Added round caps/joins. Fixed initial cap. Fixed disabling of dashing. --- ChangeLog | 22 ++++++ src/xrgstate.c | 46 ++++++----- src/xrint.h | 33 ++++---- src/xrpen.c | 96 ++++++++++++++++++----- src/xrstroker.c | 203 ++++++++++++++++++++++++++++++++---------------- src/xrtraps.c | 62 +++++++++++---- xrgstate.c | 46 ++++++----- xrint.h | 33 ++++---- xrpen.c | 96 ++++++++++++++++++----- xrstroker.c | 203 ++++++++++++++++++++++++++++++++---------------- xrtraps.c | 62 +++++++++++---- 11 files changed, 626 insertions(+), 276 deletions(-) diff --git a/ChangeLog b/ChangeLog index f18100809..2e5263739 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,25 @@ +2003-01-28 Carl Worth + + * xrtraps.c (_XrTrapsTessellateTriangle): Fixed to not re-order + the array provided as an argument. (Note: the rectangle and + polyghon tessellators still perform reordering). + + * xrstroker.c (_XrStrokerJoin): + (_XrStrokerCap): Implemented round caps and joins. + (_XrStrokerDoneSubPath): Fixed initial cap, (was always being + drawn on the inside previously). + + * xrpen.c (_XrPenInit): Fixed to keep pen vertex theta values all + in device space. + (_XrPenAddPoints): Removed the silly flags from the pen vertices, + (in favor of just using the FindActiveVertex functions) + (_XrPenFindActiveCWVertexIndex): + (_XrPenFindActiveCCWVertexIndex): Added functions needed for round + caps/joins. + + * xrgstate.c (_XrGStateSetDash): Fixed to accept a NULL dash array + to revert to no dashing. + 2003-01-28 Carl Worth * xrtraps.c (_XrTrapsTessellateTriangle): Restored triangle diff --git a/src/xrgstate.c b/src/xrgstate.c index e8dab515a..2bfb3de7f 100644 --- a/src/xrgstate.c +++ b/src/xrgstate.c @@ -63,8 +63,8 @@ _XrGStateInit(XrGState *gstate, Display *dpy) gstate->fill_rule = XR_GSTATE_FILL_RULE_DEFAULT; - gstate->dashes = 0; - gstate->ndashes = 0; + gstate->dash = NULL; + gstate->num_dashes = 0; gstate->dash_offset = 0.0; gstate->alphaFormat = XcFindStandardFormat(dpy, PictStandardA8); @@ -98,11 +98,11 @@ _XrGStateInitCopy(XrGState *gstate, XrGState *other) XrStatus status; *gstate = *other; - if (other->dashes) { - gstate->dashes = malloc (other->ndashes * sizeof (double)); - if (gstate->dashes == NULL) + if (other->dash) { + gstate->dash = malloc (other->num_dashes * sizeof (double)); + if (gstate->dash == NULL) return XrStatusNoMemory; - memcpy(gstate->dashes, other->dashes, other->ndashes * sizeof (double)); + memcpy(gstate->dash, other->dash, other->num_dashes * sizeof (double)); } status = _XrFontInitCopy(&gstate->font, &other->font); @@ -130,8 +130,8 @@ _XrGStateInitCopy(XrGState *gstate, XrGState *other) CLEANUP_FONT: _XrFontDeinit(&gstate->font); CLEANUP_DASHES: - free (gstate->dashes); - gstate->dashes = NULL; + free (gstate->dash); + gstate->dash = NULL; return status; } @@ -158,9 +158,9 @@ _XrGStateDeinit(XrGState *gstate) _XrPenDeinit(&gstate->pen_regular); - if (gstate->dashes) { - free (gstate->dashes); - gstate->dashes = NULL; + if (gstate->dash) { + free (gstate->dash); + gstate->dash = NULL; } } @@ -363,19 +363,23 @@ _XrGStateSetLineJoin(XrGState *gstate, XrLineJoin line_join) } XrStatus -_XrGStateSetDash(XrGState *gstate, double *dashes, int ndash, double offset) +_XrGStateSetDash(XrGState *gstate, double *dash, int num_dashes, double offset) { - if (gstate->dashes) { - free (gstate->dashes); + if (gstate->dash) { + free (gstate->dash); + gstate->dash = NULL; } - gstate->dashes = malloc (ndash * sizeof (double)); - if (!gstate->dashes) { - gstate->ndashes = 0; - return XrStatusNoMemory; + gstate->num_dashes = num_dashes; + if (gstate->num_dashes) { + gstate->dash = malloc (gstate->num_dashes * sizeof (double)); + if (gstate->dash == NULL) { + gstate->num_dashes = 0; + return XrStatusNoMemory; + } } - gstate->ndashes = ndash; - memcpy (gstate->dashes, dashes, ndash * sizeof (double)); + + memcpy (gstate->dash, dash, gstate->num_dashes * sizeof (double)); gstate->dash_offset = offset; return XrStatusSuccess; @@ -639,7 +643,7 @@ _XrGStateStroke(XrGState *gstate) _XrStrokerDoneSubPath, _XrStrokerDonePath }; - XrPathCallbacks *cbs = gstate->dashes ? &cb_dash : &cb; + XrPathCallbacks *cbs = gstate->dash ? &cb_dash : &cb; XrStroker stroker; XrTraps traps; diff --git a/src/xrint.h b/src/xrint.h index 4e63d5ae6..00911f3e1 100644 --- a/src/xrint.h +++ b/src/xrint.h @@ -137,20 +137,14 @@ typedef struct _XrSpline { XPointFixed *pts; } XrSpline; -typedef enum _XrPenVertexFlag { - XrPenVertexFlagNone = 0, - XrPenVertexFlagForward = 1, - XrPenVertexFlagReverse = 2 -} XrPenVertexFlag; - -typedef struct _XrPenFlaggedPoint { - XPointFixed pt; - XrPenVertexFlag flag; -} XrPenFlaggedPoint; +/* XXX: This can go away once incremental spline tessellation is working */ +typedef enum _XrPenStrokeDirection { + XrPenStrokeDirectionForward, + XrPenStrokeDirectionReverse +} XrPenStrokeDirection; typedef struct _XrPenVertex { XPointFixed pt; - XrPenVertexFlag flag; double theta; XrSlopeFixed slope_ccw; @@ -244,8 +238,8 @@ typedef struct _XrGState { XrFillRule fill_rule; - double *dashes; - int ndashes; + double *dash; + int num_dashes; double dash_offset; XcFormat *alphaFormat; @@ -284,7 +278,8 @@ typedef struct _XrStrokeFace { XPointFixed ccw; XPointFixed pt; XPointFixed cw; - XPointDouble vector; + XrSlopeFixed dev_vector; + XPointDouble usr_vector; } XrStrokeFace; typedef struct _XrStroker { @@ -387,7 +382,7 @@ XrStatus _XrGStateSetLineJoin(XrGState *gstate, XrLineJoin line_join); XrStatus -_XrGStateSetDash(XrGState *gstate, double *dashes, int ndash, double offset); +_XrGStateSetDash(XrGState *gstate, double *dash, int num_dashes, double offset); XrStatus _XrGStateSetMiterLimit(XrGState *gstate, double limit); @@ -640,11 +635,17 @@ void _XrPenDeinit(XrPen *pen); XrStatus -_XrPenAddPoints(XrPen *pen, XrPenFlaggedPoint *pt, int num_pts); +_XrPenAddPoints(XrPen *pen, XPointFixed *pt, int num_pts); XrStatus _XrPenAddPointsForSlopes(XrPen *pen, XPointFixed *a, XPointFixed *b, XPointFixed *c, XPointFixed *d); +XrStatus +_XrPenFindActiveCWVertexIndex(XrPen *pen, XrSlopeFixed *slope, int *active); + +XrStatus +_XrPenFindActiveCCWVertexIndex(XrPen *pen, XrSlopeFixed *slope, int *active); + XrStatus _XrPenStrokeSpline(XrPen *pen, XrSpline *spline, double tolerance, XrTraps *traps); diff --git a/src/xrpen.c b/src/xrpen.c index 3c434993b..18c6310f0 100644 --- a/src/xrpen.c +++ b/src/xrpen.c @@ -41,8 +41,7 @@ static int _XrPenVertexCompareByTheta(const void *a, const void *b); static XrStatus -_XrPenStrokeSplineHalf(XrPen *pen, XrSpline *spline, - XrPenVertexFlag dir, XrPolygon *polygon); +_XrPenStrokeSplineHalf(XrPen *pen, XrSpline *spline, XrPenStrokeDirection dir, XrPolygon *polygon); XrStatus _XrPenInitEmpty(XrPen *pen) @@ -93,7 +92,10 @@ _XrPenInit(XrPen *pen, double radius, XrGState *gstate) _XrTransformDistance(&gstate->ctm, &dx, &dy); v->pt.x = XDoubleToFixed(dx); v->pt.y = XDoubleToFixed(dy); - v->flag = XrPenVertexFlagNone; + /* Recompute theta in device space */ + v->theta = atan2(v->pt.y, v->pt.x); + if (v->theta < 0) + v->theta += 2 * M_PI; } _XrPenComputeSlopes(pen); @@ -141,7 +143,7 @@ _XrPenVertexCompareByTheta(const void *a, const void *b) } XrStatus -_XrPenAddPoints(XrPen *pen, XrPenFlaggedPoint *pt, int num_pts) +_XrPenAddPoints(XrPen *pen, XPointFixed *pt, int num_pts) { int i, j; XrPenVertex *v, *v_next, *new_vertex; @@ -157,8 +159,7 @@ _XrPenAddPoints(XrPen *pen, XrPenFlaggedPoint *pt, int num_pts) /* initialize new vertices */ for (i=0; i < num_pts; i++) { v = &pen->vertex[pen->num_vertices-(i+1)]; - v->pt = pt[i].pt; - v->flag = pt[i].flag; + v->pt = pt[i]; v->theta = atan2(v->pt.y, v->pt.x); if (v->theta < 0) v->theta += 2 * M_PI; @@ -171,7 +172,6 @@ _XrPenAddPoints(XrPen *pen, XrPenFlaggedPoint *pt, int num_pts) v = &pen->vertex[i]; v_next = &pen->vertex[i+1]; if (v->pt.x == v_next->pt.x && v->pt.y == v_next->pt.y) { - v->flag |= v_next->flag; for (j=i+1; j < pen->num_vertices - 1; j++) pen->vertex[j] = pen->vertex[j+1]; pen->num_vertices--; @@ -223,6 +223,12 @@ _XrPenComputeSlopes(XrPen *pen) } } +/* Is a clockwise of b? + * + * NOTE: The strict equality here is not significant in and of itself, + * but there are functions up above that are sensitive to it, + * (cf. _XrPenFindActiveCWVertexIndex). + */ static int _SlopeClockwise(XrSlopeFixed *a, XrSlopeFixed *b) { @@ -240,40 +246,90 @@ _SlopeCounterClockwise(XrSlopeFixed *a, XrSlopeFixed *b) return ! _SlopeClockwise(a, b); } +/* Find active pen vertex for clockwise edge of stroke at the given slope. + * + * NOTE: The behavior of this function is sensitive to the sense of + * the inequality within _SlopeClockwise/_SlopeCounterClockwise. + * + * The issue is that the slope_ccw member of one pen vertex will be + * equivalent to the slope_cw member of the next pen vertex in a + * counterclockwise order. However, for this function, we care + * strongly about which vertex is returned. + */ +XrStatus +_XrPenFindActiveCWVertexIndex(XrPen *pen, XrSlopeFixed *slope, int *active) +{ + int i; + + for (i=0; i < pen->num_vertices; i++) { + if (_SlopeClockwise(slope, &pen->vertex[i].slope_ccw) + && _SlopeCounterClockwise(slope, &pen->vertex[i].slope_cw)) + break; + } + + *active = i; + + return XrStatusSuccess; +} + +/* Find active pen vertex for counterclockwise edge of stroke at the given slope. + * + * NOTE: The behavior of this function is sensitive to the sense of + * the inequality within _SlopeClockwise/_SlopeCounterClockwise. + */ +XrStatus +_XrPenFindActiveCCWVertexIndex(XrPen *pen, XrSlopeFixed *slope, int *active) +{ + int i; + XrSlopeFixed slope_reverse; + + slope_reverse = *slope; + slope_reverse.dx = -slope_reverse.dx; + slope_reverse.dy = -slope_reverse.dy; + + for (i=pen->num_vertices-1; i >= 0; i--) { + if (_SlopeCounterClockwise(&pen->vertex[i].slope_ccw, &slope_reverse) + && _SlopeClockwise(&pen->vertex[i].slope_cw, &slope_reverse)) + break; + } + + *active = i; + + return XrStatusSuccess; +} + static XrStatus -_XrPenStrokeSplineHalf(XrPen *pen, XrSpline *spline, - XrPenVertexFlag dir, XrPolygon *polygon) +_XrPenStrokeSplineHalf(XrPen *pen, XrSpline *spline, XrPenStrokeDirection dir, XrPolygon *polygon) { int i; XrStatus status; int start, stop, step; int active = 0; XPointFixed hull_pt; - XrSlopeFixed slope, final_slope; + XrSlopeFixed slope, initial_slope, final_slope; XPointFixed *pt = spline->pts; int num_pts = spline->num_pts; - for (i=0; i < pen->num_vertices; i++) { - if (pen->vertex[i].flag & dir) { - active = i; - break; - } - } - - if (dir == XrPenVertexFlagForward) { + if (dir == XrPenStrokeDirectionForward) { start = 0; stop = num_pts; step = 1; + initial_slope = spline->initial_slope; final_slope = spline->final_slope; } else { start = num_pts - 1; stop = -1; step = -1; + initial_slope = spline->final_slope; + initial_slope.dx = -initial_slope.dx; + initial_slope.dy = -initial_slope.dy; final_slope = spline->initial_slope; final_slope.dx = -final_slope.dx; final_slope.dy = -final_slope.dy; } + _XrPenFindActiveCWVertexIndex(pen, &initial_slope, &active); + i = start; while (i != stop) { hull_pt.x = pt[i].x + pen->vertex[active].pt.x; @@ -318,11 +374,11 @@ _XrPenStrokeSpline(XrPen *pen, if (status) return status; - status = _XrPenStrokeSplineHalf(pen, spline, XrPenVertexFlagForward, &polygon); + status = _XrPenStrokeSplineHalf(pen, spline, XrPenStrokeDirectionForward, &polygon); if (status) return status; - status = _XrPenStrokeSplineHalf(pen, spline, XrPenVertexFlagReverse, &polygon); + status = _XrPenStrokeSplineHalf(pen, spline, XrPenStrokeDirectionReverse, &polygon); if (status) return status; diff --git a/src/xrstroker.c b/src/xrstroker.c index 5a47df749..07592dea9 100644 --- a/src/xrstroker.c +++ b/src/xrstroker.c @@ -44,15 +44,15 @@ _XrStrokerStartDash (XrStroker *stroker) int i = 0; offset = gstate->dash_offset; - while (offset >= gstate->dashes[i]) { - offset -= gstate->dashes[i]; + while (offset >= gstate->dash[i]) { + offset -= gstate->dash[i]; on = 1-on; - if (++i == gstate->ndashes) + if (++i == gstate->num_dashes) i = 0; } stroker->dash_index = i; stroker->dash_on = on; - stroker->dash_remain = gstate->dashes[i] - offset; + stroker->dash_remain = gstate->dash[i] - offset; } static void @@ -62,10 +62,10 @@ _XrStrokerStepDash (XrStroker *stroker, double step) stroker->dash_remain -= step; if (stroker->dash_remain <= 0) { stroker->dash_index++; - if (stroker->dash_index == gstate->ndashes) + if (stroker->dash_index == gstate->num_dashes) stroker->dash_index = 0; stroker->dash_on = 1-stroker->dash_on; - stroker->dash_remain = gstate->dashes[stroker->dash_index]; + stroker->dash_remain = gstate->dash[stroker->dash_index]; } } @@ -77,7 +77,7 @@ _XrStrokerInit(XrStroker *stroker, XrGState *gstate, XrTraps *traps) stroker->have_prev = 0; stroker->have_first = 0; stroker->is_first = 1; - if (gstate->dashes) + if (gstate->dash) _XrStrokerStartDash (stroker); } @@ -112,12 +112,9 @@ _XrStrokerJoin(XrStroker *stroker, XrStrokeFace *in, XrStrokeFace *out) { XrStatus status; XrGState *gstate = stroker->gstate; - int clockwise = _XrStrokerFaceClockwise (in, out); + int clockwise = _XrStrokerFaceClockwise (out, in); XPointFixed *inpt, *outpt; - /* XXX: There might be a more natural place to check for the - degenerate join later in the code, (such as right before - dividing by zero) */ if (in->cw.x == out->cw.x && in->cw.y == out->cw.y && in->ccw.x == out->ccw.x @@ -126,20 +123,57 @@ _XrStrokerJoin(XrStroker *stroker, XrStrokeFace *in, XrStrokeFace *out) } if (clockwise) { - inpt = &in->cw; - outpt = &out->cw; - } else { inpt = &in->ccw; outpt = &out->ccw; + } else { + inpt = &in->cw; + outpt = &out->cw; } switch (gstate->line_join) { - case XrLineJoinRound: - status = XrStatusSuccess; - break; - case XrLineJoinMiter: { + case XrLineJoinRound: { + int i; + int start, step, stop; + XPointFixed tri[3], initial, final; + XrPen *pen = &gstate->pen_regular; + + tri[0] = in->pt; + if (clockwise) { + initial = in->ccw; + _XrPenFindActiveCCWVertexIndex(pen, &in->dev_vector, &start); + step = -1; + _XrPenFindActiveCCWVertexIndex(pen, &out->dev_vector, &stop); + final = out->ccw; + } else { + initial = in->cw; + _XrPenFindActiveCWVertexIndex(pen, &in->dev_vector, &start); + step = +1; + _XrPenFindActiveCWVertexIndex(pen, &out->dev_vector, &stop); + final = out->cw; + } + + i = start; + tri[1] = initial; + while (i != stop) { + tri[2] = in->pt; + _TranslatePoint(&tri[2], &pen->vertex[i].pt); + _XrTrapsTessellateTriangle(stroker->traps, tri); + tri[1] = tri[2]; + i += step; + if (i < 0) + i = pen->num_vertices - 1; + if (i >= pen->num_vertices) + i = 0; + } + + tri[2] = final; + + return _XrTrapsTessellateTriangle(stroker->traps, tri); + } + case XrLineJoinMiter: + default: { XrPolygon polygon; - XDouble c = (-in->vector.x * out->vector.x)+(-in->vector.y * out->vector.y); + XDouble c = (-in->usr_vector.x * out->usr_vector.x)+(-in->usr_vector.y * out->usr_vector.y); XDouble ml = gstate->miter_limit; _XrPolygonInit (&polygon); @@ -152,14 +186,14 @@ _XrStrokerJoin(XrStroker *stroker, XrStrokeFace *in, XrStrokeFace *out) x1 = XFixedToDouble(inpt->x); y1 = XFixedToDouble(inpt->y); - dx1 = in->vector.x; - dy1 = in->vector.y; + dx1 = in->usr_vector.x; + dy1 = in->usr_vector.y; _XrTransformDistance(&gstate->ctm, &dx1, &dy1); x2 = XFixedToDouble(outpt->x); y2 = XFixedToDouble(outpt->y); - dx2 = out->vector.x; - dy2 = out->vector.y; + dx2 = out->usr_vector.x; + dy2 = out->usr_vector.y; _XrTransformDistance(&gstate->ctm, &dx2, &dy2); my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) / @@ -179,7 +213,8 @@ _XrStrokerJoin(XrStroker *stroker, XrStrokeFace *in, XrStrokeFace *out) &polygon, XrFillRuleWinding); _XrPolygonDeinit (&polygon); - break; + + return status; } /* fall through ... */ } @@ -188,12 +223,10 @@ _XrStrokerJoin(XrStroker *stroker, XrStrokeFace *in, XrStrokeFace *out) tri[0] = in->pt; tri[1] = *inpt; tri[2] = *outpt; - status = _XrTrapsTessellateTriangle (stroker->traps, tri); - break; - } - } - return status; + return _XrTrapsTessellateTriangle (stroker->traps, tri); + } + } } static XrStatus @@ -201,45 +234,70 @@ _XrStrokerCap(XrStroker *stroker, XrStrokeFace *f) { XrStatus status; XrGState *gstate = stroker->gstate; - XrPolygon polygon; if (gstate->line_cap == XrLineCapButt) return XrStatusSuccess; - _XrPolygonInit (&polygon); switch (gstate->line_cap) { - case XrLineCapRound: - break; + case XrLineCapRound: { + int i; + int start, stop; + XrSlopeFixed slope; + XPointFixed tri[3]; + XrPen *pen = &gstate->pen_regular; + + slope = f->dev_vector; + _XrPenFindActiveCWVertexIndex(pen, &slope, &start); + slope.dx = -slope.dx; + slope.dy = -slope.dy; + _XrPenFindActiveCWVertexIndex(pen, &slope, &stop); + + tri[0] = f->pt; + tri[1] = f->cw; + for (i=start; i != stop; i = (i+1) % pen->num_vertices) { + tri[2] = f->pt; + _TranslatePoint(&tri[2], &pen->vertex[i].pt); + _XrTrapsTessellateTriangle(stroker->traps, tri); + tri[1] = tri[2]; + } + tri[2] = f->ccw; + + return _XrTrapsTessellateTriangle(stroker->traps, tri); + } case XrLineCapSquare: { double dx, dy; - XPointFixed fvector; + XrSlopeFixed fvector; XPointFixed occw, ocw; - dx = f->vector.x; - dy = f->vector.y; + XrPolygon polygon; + + _XrPolygonInit (&polygon); + + dx = f->usr_vector.x; + dy = f->usr_vector.y; dx *= gstate->line_width / 2.0; dy *= gstate->line_width / 2.0; _XrTransformDistance(&gstate->ctm, &dx, &dy); - fvector.x = XDoubleToFixed(dx); - fvector.y = XDoubleToFixed(dy); - occw.x = f->ccw.x + fvector.x; - occw.y = f->ccw.y + fvector.y; - ocw.x = f->cw.x + fvector.x; - ocw.y = f->cw.y + fvector.y; + fvector.dx = XDoubleToFixed(dx); + fvector.dy = XDoubleToFixed(dy); + occw.x = f->ccw.x + fvector.dx; + occw.y = f->ccw.y + fvector.dy; + ocw.x = f->cw.x + fvector.dx; + ocw.y = f->cw.y + fvector.dy; _XrPolygonAddEdge (&polygon, &f->cw, &ocw); _XrPolygonAddEdge (&polygon, &ocw, &occw); _XrPolygonAddEdge (&polygon, &occw, &f->ccw); _XrPolygonAddEdge (&polygon, &f->ccw, &f->cw); - break; + + status = _XrTrapsTessellatePolygon (stroker->traps, &polygon, XrFillRuleWinding); + _XrPolygonDeinit (&polygon); + + return status; } case XrLineCapButt: - break; + default: + return XrStatusSuccess; } - - status = _XrTrapsTessellatePolygon (stroker->traps, &polygon, XrFillRuleWinding); - _XrPolygonDeinit (&polygon); - - return status; } static void @@ -247,7 +305,7 @@ _ComputeFace(XPointFixed *pt, XrSlopeFixed *slope, XrGState *gstate, XrStrokeFac { double mag, tmp; double dx, dy; - XPointDouble user_vector; + XPointDouble usr_vector; XPointFixed offset_ccw, offset_cw; dx = XFixedToDouble(slope->dx); @@ -264,8 +322,8 @@ _ComputeFace(XPointFixed *pt, XrSlopeFixed *slope, XrGState *gstate, XrStrokeFac dx /= mag; dy /= mag; - user_vector.x = dx; - user_vector.y = dy; + usr_vector.x = dx; + usr_vector.y = dy; tmp = dx; dx = - dy * (gstate->line_width / 2.0); @@ -286,8 +344,10 @@ _ComputeFace(XPointFixed *pt, XrSlopeFixed *slope, XrGState *gstate, XrStrokeFac face->cw = *pt; _TranslatePoint(&face->cw, &offset_cw); - face->vector.x = user_vector.x; - face->vector.y = user_vector.y; + face->usr_vector.x = usr_vector.x; + face->usr_vector.y = usr_vector.y; + + face->dev_vector = *slope; } static XrStatus @@ -475,7 +535,7 @@ _XrStrokerAddSpline (void *closure, XPointFixed *a, XPointFixed *b, XPointFixed XrSpline spline; XrPen pen; XrStrokeFace start, end; - XrPenFlaggedPoint extra_points[4]; + XPointFixed extra_points[4]; status = _XrSplineInit(&spline, a, b, c, d); if (status == XrIntStatusDegenerate) @@ -502,18 +562,18 @@ _XrStrokerAddSpline (void *closure, XPointFixed *a, XPointFixed *b, XPointFixed stroker->prev = end; stroker->is_first = 0; - extra_points[0].pt = start.cw; extra_points[0].flag = XrPenVertexFlagForward; - extra_points[0].pt.x -= start.pt.x; - extra_points[0].pt.y -= start.pt.y; - extra_points[1].pt = start.ccw; extra_points[1].flag = XrPenVertexFlagNone; - extra_points[1].pt.x -= start.pt.x; - extra_points[1].pt.y -= start.pt.y; - extra_points[2].pt = end.cw; extra_points[2].flag = XrPenVertexFlagNone; - extra_points[2].pt.x -= end.pt.x; - extra_points[2].pt.y -= end.pt.y; - extra_points[3].pt = end.ccw; extra_points[3].flag = XrPenVertexFlagReverse; - extra_points[3].pt.x -= end.pt.x; - extra_points[3].pt.y -= end.pt.y; + extra_points[0] = start.cw; + extra_points[0].x -= start.pt.x; + extra_points[0].y -= start.pt.y; + extra_points[1] = start.ccw; + extra_points[1].x -= start.pt.x; + extra_points[1].y -= start.pt.y; + extra_points[2] = end.cw; + extra_points[2].x -= end.pt.x; + extra_points[2].y -= end.pt.y; + extra_points[3] = end.ccw; + extra_points[3].x -= end.pt.x; + extra_points[3].y -= end.pt.y; status = _XrPenAddPoints(&pen, extra_points, 4); if (status) @@ -548,6 +608,15 @@ _XrStrokerDoneSubPath (void *closure, XrSubPathDone done) /* fall through... */ case XrSubPathDoneCap: if (stroker->have_first) { + XPointFixed t; + /* The initial cap needs an outward facing vector. Reverse everything */ + stroker->first.usr_vector.x = -stroker->first.usr_vector.x; + stroker->first.usr_vector.y = -stroker->first.usr_vector.y; + stroker->first.dev_vector.dx = -stroker->first.dev_vector.dx; + stroker->first.dev_vector.dy = -stroker->first.dev_vector.dy; + t = stroker->first.cw; + stroker->first.cw = stroker->first.ccw; + stroker->first.ccw = t; status = _XrStrokerCap (stroker, &stroker->first); if (status) return status; diff --git a/src/xrtraps.c b/src/xrtraps.c index a43555330..6faa33f70 100644 --- a/src/xrtraps.c +++ b/src/xrtraps.c @@ -164,34 +164,66 @@ _ComparePointFixedByY (const void *av, const void *bv) XrStatus _XrTrapsTessellateTriangle (XrTraps *traps, XPointFixed t[3]) { + XrStatus status; XLineFixed line; double intersect; + XPointFixed tsort[3]; - qsort(t, 3, sizeof(XPointFixed), _ComparePointFixedByY); + memcpy(tsort, t, 3 * sizeof(XPointFixed)); + qsort(tsort, 3, sizeof(XPointFixed), _ComparePointFixedByY); /* horizontal top edge requires special handling */ - if (t[0].y == t[1].y) { - if (t[0].x < t[1].x) - _XrTrapsAddTrapFromPoints (traps, t[1].y, t[2].y, t[0], t[2], t[1], t[2]); + if (tsort[0].y == tsort[1].y) { + if (tsort[0].x < tsort[1].x) + status = _XrTrapsAddTrapFromPoints (traps, + tsort[1].y, tsort[2].y, + tsort[0], tsort[2], + tsort[1], tsort[2]); else - _XrTrapsAddTrapFromPoints (traps, t[1].y, t[2].y, t[1], t[2], t[0], t[2]); - return; + status = _XrTrapsAddTrapFromPoints (traps, + tsort[1].y, tsort[2].y, + tsort[1], tsort[2], + tsort[0], tsort[2]); + return status; } - line.p1 = t[0]; - line.p2 = t[1]; + line.p1 = tsort[0]; + line.p2 = tsort[1]; - intersect = _ComputeX (&line, t[2].y); + intersect = _ComputeX (&line, tsort[2].y); - if (intersect < t[2].x) { - _XrTrapsAddTrapFromPoints(traps, t[0].y, t[1].y, t[0], t[1], t[0], t[2]); - _XrTrapsAddTrapFromPoints(traps, t[1].y, t[2].y, t[1], t[2], t[0], t[2]); + if (intersect < tsort[2].x) { + status = _XrTrapsAddTrapFromPoints(traps, + tsort[0].y, tsort[1].y, + tsort[0], tsort[1], + tsort[0], tsort[2]); + if (status) + return status; + status = _XrTrapsAddTrapFromPoints(traps, + tsort[1].y, tsort[2].y, + tsort[1], tsort[2], + tsort[0], tsort[2]); + if (status) + return status; } else { - _XrTrapsAddTrapFromPoints(traps, t[0].y, t[1].y, t[0], t[2], t[0], t[1]); - _XrTrapsAddTrapFromPoints(traps, t[1].y, t[2].y, t[0], t[2], t[1], t[2]); + status = _XrTrapsAddTrapFromPoints(traps, + tsort[0].y, tsort[1].y, + tsort[0], tsort[2], + tsort[0], tsort[1]); + if (status) + return status; + status = _XrTrapsAddTrapFromPoints(traps, + tsort[1].y, tsort[2].y, + tsort[0], tsort[2], + tsort[1], tsort[2]); + if (status) + return status; } + + return XrStatusSuccess; } +/* Warning: This function reorderd the elements of the array provided. */ XrStatus _XrTrapsTessellateRectangle (XrTraps *traps, XPointFixed q[4]) { @@ -432,6 +464,8 @@ _SortEdgeList(XrEdge **active) active edges, forming a trapezoid between each adjacent pair. Then, either the even-odd or winding rule is used to determine whether to emit each of these trapezoids. + + Warning: This function reorders the edges of the polygon provided. */ XrStatus _XrTrapsTessellatePolygon (XrTraps *traps, diff --git a/xrgstate.c b/xrgstate.c index e8dab515a..2bfb3de7f 100644 --- a/xrgstate.c +++ b/xrgstate.c @@ -63,8 +63,8 @@ _XrGStateInit(XrGState *gstate, Display *dpy) gstate->fill_rule = XR_GSTATE_FILL_RULE_DEFAULT; - gstate->dashes = 0; - gstate->ndashes = 0; + gstate->dash = NULL; + gstate->num_dashes = 0; gstate->dash_offset = 0.0; gstate->alphaFormat = XcFindStandardFormat(dpy, PictStandardA8); @@ -98,11 +98,11 @@ _XrGStateInitCopy(XrGState *gstate, XrGState *other) XrStatus status; *gstate = *other; - if (other->dashes) { - gstate->dashes = malloc (other->ndashes * sizeof (double)); - if (gstate->dashes == NULL) + if (other->dash) { + gstate->dash = malloc (other->num_dashes * sizeof (double)); + if (gstate->dash == NULL) return XrStatusNoMemory; - memcpy(gstate->dashes, other->dashes, other->ndashes * sizeof (double)); + memcpy(gstate->dash, other->dash, other->num_dashes * sizeof (double)); } status = _XrFontInitCopy(&gstate->font, &other->font); @@ -130,8 +130,8 @@ _XrGStateInitCopy(XrGState *gstate, XrGState *other) CLEANUP_FONT: _XrFontDeinit(&gstate->font); CLEANUP_DASHES: - free (gstate->dashes); - gstate->dashes = NULL; + free (gstate->dash); + gstate->dash = NULL; return status; } @@ -158,9 +158,9 @@ _XrGStateDeinit(XrGState *gstate) _XrPenDeinit(&gstate->pen_regular); - if (gstate->dashes) { - free (gstate->dashes); - gstate->dashes = NULL; + if (gstate->dash) { + free (gstate->dash); + gstate->dash = NULL; } } @@ -363,19 +363,23 @@ _XrGStateSetLineJoin(XrGState *gstate, XrLineJoin line_join) } XrStatus -_XrGStateSetDash(XrGState *gstate, double *dashes, int ndash, double offset) +_XrGStateSetDash(XrGState *gstate, double *dash, int num_dashes, double offset) { - if (gstate->dashes) { - free (gstate->dashes); + if (gstate->dash) { + free (gstate->dash); + gstate->dash = NULL; } - gstate->dashes = malloc (ndash * sizeof (double)); - if (!gstate->dashes) { - gstate->ndashes = 0; - return XrStatusNoMemory; + gstate->num_dashes = num_dashes; + if (gstate->num_dashes) { + gstate->dash = malloc (gstate->num_dashes * sizeof (double)); + if (gstate->dash == NULL) { + gstate->num_dashes = 0; + return XrStatusNoMemory; + } } - gstate->ndashes = ndash; - memcpy (gstate->dashes, dashes, ndash * sizeof (double)); + + memcpy (gstate->dash, dash, gstate->num_dashes * sizeof (double)); gstate->dash_offset = offset; return XrStatusSuccess; @@ -639,7 +643,7 @@ _XrGStateStroke(XrGState *gstate) _XrStrokerDoneSubPath, _XrStrokerDonePath }; - XrPathCallbacks *cbs = gstate->dashes ? &cb_dash : &cb; + XrPathCallbacks *cbs = gstate->dash ? &cb_dash : &cb; XrStroker stroker; XrTraps traps; diff --git a/xrint.h b/xrint.h index 4e63d5ae6..00911f3e1 100644 --- a/xrint.h +++ b/xrint.h @@ -137,20 +137,14 @@ typedef struct _XrSpline { XPointFixed *pts; } XrSpline; -typedef enum _XrPenVertexFlag { - XrPenVertexFlagNone = 0, - XrPenVertexFlagForward = 1, - XrPenVertexFlagReverse = 2 -} XrPenVertexFlag; - -typedef struct _XrPenFlaggedPoint { - XPointFixed pt; - XrPenVertexFlag flag; -} XrPenFlaggedPoint; +/* XXX: This can go away once incremental spline tessellation is working */ +typedef enum _XrPenStrokeDirection { + XrPenStrokeDirectionForward, + XrPenStrokeDirectionReverse +} XrPenStrokeDirection; typedef struct _XrPenVertex { XPointFixed pt; - XrPenVertexFlag flag; double theta; XrSlopeFixed slope_ccw; @@ -244,8 +238,8 @@ typedef struct _XrGState { XrFillRule fill_rule; - double *dashes; - int ndashes; + double *dash; + int num_dashes; double dash_offset; XcFormat *alphaFormat; @@ -284,7 +278,8 @@ typedef struct _XrStrokeFace { XPointFixed ccw; XPointFixed pt; XPointFixed cw; - XPointDouble vector; + XrSlopeFixed dev_vector; + XPointDouble usr_vector; } XrStrokeFace; typedef struct _XrStroker { @@ -387,7 +382,7 @@ XrStatus _XrGStateSetLineJoin(XrGState *gstate, XrLineJoin line_join); XrStatus -_XrGStateSetDash(XrGState *gstate, double *dashes, int ndash, double offset); +_XrGStateSetDash(XrGState *gstate, double *dash, int num_dashes, double offset); XrStatus _XrGStateSetMiterLimit(XrGState *gstate, double limit); @@ -640,11 +635,17 @@ void _XrPenDeinit(XrPen *pen); XrStatus -_XrPenAddPoints(XrPen *pen, XrPenFlaggedPoint *pt, int num_pts); +_XrPenAddPoints(XrPen *pen, XPointFixed *pt, int num_pts); XrStatus _XrPenAddPointsForSlopes(XrPen *pen, XPointFixed *a, XPointFixed *b, XPointFixed *c, XPointFixed *d); +XrStatus +_XrPenFindActiveCWVertexIndex(XrPen *pen, XrSlopeFixed *slope, int *active); + +XrStatus +_XrPenFindActiveCCWVertexIndex(XrPen *pen, XrSlopeFixed *slope, int *active); + XrStatus _XrPenStrokeSpline(XrPen *pen, XrSpline *spline, double tolerance, XrTraps *traps); diff --git a/xrpen.c b/xrpen.c index 3c434993b..18c6310f0 100644 --- a/xrpen.c +++ b/xrpen.c @@ -41,8 +41,7 @@ static int _XrPenVertexCompareByTheta(const void *a, const void *b); static XrStatus -_XrPenStrokeSplineHalf(XrPen *pen, XrSpline *spline, - XrPenVertexFlag dir, XrPolygon *polygon); +_XrPenStrokeSplineHalf(XrPen *pen, XrSpline *spline, XrPenStrokeDirection dir, XrPolygon *polygon); XrStatus _XrPenInitEmpty(XrPen *pen) @@ -93,7 +92,10 @@ _XrPenInit(XrPen *pen, double radius, XrGState *gstate) _XrTransformDistance(&gstate->ctm, &dx, &dy); v->pt.x = XDoubleToFixed(dx); v->pt.y = XDoubleToFixed(dy); - v->flag = XrPenVertexFlagNone; + /* Recompute theta in device space */ + v->theta = atan2(v->pt.y, v->pt.x); + if (v->theta < 0) + v->theta += 2 * M_PI; } _XrPenComputeSlopes(pen); @@ -141,7 +143,7 @@ _XrPenVertexCompareByTheta(const void *a, const void *b) } XrStatus -_XrPenAddPoints(XrPen *pen, XrPenFlaggedPoint *pt, int num_pts) +_XrPenAddPoints(XrPen *pen, XPointFixed *pt, int num_pts) { int i, j; XrPenVertex *v, *v_next, *new_vertex; @@ -157,8 +159,7 @@ _XrPenAddPoints(XrPen *pen, XrPenFlaggedPoint *pt, int num_pts) /* initialize new vertices */ for (i=0; i < num_pts; i++) { v = &pen->vertex[pen->num_vertices-(i+1)]; - v->pt = pt[i].pt; - v->flag = pt[i].flag; + v->pt = pt[i]; v->theta = atan2(v->pt.y, v->pt.x); if (v->theta < 0) v->theta += 2 * M_PI; @@ -171,7 +172,6 @@ _XrPenAddPoints(XrPen *pen, XrPenFlaggedPoint *pt, int num_pts) v = &pen->vertex[i]; v_next = &pen->vertex[i+1]; if (v->pt.x == v_next->pt.x && v->pt.y == v_next->pt.y) { - v->flag |= v_next->flag; for (j=i+1; j < pen->num_vertices - 1; j++) pen->vertex[j] = pen->vertex[j+1]; pen->num_vertices--; @@ -223,6 +223,12 @@ _XrPenComputeSlopes(XrPen *pen) } } +/* Is a clockwise of b? + * + * NOTE: The strict equality here is not significant in and of itself, + * but there are functions up above that are sensitive to it, + * (cf. _XrPenFindActiveCWVertexIndex). + */ static int _SlopeClockwise(XrSlopeFixed *a, XrSlopeFixed *b) { @@ -240,40 +246,90 @@ _SlopeCounterClockwise(XrSlopeFixed *a, XrSlopeFixed *b) return ! _SlopeClockwise(a, b); } +/* Find active pen vertex for clockwise edge of stroke at the given slope. + * + * NOTE: The behavior of this function is sensitive to the sense of + * the inequality within _SlopeClockwise/_SlopeCounterClockwise. + * + * The issue is that the slope_ccw member of one pen vertex will be + * equivalent to the slope_cw member of the next pen vertex in a + * counterclockwise order. However, for this function, we care + * strongly about which vertex is returned. + */ +XrStatus +_XrPenFindActiveCWVertexIndex(XrPen *pen, XrSlopeFixed *slope, int *active) +{ + int i; + + for (i=0; i < pen->num_vertices; i++) { + if (_SlopeClockwise(slope, &pen->vertex[i].slope_ccw) + && _SlopeCounterClockwise(slope, &pen->vertex[i].slope_cw)) + break; + } + + *active = i; + + return XrStatusSuccess; +} + +/* Find active pen vertex for counterclockwise edge of stroke at the given slope. + * + * NOTE: The behavior of this function is sensitive to the sense of + * the inequality within _SlopeClockwise/_SlopeCounterClockwise. + */ +XrStatus +_XrPenFindActiveCCWVertexIndex(XrPen *pen, XrSlopeFixed *slope, int *active) +{ + int i; + XrSlopeFixed slope_reverse; + + slope_reverse = *slope; + slope_reverse.dx = -slope_reverse.dx; + slope_reverse.dy = -slope_reverse.dy; + + for (i=pen->num_vertices-1; i >= 0; i--) { + if (_SlopeCounterClockwise(&pen->vertex[i].slope_ccw, &slope_reverse) + && _SlopeClockwise(&pen->vertex[i].slope_cw, &slope_reverse)) + break; + } + + *active = i; + + return XrStatusSuccess; +} + static XrStatus -_XrPenStrokeSplineHalf(XrPen *pen, XrSpline *spline, - XrPenVertexFlag dir, XrPolygon *polygon) +_XrPenStrokeSplineHalf(XrPen *pen, XrSpline *spline, XrPenStrokeDirection dir, XrPolygon *polygon) { int i; XrStatus status; int start, stop, step; int active = 0; XPointFixed hull_pt; - XrSlopeFixed slope, final_slope; + XrSlopeFixed slope, initial_slope, final_slope; XPointFixed *pt = spline->pts; int num_pts = spline->num_pts; - for (i=0; i < pen->num_vertices; i++) { - if (pen->vertex[i].flag & dir) { - active = i; - break; - } - } - - if (dir == XrPenVertexFlagForward) { + if (dir == XrPenStrokeDirectionForward) { start = 0; stop = num_pts; step = 1; + initial_slope = spline->initial_slope; final_slope = spline->final_slope; } else { start = num_pts - 1; stop = -1; step = -1; + initial_slope = spline->final_slope; + initial_slope.dx = -initial_slope.dx; + initial_slope.dy = -initial_slope.dy; final_slope = spline->initial_slope; final_slope.dx = -final_slope.dx; final_slope.dy = -final_slope.dy; } + _XrPenFindActiveCWVertexIndex(pen, &initial_slope, &active); + i = start; while (i != stop) { hull_pt.x = pt[i].x + pen->vertex[active].pt.x; @@ -318,11 +374,11 @@ _XrPenStrokeSpline(XrPen *pen, if (status) return status; - status = _XrPenStrokeSplineHalf(pen, spline, XrPenVertexFlagForward, &polygon); + status = _XrPenStrokeSplineHalf(pen, spline, XrPenStrokeDirectionForward, &polygon); if (status) return status; - status = _XrPenStrokeSplineHalf(pen, spline, XrPenVertexFlagReverse, &polygon); + status = _XrPenStrokeSplineHalf(pen, spline, XrPenStrokeDirectionReverse, &polygon); if (status) return status; diff --git a/xrstroker.c b/xrstroker.c index 5a47df749..07592dea9 100644 --- a/xrstroker.c +++ b/xrstroker.c @@ -44,15 +44,15 @@ _XrStrokerStartDash (XrStroker *stroker) int i = 0; offset = gstate->dash_offset; - while (offset >= gstate->dashes[i]) { - offset -= gstate->dashes[i]; + while (offset >= gstate->dash[i]) { + offset -= gstate->dash[i]; on = 1-on; - if (++i == gstate->ndashes) + if (++i == gstate->num_dashes) i = 0; } stroker->dash_index = i; stroker->dash_on = on; - stroker->dash_remain = gstate->dashes[i] - offset; + stroker->dash_remain = gstate->dash[i] - offset; } static void @@ -62,10 +62,10 @@ _XrStrokerStepDash (XrStroker *stroker, double step) stroker->dash_remain -= step; if (stroker->dash_remain <= 0) { stroker->dash_index++; - if (stroker->dash_index == gstate->ndashes) + if (stroker->dash_index == gstate->num_dashes) stroker->dash_index = 0; stroker->dash_on = 1-stroker->dash_on; - stroker->dash_remain = gstate->dashes[stroker->dash_index]; + stroker->dash_remain = gstate->dash[stroker->dash_index]; } } @@ -77,7 +77,7 @@ _XrStrokerInit(XrStroker *stroker, XrGState *gstate, XrTraps *traps) stroker->have_prev = 0; stroker->have_first = 0; stroker->is_first = 1; - if (gstate->dashes) + if (gstate->dash) _XrStrokerStartDash (stroker); } @@ -112,12 +112,9 @@ _XrStrokerJoin(XrStroker *stroker, XrStrokeFace *in, XrStrokeFace *out) { XrStatus status; XrGState *gstate = stroker->gstate; - int clockwise = _XrStrokerFaceClockwise (in, out); + int clockwise = _XrStrokerFaceClockwise (out, in); XPointFixed *inpt, *outpt; - /* XXX: There might be a more natural place to check for the - degenerate join later in the code, (such as right before - dividing by zero) */ if (in->cw.x == out->cw.x && in->cw.y == out->cw.y && in->ccw.x == out->ccw.x @@ -126,20 +123,57 @@ _XrStrokerJoin(XrStroker *stroker, XrStrokeFace *in, XrStrokeFace *out) } if (clockwise) { - inpt = &in->cw; - outpt = &out->cw; - } else { inpt = &in->ccw; outpt = &out->ccw; + } else { + inpt = &in->cw; + outpt = &out->cw; } switch (gstate->line_join) { - case XrLineJoinRound: - status = XrStatusSuccess; - break; - case XrLineJoinMiter: { + case XrLineJoinRound: { + int i; + int start, step, stop; + XPointFixed tri[3], initial, final; + XrPen *pen = &gstate->pen_regular; + + tri[0] = in->pt; + if (clockwise) { + initial = in->ccw; + _XrPenFindActiveCCWVertexIndex(pen, &in->dev_vector, &start); + step = -1; + _XrPenFindActiveCCWVertexIndex(pen, &out->dev_vector, &stop); + final = out->ccw; + } else { + initial = in->cw; + _XrPenFindActiveCWVertexIndex(pen, &in->dev_vector, &start); + step = +1; + _XrPenFindActiveCWVertexIndex(pen, &out->dev_vector, &stop); + final = out->cw; + } + + i = start; + tri[1] = initial; + while (i != stop) { + tri[2] = in->pt; + _TranslatePoint(&tri[2], &pen->vertex[i].pt); + _XrTrapsTessellateTriangle(stroker->traps, tri); + tri[1] = tri[2]; + i += step; + if (i < 0) + i = pen->num_vertices - 1; + if (i >= pen->num_vertices) + i = 0; + } + + tri[2] = final; + + return _XrTrapsTessellateTriangle(stroker->traps, tri); + } + case XrLineJoinMiter: + default: { XrPolygon polygon; - XDouble c = (-in->vector.x * out->vector.x)+(-in->vector.y * out->vector.y); + XDouble c = (-in->usr_vector.x * out->usr_vector.x)+(-in->usr_vector.y * out->usr_vector.y); XDouble ml = gstate->miter_limit; _XrPolygonInit (&polygon); @@ -152,14 +186,14 @@ _XrStrokerJoin(XrStroker *stroker, XrStrokeFace *in, XrStrokeFace *out) x1 = XFixedToDouble(inpt->x); y1 = XFixedToDouble(inpt->y); - dx1 = in->vector.x; - dy1 = in->vector.y; + dx1 = in->usr_vector.x; + dy1 = in->usr_vector.y; _XrTransformDistance(&gstate->ctm, &dx1, &dy1); x2 = XFixedToDouble(outpt->x); y2 = XFixedToDouble(outpt->y); - dx2 = out->vector.x; - dy2 = out->vector.y; + dx2 = out->usr_vector.x; + dy2 = out->usr_vector.y; _XrTransformDistance(&gstate->ctm, &dx2, &dy2); my = (((x2 - x1) * dy1 * dy2 - y2 * dx2 * dy1 + y1 * dx1 * dy2) / @@ -179,7 +213,8 @@ _XrStrokerJoin(XrStroker *stroker, XrStrokeFace *in, XrStrokeFace *out) &polygon, XrFillRuleWinding); _XrPolygonDeinit (&polygon); - break; + + return status; } /* fall through ... */ } @@ -188,12 +223,10 @@ _XrStrokerJoin(XrStroker *stroker, XrStrokeFace *in, XrStrokeFace *out) tri[0] = in->pt; tri[1] = *inpt; tri[2] = *outpt; - status = _XrTrapsTessellateTriangle (stroker->traps, tri); - break; - } - } - return status; + return _XrTrapsTessellateTriangle (stroker->traps, tri); + } + } } static XrStatus @@ -201,45 +234,70 @@ _XrStrokerCap(XrStroker *stroker, XrStrokeFace *f) { XrStatus status; XrGState *gstate = stroker->gstate; - XrPolygon polygon; if (gstate->line_cap == XrLineCapButt) return XrStatusSuccess; - _XrPolygonInit (&polygon); switch (gstate->line_cap) { - case XrLineCapRound: - break; + case XrLineCapRound: { + int i; + int start, stop; + XrSlopeFixed slope; + XPointFixed tri[3]; + XrPen *pen = &gstate->pen_regular; + + slope = f->dev_vector; + _XrPenFindActiveCWVertexIndex(pen, &slope, &start); + slope.dx = -slope.dx; + slope.dy = -slope.dy; + _XrPenFindActiveCWVertexIndex(pen, &slope, &stop); + + tri[0] = f->pt; + tri[1] = f->cw; + for (i=start; i != stop; i = (i+1) % pen->num_vertices) { + tri[2] = f->pt; + _TranslatePoint(&tri[2], &pen->vertex[i].pt); + _XrTrapsTessellateTriangle(stroker->traps, tri); + tri[1] = tri[2]; + } + tri[2] = f->ccw; + + return _XrTrapsTessellateTriangle(stroker->traps, tri); + } case XrLineCapSquare: { double dx, dy; - XPointFixed fvector; + XrSlopeFixed fvector; XPointFixed occw, ocw; - dx = f->vector.x; - dy = f->vector.y; + XrPolygon polygon; + + _XrPolygonInit (&polygon); + + dx = f->usr_vector.x; + dy = f->usr_vector.y; dx *= gstate->line_width / 2.0; dy *= gstate->line_width / 2.0; _XrTransformDistance(&gstate->ctm, &dx, &dy); - fvector.x = XDoubleToFixed(dx); - fvector.y = XDoubleToFixed(dy); - occw.x = f->ccw.x + fvector.x; - occw.y = f->ccw.y + fvector.y; - ocw.x = f->cw.x + fvector.x; - ocw.y = f->cw.y + fvector.y; + fvector.dx = XDoubleToFixed(dx); + fvector.dy = XDoubleToFixed(dy); + occw.x = f->ccw.x + fvector.dx; + occw.y = f->ccw.y + fvector.dy; + ocw.x = f->cw.x + fvector.dx; + ocw.y = f->cw.y + fvector.dy; _XrPolygonAddEdge (&polygon, &f->cw, &ocw); _XrPolygonAddEdge (&polygon, &ocw, &occw); _XrPolygonAddEdge (&polygon, &occw, &f->ccw); _XrPolygonAddEdge (&polygon, &f->ccw, &f->cw); - break; + + status = _XrTrapsTessellatePolygon (stroker->traps, &polygon, XrFillRuleWinding); + _XrPolygonDeinit (&polygon); + + return status; } case XrLineCapButt: - break; + default: + return XrStatusSuccess; } - - status = _XrTrapsTessellatePolygon (stroker->traps, &polygon, XrFillRuleWinding); - _XrPolygonDeinit (&polygon); - - return status; } static void @@ -247,7 +305,7 @@ _ComputeFace(XPointFixed *pt, XrSlopeFixed *slope, XrGState *gstate, XrStrokeFac { double mag, tmp; double dx, dy; - XPointDouble user_vector; + XPointDouble usr_vector; XPointFixed offset_ccw, offset_cw; dx = XFixedToDouble(slope->dx); @@ -264,8 +322,8 @@ _ComputeFace(XPointFixed *pt, XrSlopeFixed *slope, XrGState *gstate, XrStrokeFac dx /= mag; dy /= mag; - user_vector.x = dx; - user_vector.y = dy; + usr_vector.x = dx; + usr_vector.y = dy; tmp = dx; dx = - dy * (gstate->line_width / 2.0); @@ -286,8 +344,10 @@ _ComputeFace(XPointFixed *pt, XrSlopeFixed *slope, XrGState *gstate, XrStrokeFac face->cw = *pt; _TranslatePoint(&face->cw, &offset_cw); - face->vector.x = user_vector.x; - face->vector.y = user_vector.y; + face->usr_vector.x = usr_vector.x; + face->usr_vector.y = usr_vector.y; + + face->dev_vector = *slope; } static XrStatus @@ -475,7 +535,7 @@ _XrStrokerAddSpline (void *closure, XPointFixed *a, XPointFixed *b, XPointFixed XrSpline spline; XrPen pen; XrStrokeFace start, end; - XrPenFlaggedPoint extra_points[4]; + XPointFixed extra_points[4]; status = _XrSplineInit(&spline, a, b, c, d); if (status == XrIntStatusDegenerate) @@ -502,18 +562,18 @@ _XrStrokerAddSpline (void *closure, XPointFixed *a, XPointFixed *b, XPointFixed stroker->prev = end; stroker->is_first = 0; - extra_points[0].pt = start.cw; extra_points[0].flag = XrPenVertexFlagForward; - extra_points[0].pt.x -= start.pt.x; - extra_points[0].pt.y -= start.pt.y; - extra_points[1].pt = start.ccw; extra_points[1].flag = XrPenVertexFlagNone; - extra_points[1].pt.x -= start.pt.x; - extra_points[1].pt.y -= start.pt.y; - extra_points[2].pt = end.cw; extra_points[2].flag = XrPenVertexFlagNone; - extra_points[2].pt.x -= end.pt.x; - extra_points[2].pt.y -= end.pt.y; - extra_points[3].pt = end.ccw; extra_points[3].flag = XrPenVertexFlagReverse; - extra_points[3].pt.x -= end.pt.x; - extra_points[3].pt.y -= end.pt.y; + extra_points[0] = start.cw; + extra_points[0].x -= start.pt.x; + extra_points[0].y -= start.pt.y; + extra_points[1] = start.ccw; + extra_points[1].x -= start.pt.x; + extra_points[1].y -= start.pt.y; + extra_points[2] = end.cw; + extra_points[2].x -= end.pt.x; + extra_points[2].y -= end.pt.y; + extra_points[3] = end.ccw; + extra_points[3].x -= end.pt.x; + extra_points[3].y -= end.pt.y; status = _XrPenAddPoints(&pen, extra_points, 4); if (status) @@ -548,6 +608,15 @@ _XrStrokerDoneSubPath (void *closure, XrSubPathDone done) /* fall through... */ case XrSubPathDoneCap: if (stroker->have_first) { + XPointFixed t; + /* The initial cap needs an outward facing vector. Reverse everything */ + stroker->first.usr_vector.x = -stroker->first.usr_vector.x; + stroker->first.usr_vector.y = -stroker->first.usr_vector.y; + stroker->first.dev_vector.dx = -stroker->first.dev_vector.dx; + stroker->first.dev_vector.dy = -stroker->first.dev_vector.dy; + t = stroker->first.cw; + stroker->first.cw = stroker->first.ccw; + stroker->first.ccw = t; status = _XrStrokerCap (stroker, &stroker->first); if (status) return status; diff --git a/xrtraps.c b/xrtraps.c index a43555330..6faa33f70 100644 --- a/xrtraps.c +++ b/xrtraps.c @@ -164,34 +164,66 @@ _ComparePointFixedByY (const void *av, const void *bv) XrStatus _XrTrapsTessellateTriangle (XrTraps *traps, XPointFixed t[3]) { + XrStatus status; XLineFixed line; double intersect; + XPointFixed tsort[3]; - qsort(t, 3, sizeof(XPointFixed), _ComparePointFixedByY); + memcpy(tsort, t, 3 * sizeof(XPointFixed)); + qsort(tsort, 3, sizeof(XPointFixed), _ComparePointFixedByY); /* horizontal top edge requires special handling */ - if (t[0].y == t[1].y) { - if (t[0].x < t[1].x) - _XrTrapsAddTrapFromPoints (traps, t[1].y, t[2].y, t[0], t[2], t[1], t[2]); + if (tsort[0].y == tsort[1].y) { + if (tsort[0].x < tsort[1].x) + status = _XrTrapsAddTrapFromPoints (traps, + tsort[1].y, tsort[2].y, + tsort[0], tsort[2], + tsort[1], tsort[2]); else - _XrTrapsAddTrapFromPoints (traps, t[1].y, t[2].y, t[1], t[2], t[0], t[2]); - return; + status = _XrTrapsAddTrapFromPoints (traps, + tsort[1].y, tsort[2].y, + tsort[1], tsort[2], + tsort[0], tsort[2]); + return status; } - line.p1 = t[0]; - line.p2 = t[1]; + line.p1 = tsort[0]; + line.p2 = tsort[1]; - intersect = _ComputeX (&line, t[2].y); + intersect = _ComputeX (&line, tsort[2].y); - if (intersect < t[2].x) { - _XrTrapsAddTrapFromPoints(traps, t[0].y, t[1].y, t[0], t[1], t[0], t[2]); - _XrTrapsAddTrapFromPoints(traps, t[1].y, t[2].y, t[1], t[2], t[0], t[2]); + if (intersect < tsort[2].x) { + status = _XrTrapsAddTrapFromPoints(traps, + tsort[0].y, tsort[1].y, + tsort[0], tsort[1], + tsort[0], tsort[2]); + if (status) + return status; + status = _XrTrapsAddTrapFromPoints(traps, + tsort[1].y, tsort[2].y, + tsort[1], tsort[2], + tsort[0], tsort[2]); + if (status) + return status; } else { - _XrTrapsAddTrapFromPoints(traps, t[0].y, t[1].y, t[0], t[2], t[0], t[1]); - _XrTrapsAddTrapFromPoints(traps, t[1].y, t[2].y, t[0], t[2], t[1], t[2]); + status = _XrTrapsAddTrapFromPoints(traps, + tsort[0].y, tsort[1].y, + tsort[0], tsort[2], + tsort[0], tsort[1]); + if (status) + return status; + status = _XrTrapsAddTrapFromPoints(traps, + tsort[1].y, tsort[2].y, + tsort[0], tsort[2], + tsort[1], tsort[2]); + if (status) + return status; } + + return XrStatusSuccess; } +/* Warning: This function reorderd the elements of the array provided. */ XrStatus _XrTrapsTessellateRectangle (XrTraps *traps, XPointFixed q[4]) { @@ -432,6 +464,8 @@ _SortEdgeList(XrEdge **active) active edges, forming a trapezoid between each adjacent pair. Then, either the even-odd or winding rule is used to determine whether to emit each of these trapezoids. + + Warning: This function reorders the edges of the polygon provided. */ XrStatus _XrTrapsTessellatePolygon (XrTraps *traps,