Added round caps/joins. Fixed initial cap. Fixed disabling of dashing.

This commit is contained in:
Carl Worth 2003-01-28 13:49:57 +00:00
parent 1507f1c795
commit a4b439eb69
11 changed files with 626 additions and 276 deletions

View file

@ -1,3 +1,25 @@
2003-01-28 Carl Worth <cworth@isi.edu>
* 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 <cworth@east.isi.edu>
* xrtraps.c (_XrTrapsTessellateTriangle): Restored triangle

View file

@ -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;

View file

@ -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);

View file

@ -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;

View file

@ -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;

View file

@ -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,

View file

@ -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;

33
xrint.h
View file

@ -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);

96
xrpen.c
View file

@ -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;

View file

@ -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;

View file

@ -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,