Merge
This commit is contained in:
commit
b3324bf23d
294
jdk/src/share/classes/sun/java2d/pisces/Curve.java
Normal file
294
jdk/src/share/classes/sun/java2d/pisces/Curve.java
Normal file
@ -0,0 +1,294 @@
|
||||
/*
|
||||
* Copyright (c) 2007, 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
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.java2d.pisces;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
class Curve {
|
||||
|
||||
float ax, ay, bx, by, cx, cy, dx, dy;
|
||||
float dax, day, dbx, dby;
|
||||
|
||||
Curve() {
|
||||
}
|
||||
|
||||
void set(float[] points, int type) {
|
||||
switch(type) {
|
||||
case 8:
|
||||
set(points[0], points[1],
|
||||
points[2], points[3],
|
||||
points[4], points[5],
|
||||
points[6], points[7]);
|
||||
break;
|
||||
case 6:
|
||||
set(points[0], points[1],
|
||||
points[2], points[3],
|
||||
points[4], points[5]);
|
||||
break;
|
||||
default:
|
||||
throw new InternalError("Curves can only be cubic or quadratic");
|
||||
}
|
||||
}
|
||||
|
||||
void set(float x1, float y1,
|
||||
float x2, float y2,
|
||||
float x3, float y3,
|
||||
float x4, float y4)
|
||||
{
|
||||
ax = 3 * (x2 - x3) + x4 - x1;
|
||||
ay = 3 * (y2 - y3) + y4 - y1;
|
||||
bx = 3 * (x1 - 2 * x2 + x3);
|
||||
by = 3 * (y1 - 2 * y2 + y3);
|
||||
cx = 3 * (x2 - x1);
|
||||
cy = 3 * (y2 - y1);
|
||||
dx = x1;
|
||||
dy = y1;
|
||||
dax = 3 * ax; day = 3 * ay;
|
||||
dbx = 2 * bx; dby = 2 * by;
|
||||
}
|
||||
|
||||
void set(float x1, float y1,
|
||||
float x2, float y2,
|
||||
float x3, float y3)
|
||||
{
|
||||
ax = ay = 0f;
|
||||
|
||||
bx = x1 - 2 * x2 + x3;
|
||||
by = y1 - 2 * y2 + y3;
|
||||
cx = 2 * (x2 - x1);
|
||||
cy = 2 * (y2 - y1);
|
||||
dx = x1;
|
||||
dy = y1;
|
||||
dax = 0; day = 0;
|
||||
dbx = 2 * bx; dby = 2 * by;
|
||||
}
|
||||
|
||||
float xat(float t) {
|
||||
return t * (t * (t * ax + bx) + cx) + dx;
|
||||
}
|
||||
float yat(float t) {
|
||||
return t * (t * (t * ay + by) + cy) + dy;
|
||||
}
|
||||
|
||||
float dxat(float t) {
|
||||
return t * (t * dax + dbx) + cx;
|
||||
}
|
||||
|
||||
float dyat(float t) {
|
||||
return t * (t * day + dby) + cy;
|
||||
}
|
||||
|
||||
private float ddxat(float t) {
|
||||
return 2 * dax * t + dbx;
|
||||
}
|
||||
|
||||
private float ddyat(float t) {
|
||||
return 2 * day * t + dby;
|
||||
}
|
||||
|
||||
int dxRoots(float[] roots, int off) {
|
||||
return Helpers.quadraticRoots(dax, dbx, cx, roots, off);
|
||||
}
|
||||
|
||||
int dyRoots(float[] roots, int off) {
|
||||
return Helpers.quadraticRoots(day, dby, cy, roots, off);
|
||||
}
|
||||
|
||||
int infPoints(float[] pts, int off) {
|
||||
// inflection point at t if -f'(t)x*f''(t)y + f'(t)y*f''(t)x == 0
|
||||
// Fortunately, this turns out to be quadratic, so there are at
|
||||
// most 2 inflection points.
|
||||
final float a = dax * dby - dbx * day;
|
||||
final float b = 2 * (cy * dax - day * cx);
|
||||
final float c = cy * dbx - cx * dby;
|
||||
|
||||
return Helpers.quadraticRoots(a, b, c, pts, off);
|
||||
}
|
||||
|
||||
// finds points where the first and second derivative are
|
||||
// perpendicular. This happens when g(t) = f'(t)*f''(t) == 0 (where
|
||||
// * is a dot product). Unfortunately, we have to solve a cubic.
|
||||
private int perpendiculardfddf(float[] pts, int off, final float err) {
|
||||
assert pts.length >= off + 4;
|
||||
|
||||
// these are the coefficients of g(t):
|
||||
final float a = 2*(dax*dax + day*day);
|
||||
final float b = 3*(dax*dbx + day*dby);
|
||||
final float c = 2*(dax*cx + day*cy) + dbx*dbx + dby*dby;
|
||||
final float d = dbx*cx + dby*cy;
|
||||
// TODO: We might want to divide the polynomial by a to make the
|
||||
// coefficients smaller. This won't change the roots.
|
||||
return Helpers.cubicRootsInAB(a, b, c, d, pts, off, err, 0f, 1f);
|
||||
}
|
||||
|
||||
// Tries to find the roots of the function ROC(t)-w in [0, 1). It uses
|
||||
// a variant of the false position algorithm to find the roots. False
|
||||
// position requires that 2 initial values x0,x1 be given, and that the
|
||||
// function must have opposite signs at those values. To find such
|
||||
// values, we need the local extrema of the ROC function, for which we
|
||||
// need the roots of its derivative; however, it's harder to find the
|
||||
// roots of the derivative in this case than it is to find the roots
|
||||
// of the original function. So, we find all points where this curve's
|
||||
// first and second derivative are perpendicular, and we pretend these
|
||||
// are our local extrema. There are at most 3 of these, so we will check
|
||||
// at most 4 sub-intervals of (0,1). ROC has asymptotes at inflection
|
||||
// points, so roc-w can have at least 6 roots. This shouldn't be a
|
||||
// problem for what we're trying to do (draw a nice looking curve).
|
||||
int rootsOfROCMinusW(float[] roots, int off, final float w, final float err) {
|
||||
// no OOB exception, because by now off<=6, and roots.length >= 10
|
||||
assert off <= 6 && roots.length >= 10;
|
||||
int ret = off;
|
||||
int numPerpdfddf = perpendiculardfddf(roots, off, err);
|
||||
float t0 = 0, ft0 = ROCsq(t0) - w*w;
|
||||
roots[off + numPerpdfddf] = 1f; // always check interval end points
|
||||
numPerpdfddf++;
|
||||
for (int i = off; i < off + numPerpdfddf; i++) {
|
||||
float t1 = roots[i], ft1 = ROCsq(t1) - w*w;
|
||||
if (ft0 == 0f) {
|
||||
roots[ret++] = t0;
|
||||
} else if (ft1 * ft0 < 0f) { // have opposite signs
|
||||
// (ROC(t)^2 == w^2) == (ROC(t) == w) is true because
|
||||
// ROC(t) >= 0 for all t.
|
||||
roots[ret++] = falsePositionROCsqMinusX(t0, t1, w*w, err);
|
||||
}
|
||||
t0 = t1;
|
||||
ft0 = ft1;
|
||||
}
|
||||
|
||||
return ret - off;
|
||||
}
|
||||
|
||||
private static float eliminateInf(float x) {
|
||||
return (x == Float.POSITIVE_INFINITY ? Float.MAX_VALUE :
|
||||
(x == Float.NEGATIVE_INFINITY ? Float.MIN_VALUE : x));
|
||||
}
|
||||
|
||||
// A slight modification of the false position algorithm on wikipedia.
|
||||
// This only works for the ROCsq-x functions. It might be nice to have
|
||||
// the function as an argument, but that would be awkward in java6.
|
||||
// It is something to consider for java7, depending on how closures
|
||||
// and function objects turn out. Same goes for the newton's method
|
||||
// algorithm in Helpers.java
|
||||
private float falsePositionROCsqMinusX(float x0, float x1,
|
||||
final float x, final float err)
|
||||
{
|
||||
final int iterLimit = 100;
|
||||
int side = 0;
|
||||
float t = x1, ft = eliminateInf(ROCsq(t) - x);
|
||||
float s = x0, fs = eliminateInf(ROCsq(s) - x);
|
||||
float r = s, fr;
|
||||
for (int i = 0; i < iterLimit && Math.abs(t - s) > err * Math.abs(t + s); i++) {
|
||||
r = (fs * t - ft * s) / (fs - ft);
|
||||
fr = ROCsq(r) - x;
|
||||
if (fr * ft > 0) {// have the same sign
|
||||
ft = fr; t = r;
|
||||
if (side < 0) {
|
||||
fs /= (1 << (-side));
|
||||
side--;
|
||||
} else {
|
||||
side = -1;
|
||||
}
|
||||
} else if (fr * fs > 0) {
|
||||
fs = fr; s = r;
|
||||
if (side > 0) {
|
||||
ft /= (1 << side);
|
||||
side++;
|
||||
} else {
|
||||
side = 1;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
// returns the radius of curvature squared at t of this curve
|
||||
// see http://en.wikipedia.org/wiki/Radius_of_curvature_(applications)
|
||||
private float ROCsq(final float t) {
|
||||
final float dx = dxat(t);
|
||||
final float dy = dyat(t);
|
||||
final float ddx = ddxat(t);
|
||||
final float ddy = ddyat(t);
|
||||
final float dx2dy2 = dx*dx + dy*dy;
|
||||
final float ddx2ddy2 = ddx*ddx + ddy*ddy;
|
||||
final float ddxdxddydy = ddx*dx + ddy*dy;
|
||||
float ret = ((dx2dy2*dx2dy2) / (dx2dy2 * ddx2ddy2 - ddxdxddydy*ddxdxddydy))*dx2dy2;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// curve to be broken should be in pts[0]
|
||||
// this will change the contents of both pts and Ts
|
||||
// TODO: There's no reason for Ts to be an array. All we need is a sequence
|
||||
// of t values at which to subdivide. An array statisfies this condition,
|
||||
// but is unnecessarily restrictive. Ts should be an Iterator<Float> instead.
|
||||
// Doing this will also make dashing easier, since we could easily make
|
||||
// LengthIterator an Iterator<Float> and feed it to this function to simplify
|
||||
// the loop in Dasher.somethingTo.
|
||||
static Iterator<float[]> breakPtsAtTs(final float[][] pts, final int type,
|
||||
final float[] Ts, final int numTs)
|
||||
{
|
||||
assert pts.length >= 2 && pts[0].length >= 8 && numTs <= Ts.length;
|
||||
return new Iterator<float[]>() {
|
||||
int nextIdx = 0;
|
||||
int nextCurveIdx = 0;
|
||||
float prevT = 0;
|
||||
|
||||
@Override public boolean hasNext() {
|
||||
return nextCurveIdx < numTs + 1;
|
||||
}
|
||||
|
||||
@Override public float[] next() {
|
||||
float[] ret;
|
||||
if (nextCurveIdx < numTs) {
|
||||
float curT = Ts[nextCurveIdx];
|
||||
float splitT = (curT - prevT) / (1 - prevT);
|
||||
Helpers.subdivideAt(splitT,
|
||||
pts[nextIdx], 0,
|
||||
pts[nextIdx], 0,
|
||||
pts[1-nextIdx], 0, type);
|
||||
updateTs(Ts, Ts[nextCurveIdx], nextCurveIdx + 1, numTs - nextCurveIdx - 1);
|
||||
ret = pts[nextIdx];
|
||||
nextIdx = 1 - nextIdx;
|
||||
} else {
|
||||
ret = pts[nextIdx];
|
||||
}
|
||||
nextCurveIdx++;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override public void remove() {}
|
||||
};
|
||||
}
|
||||
|
||||
// precondition: ts[off]...ts[off+len-1] must all be greater than t.
|
||||
private static void updateTs(float[] ts, final float t, final int off, final int len) {
|
||||
for (int i = off; i < off + len; i++) {
|
||||
ts[i] = (ts[i] - t) / (1 - t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,8 @@
|
||||
|
||||
package sun.java2d.pisces;
|
||||
|
||||
import sun.awt.geom.PathConsumer2D;
|
||||
|
||||
/**
|
||||
* The <code>Dasher</code> class takes a series of linear commands
|
||||
* (<code>moveTo</code>, <code>lineTo</code>, <code>close</code> and
|
||||
@ -36,18 +38,16 @@ package sun.java2d.pisces;
|
||||
* semantics are unclear.
|
||||
*
|
||||
*/
|
||||
public class Dasher implements LineSink {
|
||||
private final LineSink output;
|
||||
public class Dasher implements sun.awt.geom.PathConsumer2D {
|
||||
|
||||
private final PathConsumer2D out;
|
||||
private final float[] dash;
|
||||
private final float startPhase;
|
||||
private final boolean startDashOn;
|
||||
private final int startIdx;
|
||||
|
||||
private final float m00, m10, m01, m11;
|
||||
private final float det;
|
||||
|
||||
private boolean firstDashOn;
|
||||
private boolean starting;
|
||||
private boolean needsMoveTo;
|
||||
|
||||
private int idx;
|
||||
private boolean dashOn;
|
||||
@ -55,28 +55,23 @@ public class Dasher implements LineSink {
|
||||
|
||||
private float sx, sy;
|
||||
private float x0, y0;
|
||||
private float sx1, sy1;
|
||||
|
||||
// temporary storage for the current curve
|
||||
private float[] curCurvepts;
|
||||
|
||||
/**
|
||||
* Constructs a <code>Dasher</code>.
|
||||
*
|
||||
* @param output an output <code>LineSink</code>.
|
||||
* @param dash an array of <code>int</code>s containing the dash pattern
|
||||
* @param phase an <code>int</code> containing the dash phase
|
||||
* @param transform a <code>Transform4</code> object indicating
|
||||
* the transform that has been previously applied to all incoming
|
||||
* coordinates. This is required in order to compute dash lengths
|
||||
* properly.
|
||||
* @param out an output <code>PathConsumer2D</code>.
|
||||
* @param dash an array of <code>float</code>s containing the dash pattern
|
||||
* @param phase a <code>float</code> containing the dash phase
|
||||
*/
|
||||
public Dasher(LineSink output,
|
||||
float[] dash, float phase,
|
||||
float a00, float a01, float a10, float a11) {
|
||||
public Dasher(PathConsumer2D out, float[] dash, float phase) {
|
||||
if (phase < 0) {
|
||||
throw new IllegalArgumentException("phase < 0 !");
|
||||
}
|
||||
|
||||
this.output = output;
|
||||
this.out = out;
|
||||
|
||||
// Normalize so 0 <= phase < dash[0]
|
||||
int idx = 0;
|
||||
@ -92,16 +87,19 @@ public class Dasher implements LineSink {
|
||||
this.startPhase = this.phase = phase;
|
||||
this.startDashOn = dashOn;
|
||||
this.startIdx = idx;
|
||||
this.starting = true;
|
||||
|
||||
m00 = a00;
|
||||
m01 = a01;
|
||||
m10 = a10;
|
||||
m11 = a11;
|
||||
det = m00 * m11 - m01 * m10;
|
||||
// we need curCurvepts to be able to contain 2 curves because when
|
||||
// dashing curves, we need to subdivide it
|
||||
curCurvepts = new float[8 * 2];
|
||||
}
|
||||
|
||||
public void moveTo(float x0, float y0) {
|
||||
output.moveTo(x0, y0);
|
||||
if (firstSegidx > 0) {
|
||||
out.moveTo(sx, sy);
|
||||
emitFirstSegments();
|
||||
}
|
||||
needsMoveTo = true;
|
||||
this.idx = startIdx;
|
||||
this.dashOn = this.startDashOn;
|
||||
this.phase = this.startPhase;
|
||||
@ -110,88 +108,108 @@ public class Dasher implements LineSink {
|
||||
this.starting = true;
|
||||
}
|
||||
|
||||
public void lineJoin() {
|
||||
output.lineJoin();
|
||||
private void emitSeg(float[] buf, int off, int type) {
|
||||
switch (type) {
|
||||
case 8:
|
||||
out.curveTo(buf[off+0], buf[off+1],
|
||||
buf[off+2], buf[off+3],
|
||||
buf[off+4], buf[off+5]);
|
||||
break;
|
||||
case 6:
|
||||
out.quadTo(buf[off+0], buf[off+1],
|
||||
buf[off+2], buf[off+3]);
|
||||
break;
|
||||
case 4:
|
||||
out.lineTo(buf[off], buf[off+1]);
|
||||
}
|
||||
}
|
||||
|
||||
private void goTo(float x1, float y1) {
|
||||
private void emitFirstSegments() {
|
||||
for (int i = 0; i < firstSegidx; ) {
|
||||
emitSeg(firstSegmentsBuffer, i+1, (int)firstSegmentsBuffer[i]);
|
||||
i += (((int)firstSegmentsBuffer[i]) - 1);
|
||||
}
|
||||
firstSegidx = 0;
|
||||
}
|
||||
|
||||
// We don't emit the first dash right away. If we did, caps would be
|
||||
// drawn on it, but we need joins to be drawn if there's a closePath()
|
||||
// So, we store the path elements that make up the first dash in the
|
||||
// buffer below.
|
||||
private float[] firstSegmentsBuffer = new float[7];
|
||||
private int firstSegidx = 0;
|
||||
// precondition: pts must be in relative coordinates (relative to x0,y0)
|
||||
// fullCurve is true iff the curve in pts has not been split.
|
||||
private void goTo(float[] pts, int off, final int type) {
|
||||
float x = pts[off + type - 4];
|
||||
float y = pts[off + type - 3];
|
||||
if (dashOn) {
|
||||
if (starting) {
|
||||
this.sx1 = x1;
|
||||
this.sy1 = y1;
|
||||
firstDashOn = true;
|
||||
starting = false;
|
||||
firstSegmentsBuffer = Helpers.widenArray(firstSegmentsBuffer,
|
||||
firstSegidx, type - 2);
|
||||
firstSegmentsBuffer[firstSegidx++] = type;
|
||||
System.arraycopy(pts, off, firstSegmentsBuffer, firstSegidx, type - 2);
|
||||
firstSegidx += type - 2;
|
||||
} else {
|
||||
if (needsMoveTo) {
|
||||
out.moveTo(x0, y0);
|
||||
needsMoveTo = false;
|
||||
}
|
||||
emitSeg(pts, off, type);
|
||||
}
|
||||
output.lineTo(x1, y1);
|
||||
} else {
|
||||
if (starting) {
|
||||
firstDashOn = false;
|
||||
starting = false;
|
||||
}
|
||||
output.moveTo(x1, y1);
|
||||
starting = false;
|
||||
needsMoveTo = true;
|
||||
}
|
||||
this.x0 = x1;
|
||||
this.y0 = y1;
|
||||
this.x0 = x;
|
||||
this.y0 = y;
|
||||
}
|
||||
|
||||
public void lineTo(float x1, float y1) {
|
||||
// The widened line is squished to a 0 width one, so no drawing is done
|
||||
if (det == 0) {
|
||||
goTo(x1, y1);
|
||||
return;
|
||||
}
|
||||
float dx = x1 - x0;
|
||||
float dy = y1 - y0;
|
||||
|
||||
float len = (float) Math.hypot(dx, dy);
|
||||
|
||||
// Compute segment length in the untransformed
|
||||
// coordinate system
|
||||
|
||||
float la = (dy*m00 - dx*m10)/det;
|
||||
float lb = (dy*m01 - dx*m11)/det;
|
||||
float origLen = (float) Math.hypot(la, lb);
|
||||
|
||||
if (origLen == 0) {
|
||||
// Let the output LineSink deal with cases where dx, dy are 0.
|
||||
goTo(x1, y1);
|
||||
if (len == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The scaling factors needed to get the dx and dy of the
|
||||
// transformed dash segments.
|
||||
float cx = dx / origLen;
|
||||
float cy = dy / origLen;
|
||||
float cx = dx / len;
|
||||
float cy = dy / len;
|
||||
|
||||
while (true) {
|
||||
float leftInThisDashSegment = dash[idx] - phase;
|
||||
if (origLen < leftInThisDashSegment) {
|
||||
goTo(x1, y1);
|
||||
if (len <= leftInThisDashSegment) {
|
||||
curCurvepts[0] = x1;
|
||||
curCurvepts[1] = y1;
|
||||
goTo(curCurvepts, 0, 4);
|
||||
// Advance phase within current dash segment
|
||||
phase += origLen;
|
||||
return;
|
||||
} else if (origLen == leftInThisDashSegment) {
|
||||
goTo(x1, y1);
|
||||
phase = 0f;
|
||||
idx = (idx + 1) % dash.length;
|
||||
dashOn = !dashOn;
|
||||
phase += len;
|
||||
if (len == leftInThisDashSegment) {
|
||||
phase = 0f;
|
||||
idx = (idx + 1) % dash.length;
|
||||
dashOn = !dashOn;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
float dashx, dashy;
|
||||
float dashdx = dash[idx] * cx;
|
||||
float dashdy = dash[idx] * cy;
|
||||
if (phase == 0) {
|
||||
dashx = x0 + dashdx;
|
||||
dashy = y0 + dashdy;
|
||||
curCurvepts[0] = x0 + dashdx;
|
||||
curCurvepts[1] = y0 + dashdy;
|
||||
} else {
|
||||
float p = (leftInThisDashSegment) / dash[idx];
|
||||
dashx = x0 + p * dashdx;
|
||||
dashy = y0 + p * dashdy;
|
||||
float p = leftInThisDashSegment / dash[idx];
|
||||
curCurvepts[0] = x0 + p * dashdx;
|
||||
curCurvepts[1] = y0 + p * dashdy;
|
||||
}
|
||||
|
||||
goTo(dashx, dashy);
|
||||
goTo(curCurvepts, 0, 4);
|
||||
|
||||
origLen -= (dash[idx] - phase);
|
||||
len -= leftInThisDashSegment;
|
||||
// Advance to next dash segment
|
||||
idx = (idx + 1) % dash.length;
|
||||
dashOn = !dashOn;
|
||||
@ -199,15 +217,289 @@ public class Dasher implements LineSink {
|
||||
}
|
||||
}
|
||||
|
||||
private LengthIterator li = null;
|
||||
|
||||
public void close() {
|
||||
lineTo(sx, sy);
|
||||
if (firstDashOn) {
|
||||
output.lineTo(sx1, sy1);
|
||||
// preconditions: curCurvepts must be an array of length at least 2 * type,
|
||||
// that contains the curve we want to dash in the first type elements
|
||||
private void somethingTo(int type) {
|
||||
if (pointCurve(curCurvepts, type)) {
|
||||
return;
|
||||
}
|
||||
if (li == null) {
|
||||
li = new LengthIterator(4, 0.0001f);
|
||||
}
|
||||
li.initializeIterationOnCurve(curCurvepts, type);
|
||||
|
||||
int curCurveoff = 0; // initially the current curve is at curCurvepts[0...type]
|
||||
float lastSplitT = 0;
|
||||
float t = 0;
|
||||
float leftInThisDashSegment = dash[idx] - phase;
|
||||
while ((t = li.next(leftInThisDashSegment)) < 1) {
|
||||
if (t != 0) {
|
||||
Helpers.subdivideAt((t - lastSplitT) / (1 - lastSplitT),
|
||||
curCurvepts, curCurveoff,
|
||||
curCurvepts, 0,
|
||||
curCurvepts, type, type);
|
||||
lastSplitT = t;
|
||||
goTo(curCurvepts, 2, type);
|
||||
curCurveoff = type;
|
||||
}
|
||||
// Advance to next dash segment
|
||||
idx = (idx + 1) % dash.length;
|
||||
dashOn = !dashOn;
|
||||
phase = 0;
|
||||
leftInThisDashSegment = dash[idx];
|
||||
}
|
||||
goTo(curCurvepts, curCurveoff+2, type);
|
||||
phase += li.lastSegLen();
|
||||
if (phase >= dash[idx]) {
|
||||
phase = 0f;
|
||||
idx = (idx + 1) % dash.length;
|
||||
dashOn = !dashOn;
|
||||
}
|
||||
}
|
||||
|
||||
public void end() {
|
||||
output.end();
|
||||
private static boolean pointCurve(float[] curve, int type) {
|
||||
for (int i = 2; i < type; i++) {
|
||||
if (curve[i] != curve[i-2]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Objects of this class are used to iterate through curves. They return
|
||||
// t values where the left side of the curve has a specified length.
|
||||
// It does this by subdividing the input curve until a certain error
|
||||
// condition has been met. A recursive subdivision procedure would
|
||||
// return as many as 1<<limit curves, but this is an iterator and we
|
||||
// don't need all the curves all at once, so what we carry out a
|
||||
// lazy inorder traversal of the recursion tree (meaning we only move
|
||||
// through the tree when we need the next subdivided curve). This saves
|
||||
// us a lot of memory because at any one time we only need to store
|
||||
// limit+1 curves - one for each level of the tree + 1.
|
||||
// NOTE: the way we do things here is not enough to traverse a general
|
||||
// tree; however, the trees we are interested in have the property that
|
||||
// every non leaf node has exactly 2 children
|
||||
private static class LengthIterator {
|
||||
private enum Side {LEFT, RIGHT};
|
||||
// Holds the curves at various levels of the recursion. The root
|
||||
// (i.e. the original curve) is at recCurveStack[0] (but then it
|
||||
// gets subdivided, the left half is put at 1, so most of the time
|
||||
// only the right half of the original curve is at 0)
|
||||
private float[][] recCurveStack;
|
||||
// sides[i] indicates whether the node at level i+1 in the path from
|
||||
// the root to the current leaf is a left or right child of its parent.
|
||||
private Side[] sides;
|
||||
private int curveType;
|
||||
private final int limit;
|
||||
private final float ERR;
|
||||
private final float minTincrement;
|
||||
// lastT and nextT delimit the current leaf.
|
||||
private float nextT;
|
||||
private float lenAtNextT;
|
||||
private float lastT;
|
||||
private float lenAtLastT;
|
||||
private float lenAtLastSplit;
|
||||
private float lastSegLen;
|
||||
// the current level in the recursion tree. 0 is the root. limit
|
||||
// is the deepest possible leaf.
|
||||
private int recLevel;
|
||||
private boolean done;
|
||||
|
||||
public LengthIterator(int reclimit, float err) {
|
||||
this.limit = reclimit;
|
||||
this.minTincrement = 1f / (1 << limit);
|
||||
this.ERR = err;
|
||||
this.recCurveStack = new float[reclimit+1][8];
|
||||
this.sides = new Side[reclimit];
|
||||
// if any methods are called without first initializing this object on
|
||||
// a curve, we want it to fail ASAP.
|
||||
this.nextT = Float.MAX_VALUE;
|
||||
this.lenAtNextT = Float.MAX_VALUE;
|
||||
this.lenAtLastSplit = Float.MIN_VALUE;
|
||||
this.recLevel = Integer.MIN_VALUE;
|
||||
this.lastSegLen = Float.MAX_VALUE;
|
||||
this.done = true;
|
||||
}
|
||||
|
||||
public void initializeIterationOnCurve(float[] pts, int type) {
|
||||
System.arraycopy(pts, 0, recCurveStack[0], 0, type);
|
||||
this.curveType = type;
|
||||
this.recLevel = 0;
|
||||
this.lastT = 0;
|
||||
this.lenAtLastT = 0;
|
||||
this.nextT = 0;
|
||||
this.lenAtNextT = 0;
|
||||
goLeft(); // initializes nextT and lenAtNextT properly
|
||||
this.lenAtLastSplit = 0;
|
||||
if (recLevel > 0) {
|
||||
this.sides[0] = Side.LEFT;
|
||||
this.done = false;
|
||||
} else {
|
||||
// the root of the tree is a leaf so we're done.
|
||||
this.sides[0] = Side.RIGHT;
|
||||
this.done = true;
|
||||
}
|
||||
this.lastSegLen = 0;
|
||||
}
|
||||
|
||||
// returns the t value where the remaining curve should be split in
|
||||
// order for the left subdivided curve to have length len. If len
|
||||
// is >= than the length of the uniterated curve, it returns 1.
|
||||
public float next(float len) {
|
||||
float targetLength = lenAtLastSplit + len;
|
||||
while(lenAtNextT < targetLength) {
|
||||
if (done) {
|
||||
lastSegLen = lenAtNextT - lenAtLastSplit;
|
||||
return 1;
|
||||
}
|
||||
goToNextLeaf();
|
||||
}
|
||||
lenAtLastSplit = targetLength;
|
||||
float t = binSearchForLen(lenAtLastSplit - lenAtLastT,
|
||||
recCurveStack[recLevel], curveType, lenAtNextT - lenAtLastT, ERR);
|
||||
// t is relative to the current leaf, so we must make it a valid parameter
|
||||
// of the original curve.
|
||||
t = t * (nextT - lastT) + lastT;
|
||||
if (t >= 1) {
|
||||
t = 1;
|
||||
done = true;
|
||||
}
|
||||
// even if done = true, if we're here, that means targetLength
|
||||
// is equal to, or very, very close to the total length of the
|
||||
// curve, so lastSegLen won't be too high. In cases where len
|
||||
// overshoots the curve, this method will exit in the while
|
||||
// loop, and lastSegLen will still be set to the right value.
|
||||
lastSegLen = len;
|
||||
return t;
|
||||
}
|
||||
|
||||
public float lastSegLen() {
|
||||
return lastSegLen;
|
||||
}
|
||||
|
||||
// Returns t such that if leaf is subdivided at t the left
|
||||
// curve will have length len. leafLen must be the length of leaf.
|
||||
private static Curve bsc = new Curve();
|
||||
private static float binSearchForLen(float len, float[] leaf, int type,
|
||||
float leafLen, float err)
|
||||
{
|
||||
assert len <= leafLen;
|
||||
bsc.set(leaf, type);
|
||||
float errBound = err*len;
|
||||
float left = 0, right = 1;
|
||||
while (left < right) {
|
||||
float m = (left + right) / 2;
|
||||
if (m == left || m == right) {
|
||||
return m;
|
||||
}
|
||||
float x = bsc.xat(m);
|
||||
float y = bsc.yat(m);
|
||||
float leftLen = Helpers.linelen(leaf[0], leaf[1], x, y);
|
||||
if (Math.abs(leftLen - len) < errBound) {
|
||||
return m;
|
||||
}
|
||||
if (leftLen < len) {
|
||||
left = m;
|
||||
} else {
|
||||
right = m;
|
||||
}
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
// go to the next leaf (in an inorder traversal) in the recursion tree
|
||||
// preconditions: must be on a leaf, and that leaf must not be the root.
|
||||
private void goToNextLeaf() {
|
||||
// We must go to the first ancestor node that has an unvisited
|
||||
// right child.
|
||||
recLevel--;
|
||||
while(sides[recLevel] == Side.RIGHT) {
|
||||
if (recLevel == 0) {
|
||||
done = true;
|
||||
return;
|
||||
}
|
||||
recLevel--;
|
||||
}
|
||||
|
||||
sides[recLevel] = Side.RIGHT;
|
||||
System.arraycopy(recCurveStack[recLevel], 0, recCurveStack[recLevel+1], 0, curveType);
|
||||
recLevel++;
|
||||
goLeft();
|
||||
}
|
||||
|
||||
// go to the leftmost node from the current node. Return its length.
|
||||
private void goLeft() {
|
||||
float len = onLeaf();
|
||||
if (len >= 0) {
|
||||
lastT = nextT;
|
||||
lenAtLastT = lenAtNextT;
|
||||
nextT += (1 << (limit - recLevel)) * minTincrement;
|
||||
lenAtNextT += len;
|
||||
} else {
|
||||
Helpers.subdivide(recCurveStack[recLevel], 0,
|
||||
recCurveStack[recLevel+1], 0,
|
||||
recCurveStack[recLevel], 0, curveType);
|
||||
sides[recLevel] = Side.LEFT;
|
||||
recLevel++;
|
||||
goLeft();
|
||||
}
|
||||
}
|
||||
|
||||
// this is a bit of a hack. It returns -1 if we're not on a leaf, and
|
||||
// the length of the leaf if we are on a leaf.
|
||||
private float onLeaf() {
|
||||
float polylen = Helpers.polyLineLength(recCurveStack[recLevel], 0, curveType);
|
||||
float linelen = Helpers.linelen(recCurveStack[recLevel][0], recCurveStack[recLevel][1],
|
||||
recCurveStack[recLevel][curveType - 2], recCurveStack[recLevel][curveType - 1]);
|
||||
return (polylen - linelen < ERR || recLevel == limit) ?
|
||||
(polylen + linelen)/2 : -1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void curveTo(float x1, float y1,
|
||||
float x2, float y2,
|
||||
float x3, float y3)
|
||||
{
|
||||
curCurvepts[0] = x0; curCurvepts[1] = y0;
|
||||
curCurvepts[2] = x1; curCurvepts[3] = y1;
|
||||
curCurvepts[4] = x2; curCurvepts[5] = y2;
|
||||
curCurvepts[6] = x3; curCurvepts[7] = y3;
|
||||
somethingTo(8);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void quadTo(float x1, float y1, float x2, float y2) {
|
||||
curCurvepts[0] = x0; curCurvepts[1] = y0;
|
||||
curCurvepts[2] = x1; curCurvepts[3] = y1;
|
||||
curCurvepts[4] = x2; curCurvepts[5] = y2;
|
||||
somethingTo(6);
|
||||
}
|
||||
|
||||
public void closePath() {
|
||||
lineTo(sx, sy);
|
||||
if (firstSegidx > 0) {
|
||||
if (!dashOn || needsMoveTo) {
|
||||
out.moveTo(sx, sy);
|
||||
}
|
||||
emitFirstSegments();
|
||||
}
|
||||
moveTo(sx, sy);
|
||||
}
|
||||
|
||||
public void pathDone() {
|
||||
if (firstSegidx > 0) {
|
||||
out.moveTo(sx, sy);
|
||||
emitFirstSegments();
|
||||
}
|
||||
out.pathDone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getNativeConsumer() {
|
||||
throw new InternalError("Dasher does not use a native consumer");
|
||||
}
|
||||
}
|
||||
|
||||
|
478
jdk/src/share/classes/sun/java2d/pisces/Helpers.java
Normal file
478
jdk/src/share/classes/sun/java2d/pisces/Helpers.java
Normal file
@ -0,0 +1,478 @@
|
||||
/*
|
||||
* Copyright (c) 2007, 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
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.java2d.pisces;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
final class Helpers {
|
||||
private Helpers() {
|
||||
throw new Error("This is a non instantiable class");
|
||||
}
|
||||
|
||||
static boolean within(final float x, final float y, final float err) {
|
||||
final float d = y - x;
|
||||
return (d <= err && d >= -err);
|
||||
}
|
||||
|
||||
static boolean within(final double x, final double y, final double err) {
|
||||
final double d = y - x;
|
||||
return (d <= err && d >= -err);
|
||||
}
|
||||
|
||||
static int quadraticRoots(final float a, final float b,
|
||||
final float c, float[] zeroes, final int off)
|
||||
{
|
||||
int ret = off;
|
||||
float t;
|
||||
if (a != 0f) {
|
||||
final float dis = b*b - 4*a*c;
|
||||
if (dis > 0) {
|
||||
final float sqrtDis = (float)Math.sqrt(dis);
|
||||
// depending on the sign of b we use a slightly different
|
||||
// algorithm than the traditional one to find one of the roots
|
||||
// so we can avoid adding numbers of different signs (which
|
||||
// might result in loss of precision).
|
||||
if (b >= 0) {
|
||||
zeroes[ret++] = (2 * c) / (-b - sqrtDis);
|
||||
zeroes[ret++] = (-b - sqrtDis) / (2 * a);
|
||||
} else {
|
||||
zeroes[ret++] = (-b + sqrtDis) / (2 * a);
|
||||
zeroes[ret++] = (2 * c) / (-b + sqrtDis);
|
||||
}
|
||||
} else if (dis == 0f) {
|
||||
t = (-b) / (2 * a);
|
||||
zeroes[ret++] = t;
|
||||
}
|
||||
} else {
|
||||
if (b != 0f) {
|
||||
t = (-c) / b;
|
||||
zeroes[ret++] = t;
|
||||
}
|
||||
}
|
||||
return ret - off;
|
||||
}
|
||||
|
||||
// find the roots of g(t) = a*t^3 + b*t^2 + c*t + d in [A,B)
|
||||
// We will not use Cardano's method, since it is complicated and
|
||||
// involves too many square and cubic roots. We will use Newton's method.
|
||||
// TODO: this should probably return ALL roots. Then the user can do
|
||||
// his own filtering of roots outside [A,B).
|
||||
static int cubicRootsInAB(final float a, final float b,
|
||||
final float c, final float d,
|
||||
float[] pts, final int off, final float E,
|
||||
final float A, final float B)
|
||||
{
|
||||
if (a == 0) {
|
||||
return quadraticRoots(b, c, d, pts, off);
|
||||
}
|
||||
// the coefficients of g'(t). no dc variable because dc=c
|
||||
// we use these to get the critical points of g(t), which
|
||||
// we then use to chose starting points for Newton's method. These
|
||||
// should be very close to the actual roots.
|
||||
final float da = 3 * a;
|
||||
final float db = 2 * b;
|
||||
int numCritPts = quadraticRoots(da, db, c, pts, off+1);
|
||||
numCritPts = filterOutNotInAB(pts, off+1, numCritPts, A, B) - off - 1;
|
||||
// need them sorted.
|
||||
if (numCritPts == 2 && pts[off+1] > pts[off+2]) {
|
||||
float tmp = pts[off+1];
|
||||
pts[off+1] = pts[off+2];
|
||||
pts[off+2] = tmp;
|
||||
}
|
||||
|
||||
int ret = off;
|
||||
|
||||
// we don't actually care much about the extrema themselves. We
|
||||
// only use them to ensure that g(t) is monotonic in each
|
||||
// interval [pts[i],pts[i+1] (for i in off...off+numCritPts+1).
|
||||
// This will allow us to determine intervals containing exactly
|
||||
// one root.
|
||||
// The end points of the interval are always local extrema.
|
||||
pts[off] = A;
|
||||
pts[off + numCritPts + 1] = B;
|
||||
numCritPts += 2;
|
||||
|
||||
float x0 = pts[off], fx0 = evalCubic(a, b, c, d, x0);
|
||||
for (int i = off; i < off + numCritPts - 1; i++) {
|
||||
float x1 = pts[i+1], fx1 = evalCubic(a, b, c, d, x1);
|
||||
if (fx0 == 0f) {
|
||||
pts[ret++] = x0;
|
||||
} else if (fx1 * fx0 < 0f) { // have opposite signs
|
||||
pts[ret++] = CubicNewton(a, b, c, d,
|
||||
x0 + fx0 * (x1 - x0) / (fx0 - fx1), E);
|
||||
}
|
||||
x0 = x1;
|
||||
fx0 = fx1;
|
||||
}
|
||||
return ret - off;
|
||||
}
|
||||
|
||||
// precondition: the polynomial to be evaluated must not be 0 at x0.
|
||||
static float CubicNewton(final float a, final float b,
|
||||
final float c, final float d,
|
||||
float x0, final float err)
|
||||
{
|
||||
// considering how this function is used, 10 should be more than enough
|
||||
final int itlimit = 10;
|
||||
float fx0 = evalCubic(a, b, c, d, x0);
|
||||
float x1;
|
||||
int count = 0;
|
||||
while(true) {
|
||||
x1 = x0 - (fx0 / evalCubic(0, 3 * a, 2 * b, c, x0));
|
||||
if (Math.abs(x1 - x0) < err * Math.abs(x1 + x0) || count == itlimit) {
|
||||
break;
|
||||
}
|
||||
x0 = x1;
|
||||
fx0 = evalCubic(a, b, c, d, x0);
|
||||
count++;
|
||||
}
|
||||
return x1;
|
||||
}
|
||||
|
||||
// fills the input array with numbers 0, INC, 2*INC, ...
|
||||
static void fillWithIdxes(final float[] data, final int[] idxes) {
|
||||
if (idxes.length > 0) {
|
||||
idxes[0] = 0;
|
||||
for (int i = 1; i < idxes.length; i++) {
|
||||
idxes[i] = idxes[i-1] + (int)data[idxes[i-1]];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void fillWithIdxes(final int[] idxes, final int inc) {
|
||||
if (idxes.length > 0) {
|
||||
idxes[0] = 0;
|
||||
for (int i = 1; i < idxes.length; i++) {
|
||||
idxes[i] = idxes[i-1] + inc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// These use a hardcoded factor of 2 for increasing sizes. Perhaps this
|
||||
// should be provided as an argument.
|
||||
static float[] widenArray(float[] in, final int cursize, final int numToAdd) {
|
||||
if (in == null) {
|
||||
return new float[5 * numToAdd];
|
||||
}
|
||||
if (in.length >= cursize + numToAdd) {
|
||||
return in;
|
||||
}
|
||||
return Arrays.copyOf(in, 2 * (cursize + numToAdd));
|
||||
}
|
||||
static int[] widenArray(int[] in, final int cursize, final int numToAdd) {
|
||||
if (in.length >= cursize + numToAdd) {
|
||||
return in;
|
||||
}
|
||||
return Arrays.copyOf(in, 2 * (cursize + numToAdd));
|
||||
}
|
||||
|
||||
static float evalCubic(final float a, final float b,
|
||||
final float c, final float d,
|
||||
final float t)
|
||||
{
|
||||
return t * (t * (t * a + b) + c) + d;
|
||||
}
|
||||
|
||||
static float evalQuad(final float a, final float b,
|
||||
final float c, final float t)
|
||||
{
|
||||
return t * (t * a + b) + c;
|
||||
}
|
||||
|
||||
// returns the index 1 past the last valid element remaining after filtering
|
||||
static int filterOutNotInAB(float[] nums, final int off, final int len,
|
||||
final float a, final float b)
|
||||
{
|
||||
int ret = off;
|
||||
for (int i = off; i < off + len; i++) {
|
||||
if (nums[i] > a && nums[i] < b) {
|
||||
nums[ret++] = nums[i];
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static float polyLineLength(float[] poly, final int off, final int nCoords) {
|
||||
assert nCoords % 2 == 0 && poly.length >= off + nCoords : "";
|
||||
float acc = 0;
|
||||
for (int i = off + 2; i < off + nCoords; i += 2) {
|
||||
acc += linelen(poly[i], poly[i+1], poly[i-2], poly[i-1]);
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
|
||||
static float linelen(float x1, float y1, float x2, float y2) {
|
||||
return (float)Math.hypot(x2 - x1, y2 - y1);
|
||||
}
|
||||
|
||||
static void subdivide(float[] src, int srcoff, float[] left, int leftoff,
|
||||
float[] right, int rightoff, int type)
|
||||
{
|
||||
switch(type) {
|
||||
case 6:
|
||||
Helpers.subdivideQuad(src, srcoff, left, leftoff, right, rightoff);
|
||||
break;
|
||||
case 8:
|
||||
Helpers.subdivideCubic(src, srcoff, left, leftoff, right, rightoff);
|
||||
break;
|
||||
default:
|
||||
throw new InternalError("Unsupported curve type");
|
||||
}
|
||||
}
|
||||
|
||||
static void isort(float[] a, int off, int len) {
|
||||
for (int i = off + 1; i < off + len; i++) {
|
||||
float ai = a[i];
|
||||
int j = i - 1;
|
||||
for (; j >= off && a[j] > ai; j--) {
|
||||
a[j+1] = a[j];
|
||||
}
|
||||
a[j+1] = ai;
|
||||
}
|
||||
}
|
||||
|
||||
// Most of these are copied from classes in java.awt.geom because we need
|
||||
// float versions of these functions, and Line2D, CubicCurve2D,
|
||||
// QuadCurve2D don't provide them.
|
||||
/**
|
||||
* Subdivides the cubic curve specified by the coordinates
|
||||
* stored in the <code>src</code> array at indices <code>srcoff</code>
|
||||
* through (<code>srcoff</code> + 7) and stores the
|
||||
* resulting two subdivided curves into the two result arrays at the
|
||||
* corresponding indices.
|
||||
* Either or both of the <code>left</code> and <code>right</code>
|
||||
* arrays may be <code>null</code> or a reference to the same array
|
||||
* as the <code>src</code> array.
|
||||
* Note that the last point in the first subdivided curve is the
|
||||
* same as the first point in the second subdivided curve. Thus,
|
||||
* it is possible to pass the same array for <code>left</code>
|
||||
* and <code>right</code> and to use offsets, such as <code>rightoff</code>
|
||||
* equals (<code>leftoff</code> + 6), in order
|
||||
* to avoid allocating extra storage for this common point.
|
||||
* @param src the array holding the coordinates for the source curve
|
||||
* @param srcoff the offset into the array of the beginning of the
|
||||
* the 6 source coordinates
|
||||
* @param left the array for storing the coordinates for the first
|
||||
* half of the subdivided curve
|
||||
* @param leftoff the offset into the array of the beginning of the
|
||||
* the 6 left coordinates
|
||||
* @param right the array for storing the coordinates for the second
|
||||
* half of the subdivided curve
|
||||
* @param rightoff the offset into the array of the beginning of the
|
||||
* the 6 right coordinates
|
||||
* @since 1.7
|
||||
*/
|
||||
static void subdivideCubic(float src[], int srcoff,
|
||||
float left[], int leftoff,
|
||||
float right[], int rightoff)
|
||||
{
|
||||
float x1 = src[srcoff + 0];
|
||||
float y1 = src[srcoff + 1];
|
||||
float ctrlx1 = src[srcoff + 2];
|
||||
float ctrly1 = src[srcoff + 3];
|
||||
float ctrlx2 = src[srcoff + 4];
|
||||
float ctrly2 = src[srcoff + 5];
|
||||
float x2 = src[srcoff + 6];
|
||||
float y2 = src[srcoff + 7];
|
||||
if (left != null) {
|
||||
left[leftoff + 0] = x1;
|
||||
left[leftoff + 1] = y1;
|
||||
}
|
||||
if (right != null) {
|
||||
right[rightoff + 6] = x2;
|
||||
right[rightoff + 7] = y2;
|
||||
}
|
||||
x1 = (x1 + ctrlx1) / 2.0f;
|
||||
y1 = (y1 + ctrly1) / 2.0f;
|
||||
x2 = (x2 + ctrlx2) / 2.0f;
|
||||
y2 = (y2 + ctrly2) / 2.0f;
|
||||
float centerx = (ctrlx1 + ctrlx2) / 2.0f;
|
||||
float centery = (ctrly1 + ctrly2) / 2.0f;
|
||||
ctrlx1 = (x1 + centerx) / 2.0f;
|
||||
ctrly1 = (y1 + centery) / 2.0f;
|
||||
ctrlx2 = (x2 + centerx) / 2.0f;
|
||||
ctrly2 = (y2 + centery) / 2.0f;
|
||||
centerx = (ctrlx1 + ctrlx2) / 2.0f;
|
||||
centery = (ctrly1 + ctrly2) / 2.0f;
|
||||
if (left != null) {
|
||||
left[leftoff + 2] = x1;
|
||||
left[leftoff + 3] = y1;
|
||||
left[leftoff + 4] = ctrlx1;
|
||||
left[leftoff + 5] = ctrly1;
|
||||
left[leftoff + 6] = centerx;
|
||||
left[leftoff + 7] = centery;
|
||||
}
|
||||
if (right != null) {
|
||||
right[rightoff + 0] = centerx;
|
||||
right[rightoff + 1] = centery;
|
||||
right[rightoff + 2] = ctrlx2;
|
||||
right[rightoff + 3] = ctrly2;
|
||||
right[rightoff + 4] = x2;
|
||||
right[rightoff + 5] = y2;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void subdivideCubicAt(float t, float src[], int srcoff,
|
||||
float left[], int leftoff,
|
||||
float right[], int rightoff)
|
||||
{
|
||||
float x1 = src[srcoff + 0];
|
||||
float y1 = src[srcoff + 1];
|
||||
float ctrlx1 = src[srcoff + 2];
|
||||
float ctrly1 = src[srcoff + 3];
|
||||
float ctrlx2 = src[srcoff + 4];
|
||||
float ctrly2 = src[srcoff + 5];
|
||||
float x2 = src[srcoff + 6];
|
||||
float y2 = src[srcoff + 7];
|
||||
if (left != null) {
|
||||
left[leftoff + 0] = x1;
|
||||
left[leftoff + 1] = y1;
|
||||
}
|
||||
if (right != null) {
|
||||
right[rightoff + 6] = x2;
|
||||
right[rightoff + 7] = y2;
|
||||
}
|
||||
x1 = x1 + t * (ctrlx1 - x1);
|
||||
y1 = y1 + t * (ctrly1 - y1);
|
||||
x2 = ctrlx2 + t * (x2 - ctrlx2);
|
||||
y2 = ctrly2 + t * (y2 - ctrly2);
|
||||
float centerx = ctrlx1 + t * (ctrlx2 - ctrlx1);
|
||||
float centery = ctrly1 + t * (ctrly2 - ctrly1);
|
||||
ctrlx1 = x1 + t * (centerx - x1);
|
||||
ctrly1 = y1 + t * (centery - y1);
|
||||
ctrlx2 = centerx + t * (x2 - centerx);
|
||||
ctrly2 = centery + t * (y2 - centery);
|
||||
centerx = ctrlx1 + t * (ctrlx2 - ctrlx1);
|
||||
centery = ctrly1 + t * (ctrly2 - ctrly1);
|
||||
if (left != null) {
|
||||
left[leftoff + 2] = x1;
|
||||
left[leftoff + 3] = y1;
|
||||
left[leftoff + 4] = ctrlx1;
|
||||
left[leftoff + 5] = ctrly1;
|
||||
left[leftoff + 6] = centerx;
|
||||
left[leftoff + 7] = centery;
|
||||
}
|
||||
if (right != null) {
|
||||
right[rightoff + 0] = centerx;
|
||||
right[rightoff + 1] = centery;
|
||||
right[rightoff + 2] = ctrlx2;
|
||||
right[rightoff + 3] = ctrly2;
|
||||
right[rightoff + 4] = x2;
|
||||
right[rightoff + 5] = y2;
|
||||
}
|
||||
}
|
||||
|
||||
static void subdivideQuad(float src[], int srcoff,
|
||||
float left[], int leftoff,
|
||||
float right[], int rightoff)
|
||||
{
|
||||
float x1 = src[srcoff + 0];
|
||||
float y1 = src[srcoff + 1];
|
||||
float ctrlx = src[srcoff + 2];
|
||||
float ctrly = src[srcoff + 3];
|
||||
float x2 = src[srcoff + 4];
|
||||
float y2 = src[srcoff + 5];
|
||||
if (left != null) {
|
||||
left[leftoff + 0] = x1;
|
||||
left[leftoff + 1] = y1;
|
||||
}
|
||||
if (right != null) {
|
||||
right[rightoff + 4] = x2;
|
||||
right[rightoff + 5] = y2;
|
||||
}
|
||||
x1 = (x1 + ctrlx) / 2.0f;
|
||||
y1 = (y1 + ctrly) / 2.0f;
|
||||
x2 = (x2 + ctrlx) / 2.0f;
|
||||
y2 = (y2 + ctrly) / 2.0f;
|
||||
ctrlx = (x1 + x2) / 2.0f;
|
||||
ctrly = (y1 + y2) / 2.0f;
|
||||
if (left != null) {
|
||||
left[leftoff + 2] = x1;
|
||||
left[leftoff + 3] = y1;
|
||||
left[leftoff + 4] = ctrlx;
|
||||
left[leftoff + 5] = ctrly;
|
||||
}
|
||||
if (right != null) {
|
||||
right[rightoff + 0] = ctrlx;
|
||||
right[rightoff + 1] = ctrly;
|
||||
right[rightoff + 2] = x2;
|
||||
right[rightoff + 3] = y2;
|
||||
}
|
||||
}
|
||||
|
||||
static void subdivideQuadAt(float t, float src[], int srcoff,
|
||||
float left[], int leftoff,
|
||||
float right[], int rightoff)
|
||||
{
|
||||
float x1 = src[srcoff + 0];
|
||||
float y1 = src[srcoff + 1];
|
||||
float ctrlx = src[srcoff + 2];
|
||||
float ctrly = src[srcoff + 3];
|
||||
float x2 = src[srcoff + 4];
|
||||
float y2 = src[srcoff + 5];
|
||||
if (left != null) {
|
||||
left[leftoff + 0] = x1;
|
||||
left[leftoff + 1] = y1;
|
||||
}
|
||||
if (right != null) {
|
||||
right[rightoff + 4] = x2;
|
||||
right[rightoff + 5] = y2;
|
||||
}
|
||||
x1 = x1 + t * (ctrlx - x1);
|
||||
y1 = y1 + t * (ctrly - y1);
|
||||
x2 = ctrlx + t * (x2 - ctrlx);
|
||||
y2 = ctrly + t * (y2 - ctrly);
|
||||
ctrlx = x1 + t * (x2 - x1);
|
||||
ctrly = y1 + t * (y2 - y1);
|
||||
if (left != null) {
|
||||
left[leftoff + 2] = x1;
|
||||
left[leftoff + 3] = y1;
|
||||
left[leftoff + 4] = ctrlx;
|
||||
left[leftoff + 5] = ctrly;
|
||||
}
|
||||
if (right != null) {
|
||||
right[rightoff + 0] = ctrlx;
|
||||
right[rightoff + 1] = ctrly;
|
||||
right[rightoff + 2] = x2;
|
||||
right[rightoff + 3] = y2;
|
||||
}
|
||||
}
|
||||
|
||||
static void subdivideAt(float t, float src[], int srcoff,
|
||||
float left[], int leftoff,
|
||||
float right[], int rightoff, int size)
|
||||
{
|
||||
switch(size) {
|
||||
case 8:
|
||||
subdivideCubicAt(t, src, srcoff, left, leftoff, right, rightoff);
|
||||
break;
|
||||
case 6:
|
||||
subdivideQuadAt(t, src, srcoff, left, leftoff, right, rightoff);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@ -1,93 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2007, 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
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.java2d.pisces;
|
||||
|
||||
/**
|
||||
* The <code>LineSink</code> interface accepts a series of line
|
||||
* drawing commands: <code>moveTo</code>, <code>lineTo</code>,
|
||||
* <code>close</code> (equivalent to a <code>lineTo</code> command
|
||||
* with an argument equal to the argument of the last
|
||||
* <code>moveTo</code> command), and <code>end</code>.
|
||||
*
|
||||
* <p> A <code>Flattener</code> may be used to connect a general path
|
||||
* source to a <code>LineSink</code>.
|
||||
*
|
||||
* <p> The <code>Renderer</code> class implements the
|
||||
* <code>LineSink</code> interface.
|
||||
*
|
||||
*/
|
||||
public interface LineSink {
|
||||
|
||||
/**
|
||||
* Moves the current drawing position to the point <code>(x0,
|
||||
* y0)</code>.
|
||||
*
|
||||
* @param x0 the X coordinate
|
||||
* @param y0 the Y coordinate
|
||||
*/
|
||||
public void moveTo(float x0, float y0);
|
||||
|
||||
/**
|
||||
* Provides a hint that the current segment should be joined to
|
||||
* the following segment using an explicit miter or round join if
|
||||
* required.
|
||||
*
|
||||
* <p> An application-generated path will generally have no need
|
||||
* to contain calls to this method; they are typically introduced
|
||||
* by a <code>Flattener</code> to mark segment divisions that
|
||||
* appear in its input, and consumed by a <code>Stroker</code>
|
||||
* that is responsible for emitting the miter or round join
|
||||
* segments.
|
||||
*
|
||||
* <p> Other <code>LineSink</code> classes should simply pass this
|
||||
* hint to their output sink as needed.
|
||||
*/
|
||||
public void lineJoin();
|
||||
|
||||
/**
|
||||
* Draws a line from the current drawing position to the point
|
||||
* <code>(x1, y1)</code> and sets the current drawing position to
|
||||
* <code>(x1, y1)</code>.
|
||||
*
|
||||
* @param x1 the X coordinate
|
||||
* @param y1 the Y coordinate
|
||||
*/
|
||||
public void lineTo(float x1, float y1);
|
||||
|
||||
/**
|
||||
* Closes the current path by drawing a line from the current
|
||||
* drawing position to the point specified by the moset recent
|
||||
* <code>moveTo</code> command.
|
||||
*/
|
||||
public void close();
|
||||
|
||||
/**
|
||||
* Ends the current path. It may be necessary to end a path in
|
||||
* order to allow end caps to be drawn.
|
||||
*/
|
||||
public void end();
|
||||
|
||||
}
|
@ -25,6 +25,8 @@
|
||||
|
||||
package sun.java2d.pisces;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* An object used to cache pre-rendered complex paths.
|
||||
*
|
||||
@ -32,115 +34,153 @@ package sun.java2d.pisces;
|
||||
*/
|
||||
public final class PiscesCache {
|
||||
|
||||
int bboxX0, bboxY0, bboxX1, bboxY1;
|
||||
final int bboxX0, bboxY0, bboxX1, bboxY1;
|
||||
|
||||
byte[] rowAARLE;
|
||||
int alphaRLELength;
|
||||
// rowAARLE[i] holds the encoding of the pixel row with y = bboxY0+i.
|
||||
// The format of each of the inner arrays is: rowAARLE[i][0,1] = (x0, n)
|
||||
// where x0 is the first x in row i with nonzero alpha, and n is the
|
||||
// number of RLE entries in this row. rowAARLE[i][j,j+1] for j>1 is
|
||||
// (val,runlen)
|
||||
final int[][] rowAARLE;
|
||||
|
||||
int[] rowOffsetsRLE;
|
||||
int[] minTouched;
|
||||
int alphaRows;
|
||||
// RLE encodings are added in increasing y rows and then in increasing
|
||||
// x inside those rows. Therefore, at any one time there is a well
|
||||
// defined position (x,y) where a run length is about to be added (or
|
||||
// the row terminated). x0,y0 is this (x,y)-(bboxX0,bboxY0). They
|
||||
// are used to get indices into the current tile.
|
||||
private int x0 = Integer.MIN_VALUE, y0 = Integer.MIN_VALUE;
|
||||
|
||||
private PiscesCache() {}
|
||||
// touchedTile[i][j] is the sum of all the alphas in the tile with
|
||||
// y=i*TILE_SIZE+bboxY0 and x=j*TILE_SIZE+bboxX0.
|
||||
private final int[][] touchedTile;
|
||||
|
||||
public static PiscesCache createInstance() {
|
||||
return new PiscesCache();
|
||||
static final int TILE_SIZE_LG = 5;
|
||||
static final int TILE_SIZE = 1 << TILE_SIZE_LG; // 32
|
||||
private static final int INIT_ROW_SIZE = 8; // enough for 3 run lengths
|
||||
|
||||
PiscesCache(int minx, int miny, int maxx, int maxy) {
|
||||
assert maxy >= miny && maxx >= minx;
|
||||
bboxX0 = minx;
|
||||
bboxY0 = miny;
|
||||
bboxX1 = maxx + 1;
|
||||
bboxY1 = maxy + 1;
|
||||
// we could just leave the inner arrays as null and allocate them
|
||||
// lazily (which would be beneficial for shapes with gaps), but we
|
||||
// assume there won't be too many of those so we allocate everything
|
||||
// up front (which is better for other cases)
|
||||
rowAARLE = new int[bboxY1 - bboxY0 + 1][INIT_ROW_SIZE];
|
||||
x0 = 0;
|
||||
y0 = -1; // -1 makes the first assert in startRow succeed
|
||||
// the ceiling of (maxy - miny + 1) / TILE_SIZE;
|
||||
int nyTiles = (maxy - miny + TILE_SIZE) >> TILE_SIZE_LG;
|
||||
int nxTiles = (maxx - minx + TILE_SIZE) >> TILE_SIZE_LG;
|
||||
|
||||
touchedTile = new int[nyTiles][nxTiles];
|
||||
}
|
||||
|
||||
private static final float ROWAA_RLE_FACTOR = 1.5f;
|
||||
private static final float TOUCHED_FACTOR = 1.5f;
|
||||
private static final int MIN_TOUCHED_LEN = 64;
|
||||
|
||||
private void reallocRowAARLE(int newLength) {
|
||||
if (rowAARLE == null) {
|
||||
rowAARLE = new byte[newLength];
|
||||
} else if (rowAARLE.length < newLength) {
|
||||
int len = Math.max(newLength,
|
||||
(int)(rowAARLE.length*ROWAA_RLE_FACTOR));
|
||||
byte[] newRowAARLE = new byte[len];
|
||||
System.arraycopy(rowAARLE, 0, newRowAARLE, 0, rowAARLE.length);
|
||||
rowAARLE = newRowAARLE;
|
||||
void addRLERun(int val, int runLen) {
|
||||
if (runLen > 0) {
|
||||
addTupleToRow(y0, val, runLen);
|
||||
if (val != 0) {
|
||||
// the x and y of the current row, minus bboxX0, bboxY0
|
||||
int tx = x0 >> TILE_SIZE_LG;
|
||||
int ty = y0 >> TILE_SIZE_LG;
|
||||
int tx1 = (x0 + runLen - 1) >> TILE_SIZE_LG;
|
||||
// while we forbid rows from starting before bboxx0, our users
|
||||
// can still store rows that go beyond bboxx1 (although this
|
||||
// shouldn't happen), so it's a good idea to check that i
|
||||
// is not going out of bounds in touchedTile[ty]
|
||||
if (tx1 >= touchedTile[ty].length) {
|
||||
tx1 = touchedTile[ty].length - 1;
|
||||
}
|
||||
if (tx <= tx1) {
|
||||
int nextTileXCoord = (tx + 1) << TILE_SIZE_LG;
|
||||
if (nextTileXCoord > x0+runLen) {
|
||||
touchedTile[ty][tx] += val * runLen;
|
||||
} else {
|
||||
touchedTile[ty][tx] += val * (nextTileXCoord - x0);
|
||||
}
|
||||
tx++;
|
||||
}
|
||||
// don't go all the way to tx1 - we need to handle the last
|
||||
// tile as a special case (just like we did with the first
|
||||
for (; tx < tx1; tx++) {
|
||||
// try {
|
||||
touchedTile[ty][tx] += (val << TILE_SIZE_LG);
|
||||
// } catch (RuntimeException e) {
|
||||
// System.out.println("x0, y0: " + x0 + ", " + y0);
|
||||
// System.out.printf("tx, ty, tx1: %d, %d, %d %n", tx, ty, tx1);
|
||||
// System.out.printf("bboxX/Y0/1: %d, %d, %d, %d %n",
|
||||
// bboxX0, bboxY0, bboxX1, bboxY1);
|
||||
// throw e;
|
||||
// }
|
||||
}
|
||||
// they will be equal unless x0>>TILE_SIZE_LG == tx1
|
||||
if (tx == tx1) {
|
||||
int lastXCoord = Math.min(x0 + runLen, (tx + 1) << TILE_SIZE_LG);
|
||||
int txXCoord = tx << TILE_SIZE_LG;
|
||||
touchedTile[ty][tx] += val * (lastXCoord - txXCoord);
|
||||
}
|
||||
}
|
||||
x0 += runLen;
|
||||
}
|
||||
}
|
||||
|
||||
private void reallocRowInfo(int newHeight) {
|
||||
if (minTouched == null) {
|
||||
int len = Math.max(newHeight, MIN_TOUCHED_LEN);
|
||||
minTouched = new int[len];
|
||||
rowOffsetsRLE = new int[len];
|
||||
} else if (minTouched.length < newHeight) {
|
||||
int len = Math.max(newHeight,
|
||||
(int)(minTouched.length*TOUCHED_FACTOR));
|
||||
int[] newMinTouched = new int[len];
|
||||
int[] newRowOffsetsRLE = new int[len];
|
||||
System.arraycopy(minTouched, 0, newMinTouched, 0,
|
||||
alphaRows);
|
||||
System.arraycopy(rowOffsetsRLE, 0, newRowOffsetsRLE, 0,
|
||||
alphaRows);
|
||||
minTouched = newMinTouched;
|
||||
rowOffsetsRLE = newRowOffsetsRLE;
|
||||
}
|
||||
void startRow(int y, int x) {
|
||||
// rows are supposed to be added by increasing y.
|
||||
assert y - bboxY0 > y0;
|
||||
assert y <= bboxY1; // perhaps this should be < instead of <=
|
||||
|
||||
y0 = y - bboxY0;
|
||||
// this should be a new, uninitialized row.
|
||||
assert rowAARLE[y0][1] == 0;
|
||||
|
||||
x0 = x - bboxX0;
|
||||
assert x0 >= 0 : "Input must not be to the left of bbox bounds";
|
||||
|
||||
// the way addTupleToRow is implemented it would work for this but it's
|
||||
// not a good idea to use it because it is meant for adding
|
||||
// RLE tuples, not the first tuple (which is special).
|
||||
rowAARLE[y0][0] = x;
|
||||
rowAARLE[y0][1] = 2;
|
||||
}
|
||||
|
||||
void addRLERun(byte val, int runLen) {
|
||||
reallocRowAARLE(alphaRLELength + 2);
|
||||
rowAARLE[alphaRLELength++] = val;
|
||||
rowAARLE[alphaRLELength++] = (byte)runLen;
|
||||
int alphaSumInTile(int x, int y) {
|
||||
x -= bboxX0;
|
||||
y -= bboxY0;
|
||||
return touchedTile[y>>TILE_SIZE_LG][x>>TILE_SIZE_LG];
|
||||
}
|
||||
|
||||
void startRow(int y, int x0, int x1) {
|
||||
if (alphaRows == 0) {
|
||||
bboxY0 = y;
|
||||
bboxY1 = y+1;
|
||||
bboxX0 = x0;
|
||||
bboxX1 = x1+1;
|
||||
} else {
|
||||
if (bboxX0 > x0) bboxX0 = x0;
|
||||
if (bboxX1 < x1 + 1) bboxX1 = x1 + 1;
|
||||
while (bboxY1++ < y) {
|
||||
reallocRowInfo(alphaRows+1);
|
||||
minTouched[alphaRows] = 0;
|
||||
// Assuming last 2 entries in rowAARLE are 0,0
|
||||
rowOffsetsRLE[alphaRows] = alphaRLELength-2;
|
||||
alphaRows++;
|
||||
int minTouched(int rowidx) {
|
||||
return rowAARLE[rowidx][0];
|
||||
}
|
||||
|
||||
int rowLength(int rowidx) {
|
||||
return rowAARLE[rowidx][1];
|
||||
}
|
||||
|
||||
private void addTupleToRow(int row, int a, int b) {
|
||||
int end = rowAARLE[row][1];
|
||||
rowAARLE[row] = Helpers.widenArray(rowAARLE[row], end, 2);
|
||||
rowAARLE[row][end++] = a;
|
||||
rowAARLE[row][end++] = b;
|
||||
rowAARLE[row][1] = end;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String ret = "bbox = ["+
|
||||
bboxX0+", "+bboxY0+" => "+
|
||||
bboxX1+", "+bboxY1+"]\n";
|
||||
for (int[] row : rowAARLE) {
|
||||
if (row != null) {
|
||||
ret += ("minTouchedX=" + row[0] +
|
||||
"\tRLE Entries: " + Arrays.toString(
|
||||
Arrays.copyOfRange(row, 2, row[1])) + "\n");
|
||||
} else {
|
||||
ret += "[]\n";
|
||||
}
|
||||
}
|
||||
reallocRowInfo(alphaRows+1);
|
||||
minTouched[alphaRows] = x0;
|
||||
rowOffsetsRLE[alphaRows] = alphaRLELength;
|
||||
alphaRows++;
|
||||
}
|
||||
|
||||
public synchronized void dispose() {
|
||||
rowAARLE = null;
|
||||
alphaRLELength = 0;
|
||||
|
||||
minTouched = null;
|
||||
rowOffsetsRLE = null;
|
||||
alphaRows = 0;
|
||||
|
||||
bboxX0 = bboxY0 = bboxX1 = bboxY1 = 0;
|
||||
}
|
||||
|
||||
public void print(java.io.PrintStream out) {
|
||||
synchronized (out) {
|
||||
out.println("bbox = ["+
|
||||
bboxX0+", "+bboxY0+" => "+
|
||||
bboxX1+", "+bboxY1+"]");
|
||||
|
||||
out.println("alphRLELength = "+alphaRLELength);
|
||||
|
||||
for (int y = bboxY0; y < bboxY1; y++) {
|
||||
int i = y-bboxY0;
|
||||
out.println("row["+i+"] == {"+
|
||||
"minX = "+minTouched[i]+
|
||||
", off = "+rowOffsetsRLE[i]+"}");
|
||||
}
|
||||
|
||||
for (int i = 0; i < alphaRLELength; i += 2) {
|
||||
out.println("rle["+i+"] = "+
|
||||
(rowAARLE[i+1]&0xff)+" of "+(rowAARLE[i]&0xff));
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ package sun.java2d.pisces;
|
||||
|
||||
import java.awt.Shape;
|
||||
import java.awt.BasicStroke;
|
||||
import java.awt.geom.FlatteningPathIterator;
|
||||
import java.awt.geom.NoninvertibleTransformException;
|
||||
import java.awt.geom.Path2D;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.PathIterator;
|
||||
@ -38,8 +38,6 @@ import sun.java2d.pipe.RenderingEngine;
|
||||
import sun.java2d.pipe.AATileGenerator;
|
||||
|
||||
public class PiscesRenderingEngine extends RenderingEngine {
|
||||
public static double defaultFlat = 0.1;
|
||||
|
||||
private static enum NormMode {OFF, ON_NO_AA, ON_WITH_AA}
|
||||
|
||||
/**
|
||||
@ -78,20 +76,29 @@ public class PiscesRenderingEngine extends RenderingEngine {
|
||||
miterlimit,
|
||||
dashes,
|
||||
dashphase,
|
||||
new LineSink() {
|
||||
new PathConsumer2D() {
|
||||
public void moveTo(float x0, float y0) {
|
||||
p2d.moveTo(x0, y0);
|
||||
}
|
||||
public void lineJoin() {}
|
||||
public void lineTo(float x1, float y1) {
|
||||
p2d.lineTo(x1, y1);
|
||||
}
|
||||
public void close() {
|
||||
public void closePath() {
|
||||
p2d.closePath();
|
||||
}
|
||||
public void end() {}
|
||||
public void pathDone() {}
|
||||
public void curveTo(float x1, float y1,
|
||||
float x2, float y2,
|
||||
float x3, float y3) {
|
||||
p2d.curveTo(x1, y1, x2, y2, x3, y3);
|
||||
}
|
||||
public void quadTo(float x1, float y1, float x2, float y2) {
|
||||
p2d.quadTo(x1, y1, x2, y2);
|
||||
}
|
||||
public long getNativeConsumer() {
|
||||
throw new InternalError("Not using a native peer");
|
||||
}
|
||||
});
|
||||
|
||||
return p2d;
|
||||
}
|
||||
|
||||
@ -133,22 +140,7 @@ public class PiscesRenderingEngine extends RenderingEngine {
|
||||
NormMode norm = (normalize) ?
|
||||
((antialias) ? NormMode.ON_WITH_AA : NormMode.ON_NO_AA)
|
||||
: NormMode.OFF;
|
||||
strokeTo(src, at, bs, thin, norm, antialias,
|
||||
new LineSink() {
|
||||
public void moveTo(float x0, float y0) {
|
||||
consumer.moveTo(x0, y0);
|
||||
}
|
||||
public void lineJoin() {}
|
||||
public void lineTo(float x1, float y1) {
|
||||
consumer.lineTo(x1, y1);
|
||||
}
|
||||
public void close() {
|
||||
consumer.closePath();
|
||||
}
|
||||
public void end() {
|
||||
consumer.pathDone();
|
||||
}
|
||||
});
|
||||
strokeTo(src, at, bs, thin, norm, antialias, consumer);
|
||||
}
|
||||
|
||||
void strokeTo(Shape src,
|
||||
@ -157,7 +149,7 @@ public class PiscesRenderingEngine extends RenderingEngine {
|
||||
boolean thin,
|
||||
NormMode normalize,
|
||||
boolean antialias,
|
||||
LineSink lsink)
|
||||
PathConsumer2D pc2d)
|
||||
{
|
||||
float lw;
|
||||
if (thin) {
|
||||
@ -178,7 +170,7 @@ public class PiscesRenderingEngine extends RenderingEngine {
|
||||
bs.getMiterLimit(),
|
||||
bs.getDashArray(),
|
||||
bs.getDashPhase(),
|
||||
lsink);
|
||||
pc2d);
|
||||
}
|
||||
|
||||
private float userSpaceLineWidth(AffineTransform at, float lw) {
|
||||
@ -256,28 +248,113 @@ public class PiscesRenderingEngine extends RenderingEngine {
|
||||
float miterlimit,
|
||||
float dashes[],
|
||||
float dashphase,
|
||||
LineSink lsink)
|
||||
PathConsumer2D pc2d)
|
||||
{
|
||||
float a00 = 1f, a01 = 0f, a10 = 0f, a11 = 1f;
|
||||
// We use inat and outat so that in Stroker and Dasher we can work only
|
||||
// with the pre-transformation coordinates. This will repeat a lot of
|
||||
// computations done in the path iterator, but the alternative is to
|
||||
// work with transformed paths and compute untransformed coordinates
|
||||
// as needed. This would be faster but I do not think the complexity
|
||||
// of working with both untransformed and transformed coordinates in
|
||||
// the same code is worth it.
|
||||
// However, if a path's width is constant after a transformation,
|
||||
// we can skip all this untransforming.
|
||||
|
||||
// If normalization is off we save some transformations by not
|
||||
// transforming the input to pisces. Instead, we apply the
|
||||
// transformation after the path processing has been done.
|
||||
// We can't do this if normalization is on, because it isn't a good
|
||||
// idea to normalize before the transformation is applied.
|
||||
AffineTransform inat = null;
|
||||
AffineTransform outat = null;
|
||||
|
||||
PathIterator pi = null;
|
||||
|
||||
if (at != null && !at.isIdentity()) {
|
||||
a00 = (float)at.getScaleX();
|
||||
a01 = (float)at.getShearX();
|
||||
a10 = (float)at.getShearY();
|
||||
a11 = (float)at.getScaleY();
|
||||
}
|
||||
lsink = new Stroker(lsink, width, caps, join, miterlimit, a00, a01, a10, a11);
|
||||
if (dashes != null) {
|
||||
lsink = new Dasher(lsink, dashes, dashphase, a00, a01, a10, a11);
|
||||
}
|
||||
PathIterator pi;
|
||||
if (normalize != NormMode.OFF) {
|
||||
pi = new FlatteningPathIterator(
|
||||
new NormalizingPathIterator(src.getPathIterator(at), normalize),
|
||||
defaultFlat);
|
||||
final double a = at.getScaleX();
|
||||
final double b = at.getShearX();
|
||||
final double c = at.getShearY();
|
||||
final double d = at.getScaleY();
|
||||
final double det = a * d - c * b;
|
||||
if (Math.abs(det) <= 2 * Float.MIN_VALUE) {
|
||||
// this rendering engine takes one dimensional curves and turns
|
||||
// them into 2D shapes by giving them width.
|
||||
// However, if everything is to be passed through a singular
|
||||
// transformation, these 2D shapes will be squashed down to 1D
|
||||
// again so, nothing can be drawn.
|
||||
|
||||
// Every path needs an initial moveTo and a pathDone. If these
|
||||
// aren't there this causes a SIGSEV in libawt.so (at the time
|
||||
// of writing of this comment (September 16, 2010)). Actually,
|
||||
// I'm not sure if the moveTo is necessary to avoid the SIGSEV
|
||||
// but the pathDone is definitely needed.
|
||||
pc2d.moveTo(0, 0);
|
||||
pc2d.pathDone();
|
||||
return;
|
||||
}
|
||||
|
||||
// If the transform is a constant multiple of an orthogonal transformation
|
||||
// then every length is just multiplied by a constant, so we just
|
||||
// need to transform input paths to stroker and tell stroker
|
||||
// the scaled width. This condition is satisfied if
|
||||
// a*b == -c*d && a*a+c*c == b*b+d*d. In the actual check below, we
|
||||
// leave a bit of room for error.
|
||||
if (nearZero(a*b + c*d, 2) && nearZero(a*a+c*c - (b*b+d*d), 2)) {
|
||||
double scale = Math.sqrt(a*a + c*c);
|
||||
if (dashes != null) {
|
||||
dashes = java.util.Arrays.copyOf(dashes, dashes.length);
|
||||
for (int i = 0; i < dashes.length; i++) {
|
||||
dashes[i] = (float)(scale * dashes[i]);
|
||||
}
|
||||
dashphase = (float)(scale * dashphase);
|
||||
}
|
||||
width = (float)(scale * width);
|
||||
pi = src.getPathIterator(at);
|
||||
if (normalize != NormMode.OFF) {
|
||||
pi = new NormalizingPathIterator(pi, normalize);
|
||||
}
|
||||
// leave inat and outat null.
|
||||
} else {
|
||||
// We only need the inverse if normalization is on. Otherwise
|
||||
// we just don't transform the input paths, do all the stroking
|
||||
// and then transform out output (instead of making PathIterator
|
||||
// apply the transformation, us applying the inverse, and then
|
||||
// us applying the transform again to our output).
|
||||
outat = at;
|
||||
if (normalize != NormMode.OFF) {
|
||||
try {
|
||||
inat = outat.createInverse();
|
||||
} catch (NoninvertibleTransformException e) {
|
||||
// we made sure this can't happen
|
||||
e.printStackTrace();
|
||||
}
|
||||
pi = src.getPathIterator(at);
|
||||
pi = new NormalizingPathIterator(pi, normalize);
|
||||
} else {
|
||||
pi = src.getPathIterator(null);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pi = src.getPathIterator(at, defaultFlat);
|
||||
// either at is null or it's the identity. In either case
|
||||
// we don't transform the path.
|
||||
pi = src.getPathIterator(null);
|
||||
if (normalize != NormMode.OFF) {
|
||||
pi = new NormalizingPathIterator(pi, normalize);
|
||||
}
|
||||
}
|
||||
pathTo(pi, lsink);
|
||||
|
||||
pc2d = TransformingPathConsumer2D.transformConsumer(pc2d, outat);
|
||||
pc2d = new Stroker(pc2d, width, caps, join, miterlimit);
|
||||
if (dashes != null) {
|
||||
pc2d = new Dasher(pc2d, dashes, dashphase);
|
||||
}
|
||||
pc2d = TransformingPathConsumer2D.transformConsumer(pc2d, inat);
|
||||
|
||||
pathTo(pi, pc2d);
|
||||
}
|
||||
|
||||
private static boolean nearZero(double num, int nulps) {
|
||||
return Math.abs(num) < nulps * Math.ulp(num);
|
||||
}
|
||||
|
||||
private static class NormalizingPathIterator implements PathIterator {
|
||||
@ -337,10 +414,10 @@ public class PiscesRenderingEngine extends RenderingEngine {
|
||||
}
|
||||
|
||||
// normalize endpoint
|
||||
float x_adjust = (float)Math.floor(coords[lastCoord] + lval) + rval -
|
||||
coords[lastCoord];
|
||||
float y_adjust = (float)Math.floor(coords[lastCoord+1] + lval) + rval -
|
||||
coords[lastCoord + 1];
|
||||
float x_adjust = (float)Math.floor(coords[lastCoord] + lval) +
|
||||
rval - coords[lastCoord];
|
||||
float y_adjust = (float)Math.floor(coords[lastCoord+1] + lval) +
|
||||
rval - coords[lastCoord + 1];
|
||||
|
||||
coords[lastCoord ] += x_adjust;
|
||||
coords[lastCoord + 1] += y_adjust;
|
||||
@ -393,27 +470,9 @@ public class PiscesRenderingEngine extends RenderingEngine {
|
||||
}
|
||||
}
|
||||
|
||||
void pathTo(PathIterator pi, LineSink lsink) {
|
||||
float coords[] = new float[2];
|
||||
while (!pi.isDone()) {
|
||||
switch (pi.currentSegment(coords)) {
|
||||
case PathIterator.SEG_MOVETO:
|
||||
lsink.moveTo(coords[0], coords[1]);
|
||||
break;
|
||||
case PathIterator.SEG_LINETO:
|
||||
lsink.lineJoin();
|
||||
lsink.lineTo(coords[0], coords[1]);
|
||||
break;
|
||||
case PathIterator.SEG_CLOSE:
|
||||
lsink.lineJoin();
|
||||
lsink.close();
|
||||
break;
|
||||
default:
|
||||
throw new InternalError("unknown flattened segment type");
|
||||
}
|
||||
pi.next();
|
||||
}
|
||||
lsink.end();
|
||||
static void pathTo(PathIterator pi, PathConsumer2D pc2d) {
|
||||
RenderingEngine.feedConsumer(pi, pc2d);
|
||||
pc2d.pathDone();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -471,32 +530,29 @@ public class PiscesRenderingEngine extends RenderingEngine {
|
||||
boolean normalize,
|
||||
int bbox[])
|
||||
{
|
||||
PiscesCache pc = PiscesCache.createInstance();
|
||||
Renderer r;
|
||||
NormMode norm = (normalize) ? NormMode.ON_WITH_AA : NormMode.OFF;
|
||||
if (bs == null) {
|
||||
PathIterator pi;
|
||||
if (normalize) {
|
||||
pi = new FlatteningPathIterator(
|
||||
new NormalizingPathIterator(s.getPathIterator(at), norm),
|
||||
defaultFlat);
|
||||
pi = new NormalizingPathIterator(s.getPathIterator(at), norm);
|
||||
} else {
|
||||
pi = s.getPathIterator(at, defaultFlat);
|
||||
pi = s.getPathIterator(at);
|
||||
}
|
||||
r = new Renderer(3, 3,
|
||||
clip.getLoX(), clip.getLoY(),
|
||||
clip.getWidth(), clip.getHeight(),
|
||||
pi.getWindingRule(), pc);
|
||||
pi.getWindingRule());
|
||||
pathTo(pi, r);
|
||||
} else {
|
||||
r = new Renderer(3, 3,
|
||||
clip.getLoX(), clip.getLoY(),
|
||||
clip.getWidth(), clip.getHeight(),
|
||||
PathIterator.WIND_NON_ZERO, pc);
|
||||
PathIterator.WIND_NON_ZERO);
|
||||
strokeTo(s, at, bs, thin, norm, true, r);
|
||||
}
|
||||
r.endRendering();
|
||||
PiscesTileGenerator ptg = new PiscesTileGenerator(pc, r.MAX_AA_ALPHA);
|
||||
PiscesTileGenerator ptg = new PiscesTileGenerator(r, r.MAX_AA_ALPHA);
|
||||
ptg.getBbox(bbox);
|
||||
return ptg;
|
||||
}
|
||||
|
@ -25,40 +25,54 @@
|
||||
|
||||
package sun.java2d.pisces;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import sun.java2d.pipe.AATileGenerator;
|
||||
|
||||
public class PiscesTileGenerator implements AATileGenerator {
|
||||
public static final int TILE_SIZE = 32;
|
||||
public final class PiscesTileGenerator implements AATileGenerator {
|
||||
public static final int TILE_SIZE = PiscesCache.TILE_SIZE;
|
||||
|
||||
// perhaps we should be using weak references here, but right now
|
||||
// that's not necessary. The way the renderer is, this map will
|
||||
// never contain more than one element - the one with key 64, since
|
||||
// we only do 8x8 supersampling.
|
||||
private static final Map<Integer, byte[]> alphaMapsCache = new
|
||||
ConcurrentHashMap<Integer, byte[]>();
|
||||
|
||||
PiscesCache cache;
|
||||
int x, y;
|
||||
int maxalpha;
|
||||
final int maxalpha;
|
||||
private final int maxTileAlphaSum;
|
||||
|
||||
// The alpha map used by this object (taken out of our map cache) to convert
|
||||
// pixel coverage counts gotten from PiscesCache (which are in the range
|
||||
// [0, maxalpha]) into alpha values, which are in [0,256).
|
||||
byte alphaMap[];
|
||||
|
||||
public PiscesTileGenerator(PiscesCache cache, int maxalpha) {
|
||||
this.cache = cache;
|
||||
public PiscesTileGenerator(Renderer r, int maxalpha) {
|
||||
this.cache = r.getCache();
|
||||
this.x = cache.bboxX0;
|
||||
this.y = cache.bboxY0;
|
||||
this.alphaMap = getAlphaMap(maxalpha);
|
||||
this.maxalpha = maxalpha;
|
||||
this.maxTileAlphaSum = TILE_SIZE*TILE_SIZE*maxalpha;
|
||||
}
|
||||
|
||||
static int prevMaxAlpha;
|
||||
static byte prevAlphaMap[];
|
||||
|
||||
public synchronized static byte[] getAlphaMap(int maxalpha) {
|
||||
if (maxalpha != prevMaxAlpha) {
|
||||
prevAlphaMap = new byte[maxalpha+300];
|
||||
int halfmaxalpha = maxalpha>>2;
|
||||
for (int i = 0; i <= maxalpha; i++) {
|
||||
prevAlphaMap[i] = (byte) ((i * 255 + halfmaxalpha) / maxalpha);
|
||||
}
|
||||
for (int i = maxalpha; i < prevAlphaMap.length; i++) {
|
||||
prevAlphaMap[i] = (byte) 255;
|
||||
}
|
||||
prevMaxAlpha = maxalpha;
|
||||
private static byte[] buildAlphaMap(int maxalpha) {
|
||||
byte[] alMap = new byte[maxalpha+1];
|
||||
int halfmaxalpha = maxalpha>>2;
|
||||
for (int i = 0; i <= maxalpha; i++) {
|
||||
alMap[i] = (byte) ((i * 255 + halfmaxalpha) / maxalpha);
|
||||
}
|
||||
return prevAlphaMap;
|
||||
return alMap;
|
||||
}
|
||||
|
||||
public static byte[] getAlphaMap(int maxalpha) {
|
||||
if (!alphaMapsCache.containsKey(maxalpha)) {
|
||||
alphaMapsCache.put(maxalpha, buildAlphaMap(maxalpha));
|
||||
}
|
||||
return alphaMapsCache.get(maxalpha);
|
||||
}
|
||||
|
||||
public void getBbox(int bbox[]) {
|
||||
@ -96,53 +110,24 @@ public class PiscesTileGenerator implements AATileGenerator {
|
||||
* value for partial coverage of the tile
|
||||
*/
|
||||
public int getTypicalAlpha() {
|
||||
if (true) return 0x80;
|
||||
// Decode run-length encoded alpha mask data
|
||||
// The data for row j begins at cache.rowOffsetsRLE[j]
|
||||
// and is encoded as a set of 2-byte pairs (val, runLen)
|
||||
// terminated by a (0, 0) pair.
|
||||
|
||||
int x0 = this.x;
|
||||
int x1 = x0 + TILE_SIZE;
|
||||
int y0 = this.y;
|
||||
int y1 = y0 + TILE_SIZE;
|
||||
if (x1 > cache.bboxX1) x1 = cache.bboxX1;
|
||||
if (y1 > cache.bboxY1) y1 = cache.bboxY1;
|
||||
y0 -= cache.bboxY0;
|
||||
y1 -= cache.bboxY0;
|
||||
|
||||
int ret = -1;
|
||||
for (int cy = y0; cy < y1; cy++) {
|
||||
int pos = cache.rowOffsetsRLE[cy];
|
||||
int cx = cache.minTouched[cy];
|
||||
|
||||
if (cx > x0) {
|
||||
if (ret > 0) return 0x80;
|
||||
ret = 0x00;
|
||||
}
|
||||
while (cx < x1) {
|
||||
int runLen = cache.rowAARLE[pos + 1] & 0xff;
|
||||
if (runLen == 0) {
|
||||
if (ret > 0) return 0x80;
|
||||
ret = 0x00;
|
||||
break;
|
||||
}
|
||||
cx += runLen;
|
||||
if (cx > x0) {
|
||||
int val = cache.rowAARLE[pos] & 0xff;
|
||||
if (ret != val) {
|
||||
if (ret < 0) {
|
||||
if (val != 0x00 && val != maxalpha) return 0x80;
|
||||
ret = val;
|
||||
} else {
|
||||
return 0x80;
|
||||
}
|
||||
}
|
||||
}
|
||||
pos += 2;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
int al = cache.alphaSumInTile(x, y);
|
||||
// Note: if we have a filled rectangle that doesn't end on a tile
|
||||
// border, we could still return 0xff, even though al!=maxTileAlphaSum
|
||||
// This is because if we return 0xff, our users will fill a rectangle
|
||||
// starting at x,y that has width = Math.min(TILE_SIZE, bboxX1-x),
|
||||
// and height min(TILE_SIZE,bboxY1-y), which is what should happen.
|
||||
// However, to support this, we would have to use 2 Math.min's
|
||||
// and 2 multiplications per tile, instead of just 2 multiplications
|
||||
// to compute maxTileAlphaSum. The savings offered would probably
|
||||
// not be worth it, considering how rare this case is.
|
||||
// Note: I have not tested this, so in the future if it is determined
|
||||
// that it is worth it, it should be implemented. Perhaps this method's
|
||||
// interface should be changed to take arguments the width and height
|
||||
// of the current tile. This would eliminate the 2 Math.min calls that
|
||||
// would be needed here, since our caller needs to compute these 2
|
||||
// values anyway.
|
||||
return (al == 0x00 ? 0x00 :
|
||||
(al == maxTileAlphaSum ? 0xff : 0x80));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -179,22 +164,24 @@ public class PiscesTileGenerator implements AATileGenerator {
|
||||
|
||||
int idx = offset;
|
||||
for (int cy = y0; cy < y1; cy++) {
|
||||
int pos = cache.rowOffsetsRLE[cy];
|
||||
int cx = cache.minTouched[cy];
|
||||
int[] row = cache.rowAARLE[cy];
|
||||
assert row != null;
|
||||
int cx = cache.minTouched(cy);
|
||||
if (cx > x1) cx = x1;
|
||||
|
||||
if (cx > x0) {
|
||||
//System.out.println("L["+(cx-x0)+"]");
|
||||
for (int i = x0; i < cx; i++) {
|
||||
tile[idx++] = 0x00;
|
||||
}
|
||||
for (int i = x0; i < cx; i++) {
|
||||
tile[idx++] = 0x00;
|
||||
}
|
||||
while (cx < x1) {
|
||||
|
||||
int pos = 2;
|
||||
while (cx < x1 && pos < row[1]) {
|
||||
byte val;
|
||||
int runLen = 0;
|
||||
assert row[1] > 2;
|
||||
try {
|
||||
val = alphaMap[cache.rowAARLE[pos] & 0xff];
|
||||
runLen = cache.rowAARLE[pos + 1] & 0xff;
|
||||
val = alphaMap[row[pos]];
|
||||
runLen = row[pos + 1];
|
||||
assert runLen > 0;
|
||||
} catch (RuntimeException e0) {
|
||||
System.out.println("maxalpha = "+maxalpha);
|
||||
System.out.println("tile["+x0+", "+y0+
|
||||
@ -202,14 +189,12 @@ public class PiscesTileGenerator implements AATileGenerator {
|
||||
System.out.println("cx = "+cx+", cy = "+cy);
|
||||
System.out.println("idx = "+idx+", pos = "+pos);
|
||||
System.out.println("len = "+runLen);
|
||||
cache.print(System.out);
|
||||
System.out.print(cache.toString());
|
||||
e0.printStackTrace();
|
||||
System.exit(1);
|
||||
return;
|
||||
}
|
||||
if (runLen == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
int rx0 = cx;
|
||||
cx += runLen;
|
||||
int rx1 = cx;
|
||||
@ -228,7 +213,7 @@ public class PiscesTileGenerator implements AATileGenerator {
|
||||
System.out.println("idx = "+idx+", pos = "+pos);
|
||||
System.out.println("rx0 = "+rx0+", rx1 = "+rx1);
|
||||
System.out.println("len = "+runLen);
|
||||
cache.print(System.out);
|
||||
System.out.print(cache.toString());
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
return;
|
||||
@ -265,4 +250,4 @@ public class PiscesTileGenerator implements AATileGenerator {
|
||||
* No further calls will be made on this instance.
|
||||
*/
|
||||
public void dispose() {}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,229 @@
|
||||
/*
|
||||
* Copyright (c) 2007, 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
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package sun.java2d.pisces;
|
||||
|
||||
import sun.awt.geom.PathConsumer2D;
|
||||
import java.awt.geom.AffineTransform;
|
||||
|
||||
public class TransformingPathConsumer2D {
|
||||
public static PathConsumer2D
|
||||
transformConsumer(PathConsumer2D out,
|
||||
AffineTransform at)
|
||||
{
|
||||
if (at == null) {
|
||||
return out;
|
||||
}
|
||||
float Mxx = (float) at.getScaleX();
|
||||
float Mxy = (float) at.getShearX();
|
||||
float Mxt = (float) at.getTranslateX();
|
||||
float Myx = (float) at.getShearY();
|
||||
float Myy = (float) at.getScaleY();
|
||||
float Myt = (float) at.getTranslateY();
|
||||
if (Mxy == 0f && Myx == 0f) {
|
||||
if (Mxx == 1f && Myy == 1f) {
|
||||
if (Mxt == 0f && Myt == 0f) {
|
||||
return out;
|
||||
} else {
|
||||
return new TranslateFilter(out, Mxt, Myt);
|
||||
}
|
||||
} else {
|
||||
return new ScaleFilter(out, Mxx, Myy, Mxt, Myt);
|
||||
}
|
||||
} else {
|
||||
return new TransformFilter(out, Mxx, Mxy, Mxt, Myx, Myy, Myt);
|
||||
}
|
||||
}
|
||||
|
||||
static class TranslateFilter implements PathConsumer2D {
|
||||
PathConsumer2D out;
|
||||
float tx;
|
||||
float ty;
|
||||
|
||||
TranslateFilter(PathConsumer2D out,
|
||||
float tx, float ty)
|
||||
{
|
||||
this.out = out;
|
||||
this.tx = tx;
|
||||
this.ty = ty;
|
||||
}
|
||||
|
||||
public void moveTo(float x0, float y0) {
|
||||
out.moveTo(x0 + tx, y0 + ty);
|
||||
}
|
||||
|
||||
public void lineTo(float x1, float y1) {
|
||||
out.lineTo(x1 + tx, y1 + ty);
|
||||
}
|
||||
|
||||
public void quadTo(float x1, float y1,
|
||||
float x2, float y2)
|
||||
{
|
||||
out.quadTo(x1 + tx, y1 + ty,
|
||||
x2 + tx, y2 + ty);
|
||||
}
|
||||
|
||||
public void curveTo(float x1, float y1,
|
||||
float x2, float y2,
|
||||
float x3, float y3)
|
||||
{
|
||||
out.curveTo(x1 + tx, y1 + ty,
|
||||
x2 + tx, y2 + ty,
|
||||
x3 + tx, y3 + ty);
|
||||
}
|
||||
|
||||
public void closePath() {
|
||||
out.closePath();
|
||||
}
|
||||
|
||||
public void pathDone() {
|
||||
out.pathDone();
|
||||
}
|
||||
|
||||
public long getNativeConsumer() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static class ScaleFilter implements PathConsumer2D {
|
||||
PathConsumer2D out;
|
||||
float sx;
|
||||
float sy;
|
||||
float tx;
|
||||
float ty;
|
||||
|
||||
ScaleFilter(PathConsumer2D out,
|
||||
float sx, float sy, float tx, float ty)
|
||||
{
|
||||
this.out = out;
|
||||
this.sx = sx;
|
||||
this.sy = sy;
|
||||
this.tx = tx;
|
||||
this.ty = ty;
|
||||
}
|
||||
|
||||
public void moveTo(float x0, float y0) {
|
||||
out.moveTo(x0 * sx + tx, y0 * sy + ty);
|
||||
}
|
||||
|
||||
public void lineTo(float x1, float y1) {
|
||||
out.lineTo(x1 * sx + tx, y1 * sy + ty);
|
||||
}
|
||||
|
||||
public void quadTo(float x1, float y1,
|
||||
float x2, float y2)
|
||||
{
|
||||
out.quadTo(x1 * sx + tx, y1 * sy + ty,
|
||||
x2 * sx + tx, y2 * sy + ty);
|
||||
}
|
||||
|
||||
public void curveTo(float x1, float y1,
|
||||
float x2, float y2,
|
||||
float x3, float y3)
|
||||
{
|
||||
out.curveTo(x1 * sx + tx, y1 * sy + ty,
|
||||
x2 * sx + tx, y2 * sy + ty,
|
||||
x3 * sx + tx, y3 * sy + ty);
|
||||
}
|
||||
|
||||
public void closePath() {
|
||||
out.closePath();
|
||||
}
|
||||
|
||||
public void pathDone() {
|
||||
out.pathDone();
|
||||
}
|
||||
|
||||
public long getNativeConsumer() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static class TransformFilter implements PathConsumer2D {
|
||||
PathConsumer2D out;
|
||||
float Mxx;
|
||||
float Mxy;
|
||||
float Mxt;
|
||||
float Myx;
|
||||
float Myy;
|
||||
float Myt;
|
||||
|
||||
TransformFilter(PathConsumer2D out,
|
||||
float Mxx, float Mxy, float Mxt,
|
||||
float Myx, float Myy, float Myt)
|
||||
{
|
||||
this.out = out;
|
||||
this.Mxx = Mxx;
|
||||
this.Mxy = Mxy;
|
||||
this.Mxt = Mxt;
|
||||
this.Myx = Myx;
|
||||
this.Myy = Myy;
|
||||
this.Myt = Myt;
|
||||
}
|
||||
|
||||
public void moveTo(float x0, float y0) {
|
||||
out.moveTo(x0 * Mxx + y0 * Mxy + Mxt,
|
||||
x0 * Myx + y0 * Myy + Myt);
|
||||
}
|
||||
|
||||
public void lineTo(float x1, float y1) {
|
||||
out.lineTo(x1 * Mxx + y1 * Mxy + Mxt,
|
||||
x1 * Myx + y1 * Myy + Myt);
|
||||
}
|
||||
|
||||
public void quadTo(float x1, float y1,
|
||||
float x2, float y2)
|
||||
{
|
||||
out.quadTo(x1 * Mxx + y1 * Mxy + Mxt,
|
||||
x1 * Myx + y1 * Myy + Myt,
|
||||
x2 * Mxx + y2 * Mxy + Mxt,
|
||||
x2 * Myx + y2 * Myy + Myt);
|
||||
}
|
||||
|
||||
public void curveTo(float x1, float y1,
|
||||
float x2, float y2,
|
||||
float x3, float y3)
|
||||
{
|
||||
out.curveTo(x1 * Mxx + y1 * Mxy + Mxt,
|
||||
x1 * Myx + y1 * Myy + Myt,
|
||||
x2 * Mxx + y2 * Mxy + Mxt,
|
||||
x2 * Myx + y2 * Myy + Myt,
|
||||
x3 * Mxx + y3 * Mxy + Mxt,
|
||||
x3 * Myx + y3 * Myy + Myt);
|
||||
}
|
||||
|
||||
public void closePath() {
|
||||
out.closePath();
|
||||
}
|
||||
|
||||
public void pathDone() {
|
||||
out.pathDone();
|
||||
}
|
||||
|
||||
public long getNativeConsumer() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
@ -116,14 +116,26 @@
|
||||
jint Y0 = (fY0) >> MDP_PREC; \
|
||||
jint X1 = (fX1) >> MDP_PREC; \
|
||||
jint Y1 = (fY1) >> MDP_PREC; \
|
||||
/* Handling lines having just one pixel */\
|
||||
if (((X0^X1) | (Y0^Y1)) == 0) { \
|
||||
if (checkBounds && \
|
||||
(hnd->dhnd->yMin > Y0 || \
|
||||
hnd->dhnd->yMax <= Y0 || \
|
||||
hnd->dhnd->xMin > X0 || \
|
||||
hnd->dhnd->xMax <= X0)) break; \
|
||||
jint res; \
|
||||
\
|
||||
/* Checking bounds and clipping if necessary */ \
|
||||
if (checkBounds) { \
|
||||
TESTANDCLIP(hnd->dhnd->yMin, hnd->dhnd->yMax, Y0, X0, Y1, X1, \
|
||||
jint, res); \
|
||||
if (res == CRES_INVISIBLE) break; \
|
||||
TESTANDCLIP(hnd->dhnd->yMin, hnd->dhnd->yMax, Y1, X1, Y0, X0, \
|
||||
jint, res); \
|
||||
if (res == CRES_INVISIBLE) break; \
|
||||
TESTANDCLIP(hnd->dhnd->xMin, hnd->dhnd->xMax, X0, Y0, X1, Y1, \
|
||||
jint, res); \
|
||||
if (res == CRES_INVISIBLE) break; \
|
||||
TESTANDCLIP(hnd->dhnd->xMin, hnd->dhnd->xMax, X1, Y1, X0, Y0, \
|
||||
jint, res); \
|
||||
if (res == CRES_INVISIBLE) break; \
|
||||
} \
|
||||
\
|
||||
/* Handling lines having just one pixel */ \
|
||||
if (((X0^X1) | (Y0^Y1)) == 0) { \
|
||||
if (pixelInfo[0] == 0) { \
|
||||
pixelInfo[0] = 1; \
|
||||
pixelInfo[1] = X0; \
|
||||
@ -140,18 +152,11 @@
|
||||
break; \
|
||||
} \
|
||||
\
|
||||
if (!checkBounds || \
|
||||
(hnd->dhnd->yMin <= Y0 && \
|
||||
hnd->dhnd->yMax > Y0 && \
|
||||
hnd->dhnd->xMin <= X0 && \
|
||||
hnd->dhnd->xMax > X0)) \
|
||||
if (pixelInfo[0] && \
|
||||
((pixelInfo[1] == X0 && pixelInfo[2] == Y0) || \
|
||||
(pixelInfo[3] == X0 && pixelInfo[4] == Y0))) \
|
||||
{ \
|
||||
if (pixelInfo[0] && \
|
||||
((pixelInfo[1] == X0 && pixelInfo[2] == Y0) || \
|
||||
(pixelInfo[3] == X0 && pixelInfo[4] == Y0))) \
|
||||
{ \
|
||||
hnd->dhnd->pDrawPixel(hnd->dhnd, X0, Y0); \
|
||||
} \
|
||||
hnd->dhnd->pDrawPixel(hnd->dhnd, X0, Y0); \
|
||||
} \
|
||||
\
|
||||
hnd->dhnd->pDrawLine(hnd->dhnd, X0, Y0, X1, Y1); \
|
||||
@ -170,14 +175,6 @@
|
||||
if ((pixelInfo[1] == X1 && pixelInfo[2] == Y1) || \
|
||||
(pixelInfo[3] == X1 && pixelInfo[4] == Y1)) \
|
||||
{ \
|
||||
if (checkBounds && \
|
||||
(hnd->dhnd->yMin > Y1 || \
|
||||
hnd->dhnd->yMax <= Y1 || \
|
||||
hnd->dhnd->xMin > X1 || \
|
||||
hnd->dhnd->xMax <= X1)) { \
|
||||
break; \
|
||||
} \
|
||||
\
|
||||
hnd->dhnd->pDrawPixel(hnd->dhnd, X1, Y1); \
|
||||
} \
|
||||
pixelInfo[3] = X1; \
|
||||
|
@ -600,6 +600,7 @@ public class WWindowPeer extends WPanelPeer implements WindowPeer,
|
||||
}
|
||||
|
||||
private native void setOpacity(int iOpacity);
|
||||
private float opacity = 1.0f;
|
||||
|
||||
public void setOpacity(float opacity) {
|
||||
if (!((SunToolkit)((Window)target).getToolkit()).
|
||||
@ -608,7 +609,21 @@ public class WWindowPeer extends WPanelPeer implements WindowPeer,
|
||||
return;
|
||||
}
|
||||
|
||||
replaceSurfaceDataRecursively((Component)getTarget());
|
||||
if (opacity < 0.0f || opacity > 1.0f) {
|
||||
throw new IllegalArgumentException(
|
||||
"The value of opacity should be in the range [0.0f .. 1.0f].");
|
||||
}
|
||||
|
||||
if (((this.opacity == 1.0f && opacity < 1.0f) ||
|
||||
(this.opacity < 1.0f && opacity == 1.0f)) &&
|
||||
!Win32GraphicsEnvironment.isVistaOS())
|
||||
{
|
||||
// non-Vista OS: only replace the surface data if opacity status
|
||||
// changed (see WComponentPeer.isAccelCapable() for more)
|
||||
replaceSurfaceDataRecursively((Component)getTarget());
|
||||
}
|
||||
|
||||
this.opacity = opacity;
|
||||
|
||||
final int maxOpacity = 0xff;
|
||||
int iOpacity = (int)(opacity * maxOpacity);
|
||||
@ -650,7 +665,7 @@ public class WWindowPeer extends WPanelPeer implements WindowPeer,
|
||||
|
||||
boolean isVistaOS = Win32GraphicsEnvironment.isVistaOS();
|
||||
|
||||
if (!isVistaOS) {
|
||||
if (this.isOpaque != isOpaque && !isVistaOS) {
|
||||
// non-Vista OS: only replace the surface data if the opacity
|
||||
// status changed (see WComponentPeer.isAccelCapable() for more)
|
||||
replaceSurfaceDataRecursively(target);
|
||||
|
Loading…
x
Reference in New Issue
Block a user