< prev index next >
src/java.desktop/share/classes/sun/java2d/marlin/Stroker.java
Print this page
*** 1,7 ****
/*
! * Copyright (c) 2007, 2017, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
--- 1,7 ----
/*
! * Copyright (c) 2007, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
*** 27,59 ****
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;
private int capStyle;
private int joinStyle;
--- 27,58 ----
import java.util.Arrays;
import sun.awt.geom.PathConsumer2D;
import sun.java2d.marlin.Helpers.PolyStack;
+ import sun.java2d.marlin.TransformingPathConsumer2D.CurveBasicMonotonizer;
+ import sun.java2d.marlin.TransformingPathConsumer2D.CurveClipSplitter;
// 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;
! // round join threshold = 1 subpixel
! private static final float ERR_JOIN = (1.0f / MIN_SUBPIXELS);
! private static final float ROUND_JOIN_THRESHOLD = ERR_JOIN * ERR_JOIN;
// 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 PathConsumer2D out;
private int capStyle;
private int joinStyle;
*** 80,95 ****
// would be error prone and hard to read, so we keep these anyway.
private float smx, smy, cmx, cmy;
private final PolyStack reverse;
- // This is where the curve to be processed is put. We give it
- // enough room to store all curves.
- private final float[] middle = new float[MAX_N_CURVES * 6 + 2];
private final float[] lp = new float[8];
private final float[] rp = new float[8];
- private final float[] subdivTs = new float[MAX_N_CURVES - 1];
// per-thread renderer context
final RendererContext rdrCtx;
// dirty curve
--- 79,90 ----
*** 106,115 ****
--- 101,115 ----
// 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;
+ // flag indicating to monotonize curves
+ private boolean monotonize;
+
+ private boolean subdivide = DO_CLIP_SUBDIVIDER;
+ private final CurveClipSplitter curveSplitter;
/**
* Constructs a <code>Stroker</code>.
* @param rdrCtx per-thread renderer context
*/
*** 124,133 ****
--- 124,134 ----
rdrCtx.stats.stat_array_str_polystack_curves,
rdrCtx.stats.stat_array_str_polystack_types)
: new PolyStack(rdrCtx);
this.curve = rdrCtx.curve;
+ this.curveSplitter = rdrCtx.curveClipSplitter;
}
/**
* Inits the <code>Stroker</code>.
*
*** 139,161 ****
* @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;
--- 140,166 ----
* @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
+ * @param subdivideCurves true to indicate to subdivide curves, false if dasher does
* @return this instance
*/
Stroker init(final PathConsumer2D pc2d,
final float lineWidth,
final int capStyle,
final int joinStyle,
final float miterLimit,
! final float scale,
! final boolean subdivideCurves)
{
this.out = pc2d;
this.lineWidth2 = lineWidth / 2.0f;
this.invHalfLineWidth2Sq = 1.0f / (2.0f * lineWidth2 * lineWidth2);
+ this.monotonize = subdivideCurves;
+
this.capStyle = capStyle;
this.joinStyle = joinStyle;
final float limit = miterLimit * lineWidth2;
this.miterLimitSq = limit * limit;
*** 189,206 ****
--- 194,226 ----
_clipRect[0] -= margin - rdrOffY;
_clipRect[1] += margin + rdrOffY;
_clipRect[2] -= margin - rdrOffX;
_clipRect[3] += margin + rdrOffX;
this.clipRect = _clipRect;
+
+ // initialize curve splitter here for stroker & dasher:
+ if (DO_CLIP_SUBDIVIDER) {
+ subdivide = subdivideCurves;
+ // adjust padded clip rectangle:
+ curveSplitter.init();
+ } else {
+ subdivide = false;
+ }
} else {
this.clipRect = null;
this.cOutCode = 0;
this.sOutCode = 0;
}
return this; // fluent API
}
+ void disableClipping() {
+ this.clipRect = null;
+ this.cOutCode = 0;
+ this.sOutCode = 0;
+ }
+
/**
* Disposes this stroker:
* clean up before reusing this instance
*/
void dispose() {
*** 213,226 ****
// Force zero-fill dirty arrays:
Arrays.fill(offset0, 0.0f);
Arrays.fill(offset1, 0.0f);
Arrays.fill(offset2, 0.0f);
Arrays.fill(miter, 0.0f);
- Arrays.fill(middle, 0.0f);
Arrays.fill(lp, 0.0f);
Arrays.fill(rp, 0.0f);
- Arrays.fill(subdivTs, 0.0f);
}
}
private static void computeOffset(final float lx, final float ly,
final float w, final float[] m)
--- 233,244 ----
*** 248,280 ****
final float dx2, final float dy2)
{
return dx1 * dy2 <= dy1 * dx2;
}
! private void drawRoundJoin(float x, float y,
! float omx, float omy, float mx, float my,
! boolean rev,
! float threshold)
{
if ((omx == 0.0f && omy == 0.0f) || (mx == 0.0f && my == 0.0f)) {
return;
}
! float domx = omx - mx;
! float domy = omy - my;
! float len = domx*domx + domy*domy;
! if (len < threshold) {
return;
}
if (rev) {
omx = -omx;
omy = -omy;
mx = -mx;
my = -my;
}
! drawRoundJoin(x, y, omx, omy, mx, my, rev);
}
private void drawRoundJoin(float cx, float cy,
float omx, float omy,
float mx, float my,
--- 266,299 ----
final float dx2, final float dy2)
{
return dx1 * dy2 <= dy1 * dx2;
}
! private void mayDrawRoundJoin(float cx, float cy,
! float omx, float omy,
! float mx, float my,
! boolean rev)
{
if ((omx == 0.0f && omy == 0.0f) || (mx == 0.0f && my == 0.0f)) {
return;
}
! final float domx = omx - mx;
! final float domy = omy - my;
! final float lenSq = domx*domx + domy*domy;
!
! if (lenSq < ROUND_JOIN_THRESHOLD) {
return;
}
if (rev) {
omx = -omx;
omy = -omy;
mx = -mx;
my = -my;
}
! drawRoundJoin(cx, cy, omx, omy, mx, my, rev);
}
private void drawRoundJoin(float cx, float cy,
float omx, float omy,
float mx, float my,
*** 381,391 ****
// and (x0p, y0p) -> (x1p, y1p) in m[off] and m[off+1]
private static void computeMiter(final float x0, final float y0,
final float x1, final float y1,
final float x0p, final float y0p,
final float x1p, final float y1p,
! final float[] m, int off)
{
float x10 = x1 - x0;
float y10 = y1 - y0;
float x10p = x1p - x0p;
float y10p = y1p - y0p;
--- 400,410 ----
// and (x0p, y0p) -> (x1p, y1p) in m[off] and m[off+1]
private static void computeMiter(final float x0, final float y0,
final float x1, final float y1,
final float x0p, final float y0p,
final float x1p, final float y1p,
! final float[] m)
{
float x10 = x1 - x0;
float y10 = y1 - y0;
float x10p = x1p - x0p;
float y10p = y1p - y0p;
*** 400,420 ****
// (mx == omx && my == omy) will be true, and drawMiter will return
// immediately).
float den = x10*y10p - x10p*y10;
float t = x10p*(y0-y0p) - y10p*(x0-x0p);
t /= den;
! m[off++] = x0 + t*x10;
! m[off] = y0 + t*y10;
}
// Return the intersection point of the lines (x0, y0) -> (x1, y1)
// and (x0p, y0p) -> (x1p, y1p) in m[off] and m[off+1]
private static void safeComputeMiter(final float x0, final float y0,
final float x1, final float y1,
final float x0p, final float y0p,
final float x1p, final float y1p,
! final float[] m, int off)
{
float x10 = x1 - x0;
float y10 = y1 - y0;
float x10p = x1p - x0p;
float y10p = y1p - y0p;
--- 419,439 ----
// (mx == omx && my == omy) will be true, and drawMiter will return
// immediately).
float den = x10*y10p - x10p*y10;
float t = x10p*(y0-y0p) - y10p*(x0-x0p);
t /= den;
! m[0] = x0 + t*x10;
! m[1] = y0 + t*y10;
}
// Return the intersection point of the lines (x0, y0) -> (x1, y1)
// and (x0p, y0p) -> (x1p, y1p) in m[off] and m[off+1]
private static void safeComputeMiter(final float x0, final float y0,
final float x1, final float y1,
final float x0p, final float y0p,
final float x1p, final float y1p,
! final float[] m)
{
float x10 = x1 - x0;
float y10 = y1 - y0;
float x10p = x1p - x0p;
float y10p = y1p - y0p;
*** 428,451 ****
// miter drawing because it won't be called by drawMiter (because
// (mx == omx && my == omy) will be true, and drawMiter will return
// immediately).
float den = x10*y10p - x10p*y10;
if (den == 0.0f) {
! m[off++] = (x0 + x0p) / 2.0f;
! m[off] = (y0 + y0p) / 2.0f;
! return;
}
- float t = x10p*(y0-y0p) - y10p*(x0-x0p);
- t /= den;
- m[off++] = x0 + t*x10;
- m[off] = y0 + t*y10;
}
private void drawMiter(final float pdx, final float pdy,
final float x0, final float y0,
final float dx, final float dy,
! float omx, float omy, float mx, float my,
boolean rev)
{
if ((mx == omx && my == omy) ||
(pdx == 0.0f && pdy == 0.0f) ||
(dx == 0.0f && dy == 0.0f))
--- 447,471 ----
// miter drawing because it won't be called by drawMiter (because
// (mx == omx && my == omy) will be true, and drawMiter will return
// immediately).
float den = x10*y10p - x10p*y10;
if (den == 0.0f) {
! m[2] = (x0 + x0p) / 2.0f;
! m[3] = (y0 + y0p) / 2.0f;
! } else {
! float t = x10p*(y0-y0p) - y10p*(x0-x0p);
! t /= den;
! m[2] = x0 + t*x10;
! m[3] = y0 + t*y10;
}
}
private void drawMiter(final float pdx, final float pdy,
final float x0, final float y0,
final float dx, final float dy,
! float omx, float omy,
! float mx, float my,
boolean rev)
{
if ((mx == omx && my == omy) ||
(pdx == 0.0f && pdy == 0.0f) ||
(dx == 0.0f && dy == 0.0f))
*** 459,470 ****
mx = -mx;
my = -my;
}
computeMiter((x0 - pdx) + omx, (y0 - pdy) + omy, x0 + omx, y0 + omy,
! (dx + x0) + mx, (dy + y0) + my, x0 + mx, y0 + my,
! miter, 0);
final float miterX = miter[0];
final float miterY = miter[1];
float lenSq = (miterX-x0)*(miterX-x0) + (miterY-y0)*(miterY-y0);
--- 479,489 ----
mx = -mx;
my = -my;
}
computeMiter((x0 - pdx) + omx, (y0 - pdy) + omy, x0 + omx, y0 + omy,
! (dx + x0) + mx, (dy + y0) + my, x0 + mx, y0 + my, miter);
final float miterX = miter[0];
final float miterY = miter[1];
float lenSq = (miterX-x0)*(miterX-x0) + (miterY-y0)*(miterY-y0);
*** 478,488 ****
}
}
@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;
--- 497,507 ----
}
}
@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;
*** 494,504 ****
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;
--- 513,523 ----
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;
*** 521,540 ****
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) {
--- 540,583 ----
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);
! // Should clip
! final int orCode = (outcode0 | outcode1);
! if (orCode != 0) {
! final int sideCode = outcode0 & outcode1;
!
! // basic rejection criteria:
! if (sideCode == 0) {
! // ovelap clip:
! if (subdivide) {
! // avoid reentrance
! subdivide = false;
! // subdivide curve => callback with subdivided parts:
! boolean ret = curveSplitter.splitLine(cx0, cy0, x1, y1,
! orCode, this);
! // reentrance is done:
! subdivide = true;
! if (ret) {
! return;
! }
! }
! // already subdivided so render it
! } else {
! this.cOutCode = outcode1;
! _moveTo(x1, y1, outcode0);
! opened = true;
! return;
! }
}
+
+ this.cOutCode = outcode1;
}
float dx = x1 - cx0;
float dy = y1 - cy0;
if (dx == 0.0f && dy == 0.0f) {
*** 752,810 ****
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;
}
private static boolean within(final float x1, final float y1,
final float x2, final float y2,
! final float ERR)
{
! assert ERR > 0 : "";
// compare taxicab distance. ERR will always be small, so using
// true distance won't give much benefit
! return (Helpers.within(x1, x2, ERR) && // we want to avoid calling Math.abs
! Helpers.within(y1, y2, ERR)); // this is just as good.
}
! private void getLineOffsets(float x1, float y1,
! float x2, float y2,
! float[] left, float[] right) {
computeOffset(x2 - x1, y2 - y1, lineWidth2, offset0);
final float mx = offset0[0];
final float my = offset0[1];
left[0] = x1 + mx;
left[1] = y1 + my;
left[2] = x2 + mx;
left[3] = y2 + my;
right[0] = x1 - mx;
right[1] = y1 - my;
right[2] = x2 - mx;
right[3] = y2 - my;
}
! private int computeOffsetCubic(float[] pts, final int off,
! float[] leftOff, float[] rightOff)
{
// if p1=p2 or p3=p4 it means that the derivative at the endpoint
// vanishes, which creates problems with computeOffset. Usually
// this happens when this stroker object is trying to widen
// a curve with a cusp. What happens is that curveTo splits
// the input curve at the cusp, and passes it to this function.
// because of inaccuracies in the splitting, we consider points
// equal if they're very close to each other.
! final float x1 = pts[off + 0], y1 = pts[off + 1];
final float x2 = pts[off + 2], y2 = pts[off + 3];
final float x3 = pts[off + 4], y3 = pts[off + 5];
final float x4 = pts[off + 6], y4 = pts[off + 7];
float dx4 = x4 - x3;
--- 795,853 ----
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) {
! mayDrawRoundJoin(x0, y0, omx, omy, mx, my, cw);
}
}
emitLineTo(x0, y0, !cw);
}
prev = DRAWING_OP_TO;
}
private static boolean within(final float x1, final float y1,
final float x2, final float y2,
! final float err)
{
! assert err > 0 : "";
// compare taxicab distance. ERR will always be small, so using
// true distance won't give much benefit
! return (Helpers.within(x1, x2, err) && // we want to avoid calling Math.abs
! Helpers.within(y1, y2, err)); // this is just as good.
}
! private void getLineOffsets(final float x1, final float y1,
! final float x2, final float y2,
! final float[] left, final float[] right)
! {
computeOffset(x2 - x1, y2 - y1, lineWidth2, offset0);
final float mx = offset0[0];
final float my = offset0[1];
left[0] = x1 + mx;
left[1] = y1 + my;
left[2] = x2 + mx;
left[3] = y2 + my;
+
right[0] = x1 - mx;
right[1] = y1 - my;
right[2] = x2 - mx;
right[3] = y2 - my;
}
! private int computeOffsetCubic(final float[] pts, final int off,
! final float[] leftOff,
! final float[] rightOff)
{
// if p1=p2 or p3=p4 it means that the derivative at the endpoint
// vanishes, which creates problems with computeOffset. Usually
// this happens when this stroker object is trying to widen
// a curve with a cusp. What happens is that curveTo splits
// the input curve at the cusp, and passes it to this function.
// because of inaccuracies in the splitting, we consider points
// equal if they're very close to each other.
! final float x1 = pts[off ], y1 = pts[off + 1];
final float x2 = pts[off + 2], y2 = pts[off + 3];
final float x3 = pts[off + 4], y3 = pts[off + 5];
final float x4 = pts[off + 6], y4 = pts[off + 7];
float dx4 = x4 - x3;
*** 814,823 ****
--- 857,867 ----
// if p1 == p2 && p3 == p4: draw line from p1->p4, unless p1 == p4,
// in which case ignore if p1 == p2
final boolean p1eqp2 = within(x1, y1, x2, y2, 6.0f * Math.ulp(y2));
final boolean p3eqp4 = within(x3, y3, x4, y4, 6.0f * Math.ulp(y4));
+
if (p1eqp2 && p3eqp4) {
getLineOffsets(x1, y1, x4, y4, leftOff, rightOff);
return 4;
} else if (p1eqp2) {
dx1 = x3 - x1;
*** 829,838 ****
--- 873,883 ----
// if p2-p1 and p4-p3 are parallel, that must mean this curve is a line
float dotsq = (dx1 * dx4 + dy1 * dy4);
dotsq *= dotsq;
float l1sq = dx1 * dx1 + dy1 * dy1, l4sq = dx4 * dx4 + dy4 * dy4;
+
if (Helpers.within(dotsq, l1sq * l4sq, 4.0f * Math.ulp(dotsq))) {
getLineOffsets(x1, y1, x4, y4, leftOff, rightOff);
return 4;
}
*** 942,955 ****
}
// compute offset curves using bezier spline through t=0.5 (i.e.
// ComputedCurve(0.5) == IdealParallelCurve(0.5))
// return the kind of curve in the right and left arrays.
! private int computeOffsetQuad(float[] pts, final int off,
! float[] leftOff, float[] rightOff)
{
! final float x1 = pts[off + 0], y1 = pts[off + 1];
final float x2 = pts[off + 2], y2 = pts[off + 3];
final float x3 = pts[off + 4], y3 = pts[off + 5];
final float dx3 = x3 - x2;
final float dy3 = y3 - y2;
--- 987,1001 ----
}
// compute offset curves using bezier spline through t=0.5 (i.e.
// ComputedCurve(0.5) == IdealParallelCurve(0.5))
// return the kind of curve in the right and left arrays.
! private int computeOffsetQuad(final float[] pts, final int off,
! final float[] leftOff,
! final float[] rightOff)
{
! final float x1 = pts[off ], y1 = pts[off + 1];
final float x2 = pts[off + 2], y2 = pts[off + 3];
final float x3 = pts[off + 4], y3 = pts[off + 5];
final float dx3 = x3 - x2;
final float dy3 = y3 - y2;
*** 966,984 ****
--- 1012,1032 ----
// if p1 == p2 && p3 == p4: draw line from p1->p4, unless p1 == p4,
// in which case ignore.
final boolean p1eqp2 = within(x1, y1, x2, y2, 6.0f * Math.ulp(y2));
final boolean p2eqp3 = within(x2, y2, x3, y3, 6.0f * Math.ulp(y3));
+
if (p1eqp2 || p2eqp3) {
getLineOffsets(x1, y1, x3, y3, leftOff, rightOff);
return 4;
}
// if p2-p1 and p4-p3 are parallel, that must mean this curve is a line
float dotsq = (dx1 * dx3 + dy1 * dy3);
dotsq *= dotsq;
float l1sq = dx1 * dx1 + dy1 * dy1, l3sq = dx3 * dx3 + dy3 * dy3;
+
if (Helpers.within(dotsq, l1sq * l3sq, 4.0f * Math.ulp(dotsq))) {
getLineOffsets(x1, y1, x3, y3, leftOff, rightOff);
return 4;
}
*** 990,1162 ****
float x1p = x1 + offset0[0]; // start
float y1p = y1 + offset0[1]; // point
float x3p = x3 + offset1[0]; // end
float y3p = y3 + offset1[1]; // point
! safeComputeMiter(x1p, y1p, x1p+dx1, y1p+dy1, x3p, y3p, x3p-dx3, y3p-dy3, leftOff, 2);
leftOff[0] = x1p; leftOff[1] = y1p;
leftOff[4] = x3p; leftOff[5] = y3p;
x1p = x1 - offset0[0]; y1p = y1 - offset0[1];
x3p = x3 - offset1[0]; y3p = y3 - offset1[1];
! safeComputeMiter(x1p, y1p, x1p+dx1, y1p+dy1, x3p, y3p, x3p-dx3, y3p-dy3, rightOff, 2);
rightOff[0] = x1p; rightOff[1] = y1p;
rightOff[4] = x3p; rightOff[5] = y3p;
return 6;
}
- // finds values of t where the curve in pts should be subdivided in order
- // to get good offset curves a distance of w away from the middle curve.
- // Stores the points in ts, and returns how many of them there were.
- private static int findSubdivPoints(final Curve c, float[] pts, float[] ts,
- final int type, final float w)
- {
- final float x12 = pts[2] - pts[0];
- final float y12 = pts[3] - pts[1];
- // if the curve is already parallel to either axis we gain nothing
- // from rotating it.
- if (y12 != 0.0f && x12 != 0.0f) {
- // we rotate it so that the first vector in the control polygon is
- // parallel to the x-axis. This will ensure that rotated quarter
- // circles won't be subdivided.
- final float hypot = (float) Math.sqrt(x12 * x12 + y12 * y12);
- final float cos = x12 / hypot;
- final float sin = y12 / hypot;
- final float x1 = cos * pts[0] + sin * pts[1];
- final float y1 = cos * pts[1] - sin * pts[0];
- final float x2 = cos * pts[2] + sin * pts[3];
- final float y2 = cos * pts[3] - sin * pts[2];
- final float x3 = cos * pts[4] + sin * pts[5];
- final float y3 = cos * pts[5] - sin * pts[4];
-
- switch(type) {
- case 8:
- final float x4 = cos * pts[6] + sin * pts[7];
- final float y4 = cos * pts[7] - sin * pts[6];
- c.set(x1, y1, x2, y2, x3, y3, x4, y4);
- break;
- case 6:
- c.set(x1, y1, x2, y2, x3, y3);
- break;
- default:
- }
- } else {
- c.set(pts, type);
- }
-
- int ret = 0;
- // we subdivide at values of t such that the remaining rotated
- // curves are monotonic in x and y.
- ret += c.dxRoots(ts, ret);
- ret += c.dyRoots(ts, ret);
- // subdivide at inflection points.
- if (type == 8) {
- // quadratic curves can't have inflection points
- ret += c.infPoints(ts, ret);
- }
-
- // now we must subdivide at points where one of the offset curves will have
- // a cusp. This happens at ts where the radius of curvature is equal to w.
- ret += c.rootsOfROCMinusW(ts, ret, w, 0.0001f);
-
- 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];
!
! boolean p1eqp2 = (dxs == 0.0f && dys == 0.0f);
! boolean p3eqp4 = (dxf == 0.0f && dyf == 0.0f);
! if (p1eqp2) {
! dxs = mid[4] - mid[0];
! dys = mid[5] - mid[1];
! if (dxs == 0.0f && dys == 0.0f) {
! dxs = mid[6] - mid[0];
! dys = mid[7] - mid[1];
! }
! }
! if (p3eqp4) {
! dxf = mid[6] - mid[2];
! dyf = mid[7] - mid[3];
! if (dxf == 0.0f && dyf == 0.0f) {
! dxf = mid[6] - mid[0];
! 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
// precision problems.
if (Math.abs(dxs) < 0.1f && Math.abs(dys) < 0.1f) {
! float len = (float) Math.sqrt(dxs*dxs + dys*dys);
dxs /= len;
dys /= len;
}
if (Math.abs(dxf) < 0.1f && Math.abs(dyf) < 0.1f) {
! float len = (float) Math.sqrt(dxf*dxf + dyf*dyf);
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) {
! final float t = subdivTs[i];
! Helpers.subdivideCubicAt((t - prevT) / (1.0f - prevT),
! mid, off, mid, off, mid, off + 6);
! prevT = t;
! }
! final float[] l = lp;
final float[] r = rp;
int kind = 0;
for (int i = 0, off = 0; i <= nSplits; i++, off += 6) {
kind = computeOffsetCubic(mid, off, l, r);
--- 1038,1178 ----
float x1p = x1 + offset0[0]; // start
float y1p = y1 + offset0[1]; // point
float x3p = x3 + offset1[0]; // end
float y3p = y3 + offset1[1]; // point
! safeComputeMiter(x1p, y1p, x1p+dx1, y1p+dy1, x3p, y3p, x3p-dx3, y3p-dy3, leftOff);
leftOff[0] = x1p; leftOff[1] = y1p;
leftOff[4] = x3p; leftOff[5] = y3p;
x1p = x1 - offset0[0]; y1p = y1 - offset0[1];
x3p = x3 - offset1[0]; y3p = y3 - offset1[1];
! safeComputeMiter(x1p, y1p, x1p+dx1, y1p+dy1, x3p, y3p, x3p-dx3, y3p-dy3, rightOff);
rightOff[0] = x1p; rightOff[1] = y1p;
rightOff[4] = x3p; rightOff[5] = y3p;
return 6;
}
@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 outcode1 = Helpers.outcode(x1, y1, clipRect);
+ final int outcode2 = Helpers.outcode(x2, y2, clipRect);
final int outcode3 = Helpers.outcode(x3, y3, clipRect);
! // Should clip
! final int orCode = (outcode0 | outcode1 | outcode2 | outcode3);
! if (orCode != 0) {
! final int sideCode = outcode0 & outcode1 & outcode2 & outcode3;
!
! // basic rejection criteria:
! if (sideCode == 0) {
! // ovelap clip:
! if (subdivide) {
! // avoid reentrance
! subdivide = false;
! // subdivide curve => callback with subdivided parts:
! boolean ret = curveSplitter.splitCurve(cx0, cy0, x1, y1,
! x2, y2, x3, y3,
! orCode, this);
! // reentrance is done:
! subdivide = true;
! if (ret) {
! return;
! }
! }
! // already subdivided so render it
! } else {
! this.cOutCode = outcode3;
! _moveTo(x3, y3, outcode0);
opened = true;
return;
}
}
! this.cOutCode = outcode3;
! }
! _curveTo(x1, y1, x2, y2, x3, y3, outcode0);
! }
+ private void _curveTo(final float x1, final float y1,
+ final float x2, final float y2,
+ final float x3, final float y3,
+ final int outcode0)
+ {
// need these so we can update the state at the end of this method
! float dxs = x1 - cx0;
! float dys = y1 - cy0;
! float dxf = x3 - x2;
! float dyf = y3 - y2;
!
! if ((dxs == 0.0f) && (dys == 0.0f)) {
! dxs = x2 - cx0;
! dys = y2 - cy0;
! if ((dxs == 0.0f) && (dys == 0.0f)) {
! dxs = x3 - cx0;
! dys = y3 - cy0;
! }
! }
! if ((dxf == 0.0f) && (dyf == 0.0f)) {
! dxf = x3 - x1;
! dyf = y3 - y1;
! if ((dxf == 0.0f) && (dyf == 0.0f)) {
! dxf = x3 - cx0;
! dyf = y3 - cy0;
}
}
! 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(cx0, cy0);
return;
}
// if these vectors are too small, normalize them, to avoid future
// precision problems.
if (Math.abs(dxs) < 0.1f && Math.abs(dys) < 0.1f) {
! final float len = (float)Math.sqrt(dxs * dxs + dys * dys);
dxs /= len;
dys /= len;
}
if (Math.abs(dxf) < 0.1f && Math.abs(dyf) < 0.1f) {
! final float len = (float)Math.sqrt(dxf * dxf + dyf * dyf);
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 = 0;
! final float[] mid;
! final float[] l = lp;
! if (monotonize) {
! // monotonize curve:
! final CurveBasicMonotonizer monotonizer
! = rdrCtx.monotonizer.curve(cx0, cy0, x1, y1, x2, y2, x3, y3);
! nSplits = monotonizer.nbSplits;
! mid = monotonizer.middle;
! } else {
! // use left instead:
! mid = l;
! mid[0] = cx0; mid[1] = cy0;
! mid[2] = x1; mid[3] = y1;
! mid[4] = x2; mid[5] = y2;
! mid[6] = x3; mid[7] = y3;
! }
final float[] r = rp;
int kind = 0;
for (int i = 0, off = 0; i <= nSplits; i++, off += 6) {
kind = computeOffsetCubic(mid, off, l, r);
*** 1176,1187 ****
}
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;
}
--- 1192,1203 ----
}
emitLineToRev(r[kind - 2], r[kind - 1]);
}
this.prev = DRAWING_OP_TO;
! this.cx0 = x3;
! this.cy0 = y3;
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;
}
*** 1189,1266 ****
@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.
if (Math.abs(dxs) < 0.1f && Math.abs(dys) < 0.1f) {
! float len = (float) Math.sqrt(dxs*dxs + dys*dys);
dxs /= len;
dys /= len;
}
if (Math.abs(dxf) < 0.1f && Math.abs(dyf) < 0.1f) {
! float len = (float) Math.sqrt(dxf*dxf + dyf*dyf);
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) {
! final float t = subdivTs[i];
! Helpers.subdivideQuadAt((t - prevt) / (1.0f - prevt),
! mid, off, mid, off, mid, off + 4);
! prevt = t;
! }
! final float[] l = lp;
final float[] r = rp;
int kind = 0;
for (int i = 0, off = 0; i <= nSplits; i++, off += 4) {
kind = computeOffsetQuad(mid, off, l, r);
--- 1205,1309 ----
@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 outcode1 = Helpers.outcode(x1, y1, clipRect);
final int outcode2 = Helpers.outcode(x2, y2, clipRect);
! // Should clip
! final int orCode = (outcode0 | outcode1 | outcode2);
! if (orCode != 0) {
! final int sideCode = outcode0 & outcode1 & outcode2;
!
! // basic rejection criteria:
! if (sideCode == 0) {
! // ovelap clip:
! if (subdivide) {
! // avoid reentrance
! subdivide = false;
! // subdivide curve => call lineTo() with subdivided curves:
! boolean ret = curveSplitter.splitQuad(cx0, cy0, x1, y1,
! x2, y2, orCode, this);
! // reentrance is done:
! subdivide = true;
! if (ret) {
! return;
! }
! }
! // already subdivided so render it
! } else {
! this.cOutCode = outcode2;
! _moveTo(x2, y2, outcode0);
opened = true;
return;
}
}
! this.cOutCode = outcode2;
! }
! _quadTo(x1, y1, x2, y2, outcode0);
! }
+ private void _quadTo(final float x1, final float y1,
+ final float x2, final float y2,
+ final int outcode0)
+ {
// need these so we can update the state at the end of this method
! float dxs = x1 - cx0;
! float dys = y1 - cy0;
! float dxf = x2 - x1;
! float dyf = y2 - y1;
!
! if (((dxs == 0.0f) && (dys == 0.0f)) || ((dxf == 0.0f) && (dyf == 0.0f))) {
! dxs = dxf = x2 - cx0;
! dys = dyf = y2 - cy0;
}
! 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(cx0, cy0);
return;
}
// if these vectors are too small, normalize them, to avoid future
// precision problems.
if (Math.abs(dxs) < 0.1f && Math.abs(dys) < 0.1f) {
! final float len = (float)Math.sqrt(dxs * dxs + dys * dys);
dxs /= len;
dys /= len;
}
if (Math.abs(dxf) < 0.1f && Math.abs(dyf) < 0.1f) {
! final float len = (float)Math.sqrt(dxf * dxf + dyf * dyf);
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 = 0;
! final float[] mid;
! final float[] l = lp;
! if (monotonize) {
! // monotonize quad:
! final CurveBasicMonotonizer monotonizer
! = rdrCtx.monotonizer.quad(cx0, cy0, x1, y1, x2, y2);
! nSplits = monotonizer.nbSplits;
! mid = monotonizer.middle;
! } else {
! // use left instead:
! mid = l;
! mid[0] = cx0; mid[1] = cy0;
! mid[2] = x1; mid[3] = y1;
! mid[4] = x2; mid[5] = y2;
! }
final float[] r = rp;
int kind = 0;
for (int i = 0, off = 0; i <= nSplits; i++, off += 4) {
kind = computeOffsetQuad(mid, off, l, r);
*** 1280,1291 ****
}
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;
}
--- 1323,1334 ----
}
emitLineToRev(r[kind - 2], r[kind - 1]);
}
this.prev = DRAWING_OP_TO;
! this.cx0 = x2;
! this.cy0 = y2;
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;
}
< prev index next >