< prev index next >

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

Print this page

        

*** 26,81 **** package sun.java2d.marlin; import java.util.Arrays; import sun.awt.geom.PathConsumer2D; // 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 { private static final int MOVE_TO = 0; private static final int DRAWING_OP_TO = 1; // ie. curve, line, or quad private static final int CLOSE = 2; - /** - * Constant value for join style. - */ - public static final int JOIN_MITER = 0; - - /** - * Constant value for join style. - */ - public static final int JOIN_ROUND = 1; - - /** - * Constant value for join style. - */ - public static final int JOIN_BEVEL = 2; - - /** - * Constant value for end cap style. - */ - public static final int CAP_BUTT = 0; - - /** - * Constant value for end cap style. - */ - public static final int CAP_ROUND = 1; - - /** - * Constant value for end cap style. - */ - public static final int CAP_SQUARE = 2; - // 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; --- 26,56 ---- 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 { private static final int MOVE_TO = 0; private static final int DRAWING_OP_TO = 1; // ie. curve, line, or quad private static final int CLOSE = 2; // 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>. --- 93,132 ---- 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); --- 138,216 ---- * <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; + } + // add a small rounding error: + margin += 1e-3f; + + // 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; + this.cOutCode = 0; + this.sOutCode = 0; + } 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 --- 477,628 ---- 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; } ! // basic acceptance criteria ! if ((sOutCode & cOutCode) == 0) { ! if (cx0 != sx0 || cy0 != sy0) { ! lineTo(sx0, sy0, true); ! } ! drawJoin(cdx, cdy, cx0, cy0, sdx, sdy, cmx, cmy, smx, smy, sOutCode); ! 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); --- 631,673 ---- // 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; } --- 735,766 ---- 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]; --- 1061,1102 ---- 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 ((outcode0 & 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 **** --- 1118,1131 ---- 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) { --- 1140,1150 ---- 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. --- 1175,1236 ---- 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 ((outcode0 & 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) { --- 1244,1254 ---- 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 - // adjacent ones. - 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; - - // per-thread renderer context - 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) { - rdrCtx.stats.stat_rdr_poly_stack_types.add(curveTypesUseMark); - rdrCtx.stats.stat_rdr_poly_stack_curves.add(curvesUseMark); - rdrCtx.stats.hist_rdr_poly_stack_curves.add(curvesUseMark); - - // 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 - .add(end + n); - } - curves = curves_ref.widenArray(curves, end, end + n); - } - if (curveTypes.length <= numCurves) { - if (DO_STATS) { - rdrCtx.stats.stat_array_stroker_polystack_curveTypes - .add(numCurves + 1); - } - 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); - curveTypes[numCurves++] = TYPE_QUADTO; - 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; - case TYPE_QUADTO: - e -= 4; - io.quadTo(_curves[e+0], _curves[e+1], - _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; - case TYPE_QUADTO: - len = 4; - ret += "quad: "; - 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; - } - } } --- 1279,1296 ---- 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 >