< prev index next >

src/java.desktop/share/classes/sun/java2d/marlin/Stroker.java

```*** 26,35 ****
--- 26,36 ----
package sun.java2d.marlin;

import java.util.Arrays;

import sun.awt.geom.PathConsumer2D;
+ import sun.java2d.marlin.Helpers.PolyStack;

// TODO: some of the arithmetic here is too verbose and prone to hard to
// debug typos. We should consider making a small Point/Vector class that
// has methods like plus(Point), minus(Point), dot(Point), cross(Point)and such
final class Stroker implements PathConsumer2D, MarlinConst {
*** 71,81 ****
// pisces used to use fixed point arithmetic with 16 decimal digits. I
// didn't want to change the values of the constant below when I converted
// it to floating point, so that's why the divisions by 2^16 are there.
private static final float ROUND_JOIN_THRESHOLD = 1000.0f/65536.0f;

!     private static final float C = 0.5522847498307933f;

private static final int MAX_N_CURVES = 11;

private PathConsumer2D out;

--- 72,86 ----
// pisces used to use fixed point arithmetic with 16 decimal digits. I
// didn't want to change the values of the constant below when I converted
// it to floating point, so that's why the divisions by 2^16 are there.
private static final float ROUND_JOIN_THRESHOLD = 1000.0f/65536.0f;

!     // kappa = (4/3) * (SQRT(2) - 1)
!     private static final float C = (float)(4.0d * (Math.sqrt(2.0d) - 1.0d) / 3.0d);
!
!     // SQRT(2)
!     private static final float SQRT_2 = (float)Math.sqrt(2.0d);

private static final int MAX_N_CURVES = 11;

private PathConsumer2D out;

*** 118,135 ****
final RendererContext rdrCtx;

// dirty curve
final Curve curve;

/**
* Constructs a <code>Stroker</code>.
* @param rdrCtx per-thread renderer context
*/
Stroker(final RendererContext rdrCtx) {
this.rdrCtx = rdrCtx;

!         this.reverse = new PolyStack(rdrCtx);
this.curve = rdrCtx.curve;
}

/**
* Inits the <code>Stroker</code>.
--- 123,162 ----
final RendererContext rdrCtx;

// dirty curve
final Curve curve;

+     // Bounds of the drawing region, at pixel precision.
+     private float[] clipRect;
+
+     // the outcode of the current point
+     private int cOutCode = 0;
+
+     // the outcode of the starting point
+     private int sOutCode = 0;
+
+     // flag indicating if the path is opened (clipped)
+     private boolean opened = false;
+     // flag indicating if the starting point's cap is done
+     private boolean capStart = false;
+
/**
* Constructs a <code>Stroker</code>.
* @param rdrCtx per-thread renderer context
*/
Stroker(final RendererContext rdrCtx) {
this.rdrCtx = rdrCtx;

!         this.reverse = (rdrCtx.stats != null) ?
!             new PolyStack(rdrCtx,
!                     rdrCtx.stats.stat_str_polystack_types,
!                     rdrCtx.stats.stat_str_polystack_curves,
!                     rdrCtx.stats.hist_str_polystack_curves,
!                     rdrCtx.stats.stat_array_str_polystack_curves,
!                     rdrCtx.stats.stat_array_str_polystack_types)
!             : new PolyStack(rdrCtx);
!
this.curve = rdrCtx.curve;
}

/**
* Inits the <code>Stroker</code>.
*** 141,182 ****
* <code>CAP_SQUARE</code>.
* @param joinStyle the desired line join style, one of
* <code>JOIN_MITER</code>, <code>JOIN_ROUND</code> or
* <code>JOIN_BEVEL</code>.
* @param miterLimit the desired miter limit
* @return this instance
*/
!     Stroker init(PathConsumer2D pc2d,
!               float lineWidth,
!               int capStyle,
!               int joinStyle,
!               float miterLimit)
{
this.out = pc2d;

this.lineWidth2 = lineWidth / 2.0f;
this.invHalfLineWidth2Sq = 1.0f / (2.0f * lineWidth2 * lineWidth2);
this.capStyle = capStyle;
this.joinStyle = joinStyle;

!         float limit = miterLimit * lineWidth2;
this.miterLimitSq = limit * limit;

this.prev = CLOSE;

rdrCtx.stroking = 1;

return this; // fluent API
}

/**
* Disposes this stroker:
* clean up before reusing this instance
*/
void dispose() {
reverse.dispose();

if (DO_CLEAN_DIRTY) {
// Force zero-fill dirty arrays:
Arrays.fill(offset0, 0.0f);
Arrays.fill(offset1, 0.0f);
Arrays.fill(offset2, 0.0f);
--- 168,242 ----
* <code>CAP_SQUARE</code>.
* @param joinStyle the desired line join style, one of
* <code>JOIN_MITER</code>, <code>JOIN_ROUND</code> or
* <code>JOIN_BEVEL</code>.
* @param miterLimit the desired miter limit
+      * @param scale scaling factor applied to clip boundaries
* @return this instance
*/
!     Stroker init(final PathConsumer2D pc2d,
!                  final float lineWidth,
!                  final int capStyle,
!                  final int joinStyle,
!                  final float miterLimit,
!                  final float scale)
{
this.out = pc2d;

this.lineWidth2 = lineWidth / 2.0f;
this.invHalfLineWidth2Sq = 1.0f / (2.0f * lineWidth2 * lineWidth2);
this.capStyle = capStyle;
this.joinStyle = joinStyle;

!         final float limit = miterLimit * lineWidth2;
this.miterLimitSq = limit * limit;

this.prev = CLOSE;

rdrCtx.stroking = 1;

+         if (rdrCtx.doClip) {
+             // Adjust the clipping rectangle with the stroker margin (miter limit, width)
+             float rdrOffX = 0.0f, rdrOffY = 0.0f;
+             float margin = lineWidth2;
+
+             if (capStyle == CAP_SQUARE) {
+                 margin *= SQRT_2;
+             }
+             if ((joinStyle == JOIN_MITER) && (margin < limit)) {
+                 margin = limit;
+             }
+             if (scale != 1.0f) {
+                 margin *= scale;
+                 rdrOffX = scale * Renderer.RDR_OFFSET_X;
+                 rdrOffY = scale * Renderer.RDR_OFFSET_Y;
+             }
+
+             // bounds as half-open intervals: minX <= x < maxX and minY <= y < maxY
+             // adjust clip rectangle (ymin, ymax, xmin, xmax):
+             final float[] _clipRect = rdrCtx.clipRect;
+             _clipRect[0] -= margin - rdrOffY;
+             _clipRect[1] += margin + rdrOffY;
+             _clipRect[2] -= margin - rdrOffX;
+             _clipRect[3] += margin + rdrOffX;
+             this.clipRect = _clipRect;
+         } else {
+             this.clipRect = null;
+         }
return this; // fluent API
}

/**
* Disposes this stroker:
* clean up before reusing this instance
*/
void dispose() {
reverse.dispose();

+         opened   = false;
+         capStart = false;
+
if (DO_CLEAN_DIRTY) {
// Force zero-fill dirty arrays:
Arrays.fill(offset0, 0.0f);
Arrays.fill(offset1, 0.0f);
Arrays.fill(offset2, 0.0f);
*** 443,529 ****
emitLineTo(miterX, miterY, rev);
}
}

@Override
!     public void moveTo(float x0, float y0) {
!         if (prev == DRAWING_OP_TO) {
!             finish();
}
-         this.sx0 = this.cx0 = x0;
-         this.sy0 = this.cy0 = y0;
-         this.cdx = this.sdx = 1.0f;
-         this.cdy = this.sdy = 0.0f;
-         this.prev = MOVE_TO;
}

@Override
!     public void lineTo(float x1, float y1) {
float dx = x1 - cx0;
float dy = y1 - cy0;
if (dx == 0.0f && dy == 0.0f) {
dx = 1.0f;
}
computeOffset(dx, dy, lineWidth2, offset0);
final float mx = offset0[0];
final float my = offset0[1];

!         drawJoin(cdx, cdy, cx0, cy0, dx, dy, cmx, cmy, mx, my);

emitLineTo(cx0 + mx, cy0 + my);
emitLineTo( x1 + mx,  y1 + my);

emitLineToRev(cx0 - mx, cy0 - my);
emitLineToRev( x1 - mx,  y1 - my);

!         this.cmx = mx;
!         this.cmy = my;
!         this.cdx = dx;
!         this.cdy = dy;
this.cx0 = x1;
this.cy0 = y1;
!         this.prev = DRAWING_OP_TO;
}

@Override
public void closePath() {
!         if (prev != DRAWING_OP_TO) {
if (prev == CLOSE) {
return;
}
emitMoveTo(cx0, cy0 - lineWidth2);
!             this.cmx = this.smx = 0.0f;
!             this.cmy = this.smy = -lineWidth2;
!             this.cdx = this.sdx = 1.0f;
!             this.cdy = this.sdy = 0.0f;
!             finish();
return;
}

!         if (cx0 != sx0 || cy0 != sy0) {
!             lineTo(sx0, sy0);
!         }

!         drawJoin(cdx, cdy, cx0, cy0, sdx, sdy, cmx, cmy, smx, smy);

!         emitLineTo(sx0 + smx, sy0 + smy);

!         emitMoveTo(sx0 - smx, sy0 - smy);
emitReverse();

this.prev = CLOSE;
!         emitClose();
}

private void emitReverse() {
reverse.popAll(out);
}

@Override
public void pathDone() {
if (prev == DRAWING_OP_TO) {
!             finish();
}

out.pathDone();

// this shouldn't matter since this object won't be used
--- 503,653 ----
emitLineTo(miterX, miterY, rev);
}
}

@Override
!     public void moveTo(final float x0, final float y0) {
!         moveTo(x0, y0, cOutCode);
!         // update starting point:
!         this.sx0 = x0;
!         this.sy0 = y0;
!         this.sdx = 1.0f;
!         this.sdy = 0.0f;
!         this.opened   = false;
!         this.capStart = false;
!
!         if (clipRect != null) {
!             final int outcode = Helpers.outcode(x0, y0, clipRect);
!             this.cOutCode = outcode;
!             this.sOutCode = outcode;
!         }
!     }
!
!     private void moveTo(final float x0, final float y0,
!                         final int outcode)
!     {
!         if (prev == MOVE_TO) {
!             this.cx0 = x0;
!             this.cy0 = y0;
!         } else {
!             if (prev == DRAWING_OP_TO) {
!                 finish(outcode);
!             }
!             this.prev = MOVE_TO;
!             this.cx0 = x0;
!             this.cy0 = y0;
!             this.cdx = 1.0f;
!             this.cdy = 0.0f;
}
}

@Override
!     public void lineTo(final float x1, final float y1) {
!         lineTo(x1, y1, false);
!     }
!
!     private void lineTo(final float x1, final float y1,
!                         final boolean force)
!     {
!         final int outcode0 = this.cOutCode;
!         if (!force && clipRect != null) {
!             final int outcode1 = Helpers.outcode(x1, y1, clipRect);
!             this.cOutCode = outcode1;
!
!             // basic rejection criteria
!             if ((outcode0 & outcode1) != 0) {
!                 moveTo(x1, y1, outcode0);
!                 opened = true;
!                 return;
!             }
!         }
!
float dx = x1 - cx0;
float dy = y1 - cy0;
if (dx == 0.0f && dy == 0.0f) {
dx = 1.0f;
}
computeOffset(dx, dy, lineWidth2, offset0);
final float mx = offset0[0];
final float my = offset0[1];

!         drawJoin(cdx, cdy, cx0, cy0, dx, dy, cmx, cmy, mx, my, outcode0);

emitLineTo(cx0 + mx, cy0 + my);
emitLineTo( x1 + mx,  y1 + my);

emitLineToRev(cx0 - mx, cy0 - my);
emitLineToRev( x1 - mx,  y1 - my);

!         this.prev = DRAWING_OP_TO;
this.cx0 = x1;
this.cy0 = y1;
!         this.cdx = dx;
!         this.cdy = dy;
!         this.cmx = mx;
!         this.cmy = my;
}

@Override
public void closePath() {
!         // distinguish empty path at all vs opened path ?
!         if (prev != DRAWING_OP_TO && !opened) {
if (prev == CLOSE) {
return;
}
emitMoveTo(cx0, cy0 - lineWidth2);
!
!             this.sdx = 1.0f;
!             this.sdy = 0.0f;
!             this.cdx = 1.0f;
!             this.cdy = 0.0f;
!
!             this.smx = 0.0f;
!             this.smy = -lineWidth2;
!             this.cmx = 0.0f;
!             this.cmy = -lineWidth2;
!
!             finish(cOutCode);
return;
}

!         if (sOutCode == 0) {
!             if (cx0 != sx0 || cy0 != sy0) {
!                 lineTo(sx0, sy0, true);
!             }

!             drawJoin(cdx, cdy, cx0, cy0, sdx, sdy, cmx, cmy, smx, smy, cOutCode);

!             emitLineTo(sx0 + smx, sy0 + smy);

!             if (opened) {
!                 emitLineTo(sx0 - smx, sy0 - smy);
!             } else {
!                 emitMoveTo(sx0 - smx, sy0 - smy);
!             }
!         }
!         // Ignore caps like finish(false)
emitReverse();

this.prev = CLOSE;
!
!         if (opened) {
!             // do not emit close
!             opened = false;
!         } else {
!             emitClose();
!         }
}

private void emitReverse() {
reverse.popAll(out);
}

@Override
public void pathDone() {
if (prev == DRAWING_OP_TO) {
!             finish(cOutCode);
}

out.pathDone();

// this shouldn't matter since this object won't be used
*** 532,558 ****

// Dispose this instance:
dispose();
}

!     private void finish() {
!         if (capStyle == CAP_ROUND) {
!             drawRoundCap(cx0, cy0, cmx, cmy);
!         } else if (capStyle == CAP_SQUARE) {
!             emitLineTo(cx0 - cmy + cmx, cy0 + cmx + cmy);
!             emitLineTo(cx0 - cmy - cmx, cy0 + cmx - cmy);
!         }

!         emitReverse();

!         if (capStyle == CAP_ROUND) {
!             drawRoundCap(sx0, sy0, -smx, -smy);
!         } else if (capStyle == CAP_SQUARE) {
!             emitLineTo(sx0 + smy - smx, sy0 - smx - smy);
!             emitLineTo(sx0 + smy + smx, sy0 - smx + smy);
}
-
emitClose();
}

private void emitMoveTo(final float x0, final float y0) {
out.moveTo(x0, y0);
--- 656,698 ----

// Dispose this instance:
dispose();
}

!     private void finish(final int outcode) {
!         // Problem: impossible to guess if the path will be closed in advance
!         //          i.e. if caps must be drawn or not ?
!         // Solution: use the ClosedPathDetector before Stroker to determine
!         // if the path is a closed path or not
!         if (!rdrCtx.closedPath) {
!             if (outcode == 0) {
!                 // current point = end's cap:
!                 if (capStyle == CAP_ROUND) {
!                     drawRoundCap(cx0, cy0, cmx, cmy);
!                 } else if (capStyle == CAP_SQUARE) {
!                     emitLineTo(cx0 - cmy + cmx, cy0 + cmx + cmy);
!                     emitLineTo(cx0 - cmy - cmx, cy0 + cmx - cmy);
!                 }
!             }
!             emitReverse();

!             if (!capStart) {
!                 capStart = true;

!                 if (sOutCode == 0) {
!                     // starting point = initial cap:
!                     if (capStyle == CAP_ROUND) {
!                         drawRoundCap(sx0, sy0, -smx, -smy);
!                     } else if (capStyle == CAP_SQUARE) {
!                         emitLineTo(sx0 + smy - smx, sy0 - smx - smy);
!                         emitLineTo(sx0 + smy + smx, sy0 - smx + smy);
!                     }
!                 }
!             }
!         } else {
!             emitReverse();
}
emitClose();
}

private void emitMoveTo(final float x0, final float y0) {
out.moveTo(x0, y0);
*** 620,646 ****

private void drawJoin(float pdx, float pdy,
float x0, float y0,
float dx, float dy,
float omx, float omy,
!                           float mx, float my)
{
if (prev != DRAWING_OP_TO) {
emitMoveTo(x0 + mx, y0 + my);
!             this.sdx = dx;
!             this.sdy = dy;
!             this.smx = mx;
!             this.smy = my;
} else {
!             boolean cw = isCW(pdx, pdy, dx, dy);
!             if (joinStyle == JOIN_MITER) {
!                 drawMiter(pdx, pdy, x0, y0, dx, dy, omx, omy, mx, my, cw);
!             } else if (joinStyle == JOIN_ROUND) {
!                 drawRoundJoin(x0, y0,
!                               omx, omy,
!                               mx, my, cw,
!                               ROUND_JOIN_THRESHOLD);
}
emitLineTo(x0, y0, !cw);
}
prev = DRAWING_OP_TO;
}
--- 760,791 ----

private void drawJoin(float pdx, float pdy,
float x0, float y0,
float dx, float dy,
float omx, float omy,
!                           float mx, float my,
!                           final int outcode)
{
if (prev != DRAWING_OP_TO) {
emitMoveTo(x0 + mx, y0 + my);
!             if (!opened) {
!                 this.sdx = dx;
!                 this.sdy = dy;
!                 this.smx = mx;
!                 this.smy = my;
!             }
} else {
!             final boolean cw = isCW(pdx, pdy, dx, dy);
!             if (outcode == 0) {
!                 if (joinStyle == JOIN_MITER) {
!                     drawMiter(pdx, pdy, x0, y0, dx, dy, omx, omy, mx, my, cw);
!                 } else if (joinStyle == JOIN_ROUND) {
!                     drawRoundJoin(x0, y0,
!                                   omx, omy,
!                                   mx, my, cw,
!                                   ROUND_JOIN_THRESHOLD);
!                 }
}
emitLineTo(x0, y0, !cw);
}
prev = DRAWING_OP_TO;
}
*** 941,963 ****
ret = Helpers.filterOutNotInAB(ts, 0, ret, 0.0001f, 0.9999f);
Helpers.isort(ts, 0, ret);
return ret;
}

!     @Override public void curveTo(float x1, float y1,
!                                   float x2, float y2,
!                                   float x3, float y3)
!     {
final float[] mid = middle;

mid[0] = cx0; mid[1] = cy0;
mid[2] = x1;  mid[3] = y1;
mid[4] = x2;  mid[5] = y2;
mid[6] = x3;  mid[7] = y3;

// need these so we can update the state at the end of this method
!         final float xf = mid[6], yf = mid[7];
float dxs = mid[2] - mid[0];
float dys = mid[3] - mid[1];
float dxf = mid[6] - mid[4];
float dyf = mid[7] - mid[5];

--- 1086,1127 ----
ret = Helpers.filterOutNotInAB(ts, 0, ret, 0.0001f, 0.9999f);
Helpers.isort(ts, 0, ret);
return ret;
}

!     @Override
!     public void curveTo(final float x1, final float y1,
!                         final float x2, final float y2,
!                         final float x3, final float y3)
!     {
!         final int outcode0 = this.cOutCode;
!         if (clipRect != null) {
!             final int outcode3 = Helpers.outcode(x3, y3, clipRect);
!             this.cOutCode = outcode3;
!
!             if (outcode3 != 0) {
!                 final int outcode1 = Helpers.outcode(x1, y1, clipRect);
!                 final int outcode2 = Helpers.outcode(x2, y2, clipRect);
!
!                 // basic rejection criteria
!                 if ((outcode0 & outcode1 & outcode2 & outcode3) != 0) {
!                     moveTo(x3, y3, outcode0);
!                     opened = true;
!                     return;
!                 }
!             }
!         }
!
final float[] mid = middle;

mid[0] = cx0; mid[1] = cy0;
mid[2] = x1;  mid[3] = y1;
mid[4] = x2;  mid[5] = y2;
mid[6] = x3;  mid[7] = y3;

// need these so we can update the state at the end of this method
!         final float xf = x3, yf = y3;
float dxs = mid[2] - mid[0];
float dys = mid[3] - mid[1];
float dxf = mid[6] - mid[4];
float dyf = mid[7] - mid[5];

*** 979,988 ****
--- 1143,1156 ----
dyf = mid[7] - mid[1];
}
}
if (dxs == 0.0f && dys == 0.0f) {
// this happens if the "curve" is just a point
+             // fix outcode0 for lineTo() call:
+             if (clipRect != null) {
+                 this.cOutCode = outcode0;
+             }
lineTo(mid[0], mid[1]);
return;
}

// if these vectors are too small, normalize them, to avoid future
*** 997,1007 ****
dxf /= len;
dyf /= len;
}

computeOffset(dxs, dys, lineWidth2, offset0);
!         drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1]);

final int nSplits = findSubdivPoints(curve, mid, subdivTs, 8, lineWidth2);

float prevT = 0.0f;
for (int i = 0, off = 0; i < nSplits; i++, off += 6) {
--- 1165,1175 ----
dxf /= len;
dyf /= len;
}

computeOffset(dxs, dys, lineWidth2, offset0);
!         drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1], outcode0);

final int nSplits = findSubdivPoints(curve, mid, subdivTs, 8, lineWidth2);

float prevT = 0.0f;
for (int i = 0, off = 0; i < nSplits; i++, off += 6) {
*** 1032,1069 ****
default:
}
emitLineToRev(r[kind - 2], r[kind - 1]);
}

!         this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0f;
!         this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0f;
!         this.cdx = dxf;
!         this.cdy = dyf;
this.cx0 = xf;
this.cy0 = yf;
!         this.prev = DRAWING_OP_TO;
}

!     @Override public void quadTo(float x1, float y1, float x2, float y2) {
final float[] mid = middle;

mid[0] = cx0; mid[1] = cy0;
mid[2] = x1;  mid[3] = y1;
mid[4] = x2;  mid[5] = y2;

// need these so we can update the state at the end of this method
!         final float xf = mid[4], yf = mid[5];
float dxs = mid[2] - mid[0];
float dys = mid[3] - mid[1];
float dxf = mid[4] - mid[2];
float dyf = mid[5] - mid[3];
if ((dxs == 0.0f && dys == 0.0f) || (dxf == 0.0f && dyf == 0.0f)) {
dxs = dxf = mid[4] - mid[0];
dys = dyf = mid[5] - mid[1];
}
if (dxs == 0.0f && dys == 0.0f) {
// this happens if the "curve" is just a point
lineTo(mid[0], mid[1]);
return;
}
// if these vectors are too small, normalize them, to avoid future
// precision problems.
--- 1200,1261 ----
default:
}
emitLineToRev(r[kind - 2], r[kind - 1]);
}

!         this.prev = DRAWING_OP_TO;
this.cx0 = xf;
this.cy0 = yf;
!         this.cdx = dxf;
!         this.cdy = dyf;
!         this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0f;
!         this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0f;
}

!     @Override
!     public void quadTo(final float x1, final float y1,
!                        final float x2, final float y2)
!     {
!         final int outcode0 = this.cOutCode;
!         if (clipRect != null) {
!             final int outcode2 = Helpers.outcode(x2, y2, clipRect);
!             this.cOutCode = outcode2;
!
!             if (outcode2 != 0) {
!                 final int outcode1 = Helpers.outcode(x1, y1, clipRect);
!
!                 // basic rejection criteria
!                 if ((outcode0 & outcode1 & outcode2) != 0) {
!                     moveTo(x2, y2, outcode0);
!                     opened = true;
!                     return;
!                 }
!             }
!         }
!
final float[] mid = middle;

mid[0] = cx0; mid[1] = cy0;
mid[2] = x1;  mid[3] = y1;
mid[4] = x2;  mid[5] = y2;

// need these so we can update the state at the end of this method
!         final float xf = x2, yf = y2;
float dxs = mid[2] - mid[0];
float dys = mid[3] - mid[1];
float dxf = mid[4] - mid[2];
float dyf = mid[5] - mid[3];
if ((dxs == 0.0f && dys == 0.0f) || (dxf == 0.0f && dyf == 0.0f)) {
dxs = dxf = mid[4] - mid[0];
dys = dyf = mid[5] - mid[1];
}
if (dxs == 0.0f && dys == 0.0f) {
// this happens if the "curve" is just a point
+             // fix outcode0 for lineTo() call:
+             if (clipRect != null) {
+                 this.cOutCode = outcode0;
+             }
lineTo(mid[0], mid[1]);
return;
}
// if these vectors are too small, normalize them, to avoid future
// precision problems.
*** 1077,1087 ****
dxf /= len;
dyf /= len;
}

computeOffset(dxs, dys, lineWidth2, offset0);
!         drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1]);

int nSplits = findSubdivPoints(curve, mid, subdivTs, 6, lineWidth2);

float prevt = 0.0f;
for (int i = 0, off = 0; i < nSplits; i++, off += 4) {
--- 1269,1279 ----
dxf /= len;
dyf /= len;
}

computeOffset(dxs, dys, lineWidth2, offset0);
!         drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1], outcode0);

int nSplits = findSubdivPoints(curve, mid, subdivTs, 6, lineWidth2);

float prevt = 0.0f;
for (int i = 0, off = 0; i < nSplits; i++, off += 4) {
*** 1112,1327 ****
default:
}
emitLineToRev(r[kind - 2], r[kind - 1]);
}

!         this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0f;
!         this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0f;
!         this.cdx = dxf;
!         this.cdy = dyf;
this.cx0 = xf;
this.cy0 = yf;
!         this.prev = DRAWING_OP_TO;
}

@Override public long getNativeConsumer() {
throw new InternalError("Stroker doesn't use a native consumer");
}
-
-     // a stack of polynomial curves where each curve shares endpoints with
-     static final class PolyStack {
-         private static final byte TYPE_LINETO  = (byte) 0;
-         private static final byte TYPE_QUADTO  = (byte) 1;
-         private static final byte TYPE_CUBICTO = (byte) 2;
-
-         // curves capacity = edges count (8192) = edges x 2 (coords)
-         private static final int INITIAL_CURVES_COUNT = INITIAL_EDGES_COUNT << 1;
-
-         // types capacity = edges count (4096)
-         private static final int INITIAL_TYPES_COUNT = INITIAL_EDGES_COUNT;
-
-         float[] curves;
-         int end;
-         byte[] curveTypes;
-         int numCurves;
-
-         final RendererContext rdrCtx;
-
-         // curves ref (dirty)
-         final FloatArrayCache.Reference curves_ref;
-         // curveTypes ref (dirty)
-         final ByteArrayCache.Reference curveTypes_ref;
-
-         // used marks (stats only)
-         int curveTypesUseMark;
-         int curvesUseMark;
-
-         /**
-          * Constructor
-          * @param rdrCtx per-thread renderer context
-          */
-         PolyStack(final RendererContext rdrCtx) {
-             this.rdrCtx = rdrCtx;
-
-             curves_ref = rdrCtx.newDirtyFloatArrayRef(INITIAL_CURVES_COUNT); // 32K
-             curves     = curves_ref.initial;
-
-             curveTypes_ref = rdrCtx.newDirtyByteArrayRef(INITIAL_TYPES_COUNT); // 4K
-             curveTypes     = curveTypes_ref.initial;
-             numCurves = 0;
-             end = 0;
-
-             if (DO_STATS) {
-                 curveTypesUseMark = 0;
-                 curvesUseMark = 0;
-             }
-         }
-
-         /**
-          * Disposes this PolyStack:
-          * clean up before reusing this instance
-          */
-         void dispose() {
-             end = 0;
-             numCurves = 0;
-
-             if (DO_STATS) {
-
-                 // reset marks
-                 curveTypesUseMark = 0;
-                 curvesUseMark = 0;
-             }
-
-             // Return arrays:
-             // curves and curveTypes are kept dirty
-             curves     = curves_ref.putArray(curves);
-             curveTypes = curveTypes_ref.putArray(curveTypes);
-         }
-
-         private void ensureSpace(final int n) {
-             // use substraction to avoid integer overflow:
-             if (curves.length - end < n) {
-                 if (DO_STATS) {
-                     rdrCtx.stats.stat_array_stroker_polystack_curves
-                 }
-                 curves = curves_ref.widenArray(curves, end, end + n);
-             }
-             if (curveTypes.length <= numCurves) {
-                 if (DO_STATS) {
-                     rdrCtx.stats.stat_array_stroker_polystack_curveTypes
-                 }
-                 curveTypes = curveTypes_ref.widenArray(curveTypes,
-                                                        numCurves,
-                                                        numCurves + 1);
-             }
-         }
-
-         void pushCubic(float x0, float y0,
-                        float x1, float y1,
-                        float x2, float y2)
-         {
-             ensureSpace(6);
-             curveTypes[numCurves++] = TYPE_CUBICTO;
-             // we reverse the coordinate order to make popping easier
-             final float[] _curves = curves;
-             int e = end;
-             _curves[e++] = x2;    _curves[e++] = y2;
-             _curves[e++] = x1;    _curves[e++] = y1;
-             _curves[e++] = x0;    _curves[e++] = y0;
-             end = e;
-         }
-
-         void pushQuad(float x0, float y0,
-                       float x1, float y1)
-         {
-             ensureSpace(4);
-             final float[] _curves = curves;
-             int e = end;
-             _curves[e++] = x1;    _curves[e++] = y1;
-             _curves[e++] = x0;    _curves[e++] = y0;
-             end = e;
-         }
-
-         void pushLine(float x, float y) {
-             ensureSpace(2);
-             curveTypes[numCurves++] = TYPE_LINETO;
-             curves[end++] = x;    curves[end++] = y;
-         }
-
-         void popAll(PathConsumer2D io) {
-             if (DO_STATS) {
-                 // update used marks:
-                 if (numCurves > curveTypesUseMark) {
-                     curveTypesUseMark = numCurves;
-                 }
-                 if (end > curvesUseMark) {
-                     curvesUseMark = end;
-                 }
-             }
-             final byte[]  _curveTypes = curveTypes;
-             final float[] _curves = curves;
-             int nc = numCurves;
-             int e  = end;
-
-             while (nc != 0) {
-                 switch(_curveTypes[--nc]) {
-                 case TYPE_LINETO:
-                     e -= 2;
-                     io.lineTo(_curves[e], _curves[e+1]);
-                     continue;
-                     e -= 4;
-                               _curves[e+2], _curves[e+3]);
-                     continue;
-                 case TYPE_CUBICTO:
-                     e -= 6;
-                     io.curveTo(_curves[e+0], _curves[e+1],
-                                _curves[e+2], _curves[e+3],
-                                _curves[e+4], _curves[e+5]);
-                     continue;
-                 default:
-                 }
-             }
-             numCurves = 0;
-             end = 0;
-         }
-
-         @Override
-         public String toString() {
-             String ret = "";
-             int nc = numCurves;
-             int last = end;
-             int len;
-             while (nc != 0) {
-                 switch(curveTypes[--nc]) {
-                 case TYPE_LINETO:
-                     len = 2;
-                     ret += "line: ";
-                     break;
-                     len = 4;
-                     break;
-                 case TYPE_CUBICTO:
-                     len = 6;
-                     ret += "cubic: ";
-                     break;
-                 default:
-                     len = 0;
-                 }
-                 last -= len;
-                 ret += Arrays.toString(Arrays.copyOfRange(curves, last, last+len))
-                                        + "\n";
-             }
-             return ret;
-         }
-     }
}
--- 1304,1321 ----
default:
}
emitLineToRev(r[kind - 2], r[kind - 1]);
}

!         this.prev = DRAWING_OP_TO;
this.cx0 = xf;
this.cy0 = yf;
!         this.cdx = dxf;
!         this.cdy = dyf;
!         this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0f;
!         this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0f;
}

@Override public long getNativeConsumer() {
throw new InternalError("Stroker doesn't use a native consumer");
}
}
```
< prev index next >