8180055: Upgrade the Marlin renderer in Java2D

Added the double-precision variant + MarlinFX backports + Improved MarlinTileGenerator + higher precision of the cubic / quadratic curve

Reviewed-by: flar, pnarayanan
This commit is contained in:
Laurent Bourgès 2017-05-17 22:05:11 +02:00
parent 5a0726a47a
commit 80581a0c13
34 changed files with 7695 additions and 894 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 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
@ -242,6 +242,8 @@ public final class ArrayCacheConst implements MarlinConst {
int factor = 1;
if (name.contains("Int") || name.contains("Float")) {
factor = 4;
} else if (name.contains("Double")) {
factor = 8;
}
return factor;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 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
@ -22,6 +22,7 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.marlin;
import static sun.java2d.marlin.ArrayCacheConst.ARRAY_SIZES;
@ -37,13 +38,14 @@ import sun.java2d.marlin.ArrayCacheConst.BucketStats;
import sun.java2d.marlin.ArrayCacheConst.CacheStats;
/*
* Note that the [BYTE/INT/FLOAT]ArrayCache files are nearly identical except
* Note that the [BYTE/INT/FLOAT/DOUBLE]ArrayCache files are nearly identical except
* for a few type and name differences. Typically, the [BYTE]ArrayCache.java file
* is edited manually and then [INT]ArrayCache.java and [FLOAT]ArrayCache.java
* is edited manually and then [INT/FLOAT/DOUBLE]ArrayCache.java
* files are generated with the following command lines:
*/
// % sed -e 's/(b\yte)[ ]*//g' -e 's/b\yte/int/g' -e 's/B\yte/Int/g' < B\yteArrayCache.java > IntArrayCache.java
// % sed -e 's/(b\yte)[ ]*/(float) /g' -e 's/b\yte/float/g' -e 's/B\yte/Float/g' < B\yteArrayCache.java > FloatArrayCache.java
// % sed -e 's/(b\yte)[ ]*0/0.0f/g' -e 's/(b\yte)[ ]*/(float) /g' -e 's/b\yte/float/g' -e 's/B\yte/Float/g' < B\yteArrayCache.java > FloatArrayCache.java
// % sed -e 's/(b\yte)[ ]*0/0.0d/g' -e 's/(b\yte)[ ]*/(double) /g' -e 's/b\yte/double/g' -e 's/B\yte/Double/g' < B\yteArrayCache.java > DoubleArrayCache.java
final class ByteArrayCache implements MarlinConst {
@ -231,8 +233,8 @@ final class ByteArrayCache implements MarlinConst {
if (clean) {
return new byte[length];
}
// use JDK9 Unsafe.allocateUninitializedArray(class, length):
return (byte[]) OffHeapArray.UNSAFE.allocateUninitializedArray(byte.class, length);
// use JDK9 Unsafe.allocateUninitializedArray(class, length):
return (byte[]) OffHeapArray.UNSAFE.allocateUninitializedArray(byte.class, length);
}
static void fill(final byte[] array, final int fromIndex,

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 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
@ -146,7 +146,7 @@ final class CollinearSimplifier implements PathConsumer2D {
private static float getSlope(float x1, float y1, float x2, float y2) {
float dy = y2 - y1;
if (dy == 0f) {
if (dy == 0.0f) {
return (x2 > x1) ? Float.POSITIVE_INFINITY
: Float.NEGATIVE_INFINITY;
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved.
* 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
@ -29,8 +29,6 @@ final class Curve {
float ax, ay, bx, by, cx, cy, dx, dy;
float dax, day, dbx, dby;
// shared iterator instance
private final BreakPtrIterator iterator = new BreakPtrIterator();
Curve() {
}
@ -58,31 +56,31 @@ final class Curve {
float x3, float y3,
float x4, float y4)
{
ax = 3f * (x2 - x3) + x4 - x1;
ay = 3f * (y2 - y3) + y4 - y1;
bx = 3f * (x1 - 2f * x2 + x3);
by = 3f * (y1 - 2f * y2 + y3);
cx = 3f * (x2 - x1);
cy = 3f * (y2 - y1);
ax = 3.0f * (x2 - x3) + x4 - x1;
ay = 3.0f * (y2 - y3) + y4 - y1;
bx = 3.0f * (x1 - 2.0f * x2 + x3);
by = 3.0f * (y1 - 2.0f * y2 + y3);
cx = 3.0f * (x2 - x1);
cy = 3.0f * (y2 - y1);
dx = x1;
dy = y1;
dax = 3f * ax; day = 3f * ay;
dbx = 2f * bx; dby = 2f * by;
dax = 3.0f * ax; day = 3.0f * ay;
dbx = 2.0f * bx; dby = 2.0f * by;
}
void set(float x1, float y1,
float x2, float y2,
float x3, float y3)
{
ax = 0f; ay = 0f;
bx = x1 - 2f * x2 + x3;
by = y1 - 2f * y2 + y3;
cx = 2f * (x2 - x1);
cy = 2f * (y2 - y1);
ax = 0.0f; ay = 0.0f;
bx = x1 - 2.0f * x2 + x3;
by = y1 - 2.0f * y2 + y3;
cx = 2.0f * (x2 - x1);
cy = 2.0f * (y2 - y1);
dx = x1;
dy = y1;
dax = 0f; day = 0f;
dbx = 2f * bx; dby = 2f * by;
dax = 0.0f; day = 0.0f;
dbx = 2.0f * bx; dby = 2.0f * by;
}
float xat(float t) {
@ -113,7 +111,7 @@ final class Curve {
// 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 = 2f * (cy * dax - day * cx);
final float b = 2.0f * (cy * dax - day * cx);
final float c = cy * dbx - cx * dby;
return Helpers.quadraticRoots(a, b, c, pts, off);
@ -128,11 +126,11 @@ final class Curve {
// these are the coefficients of some multiple of g(t) (not g(t),
// because the roots of a polynomial are not changed after multiplication
// by a constant, and this way we save a few multiplications).
final float a = 2f * (dax*dax + day*day);
final float b = 3f * (dax*dbx + day*dby);
final float c = 2f * (dax*cx + day*cy) + dbx*dbx + dby*dby;
final float a = 2.0f * (dax*dax + day*day);
final float b = 3.0f * (dax*dbx + day*dby);
final float c = 2.0f * (dax*cx + day*cy) + dbx*dbx + dby*dby;
final float d = dbx*cx + dby*cy;
return Helpers.cubicRootsInAB(a, b, c, d, pts, off, 0f, 1f);
return Helpers.cubicRootsInAB(a, b, c, d, pts, off, 0.0f, 1.0f);
}
// Tries to find the roots of the function ROC(t)-w in [0, 1). It uses
@ -153,14 +151,14 @@ final class Curve {
assert off <= 6 && roots.length >= 10;
int ret = off;
int numPerpdfddf = perpendiculardfddf(roots, off);
float t0 = 0, ft0 = ROCsq(t0) - w*w;
roots[off + numPerpdfddf] = 1f; // always check interval end points
float t0 = 0.0f, ft0 = ROCsq(t0) - w*w;
roots[off + numPerpdfddf] = 1.0f; // 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) {
if (ft0 == 0.0f) {
roots[ret++] = t0;
} else if (ft1 * ft0 < 0f) { // have opposite signs
} else if (ft1 * ft0 < 0.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);
@ -220,7 +218,7 @@ final class Curve {
private static boolean sameSign(float x, float y) {
// another way is to test if x*y > 0. This is bad for small x, y.
return (x < 0f && y < 0f) || (x > 0f && y > 0f);
return (x < 0.0f && y < 0.0f) || (x > 0.0f && y > 0.0f);
}
// returns the radius of curvature squared at t of this curve
@ -229,76 +227,11 @@ final class Curve {
// dx=xat(t) and dy=yat(t). These calls have been inlined for efficiency
final float dx = t * (t * dax + dbx) + cx;
final float dy = t * (t * day + dby) + cy;
final float ddx = 2f * dax * t + dbx;
final float ddy = 2f * day * t + dby;
final float ddx = 2.0f * dax * t + dbx;
final float ddy = 2.0f * day * t + dby;
final float dx2dy2 = dx*dx + dy*dy;
final float ddx2ddy2 = ddx*ddx + ddy*ddy;
final float ddxdxddydy = ddx*dx + ddy*dy;
return dx2dy2*((dx2dy2*dx2dy2) / (dx2dy2 * ddx2ddy2 - ddxdxddydy*ddxdxddydy));
}
// curve to be broken should be in pts
// this will change the contents of pts but not 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.
BreakPtrIterator breakPtsAtTs(final float[] pts, final int type,
final float[] Ts, final int numTs)
{
assert pts.length >= 2*type && numTs <= Ts.length;
// initialize shared iterator:
iterator.init(pts, type, Ts, numTs);
return iterator;
}
static final class BreakPtrIterator {
private int nextCurveIdx;
private int curCurveOff;
private float prevT;
private float[] pts;
private int type;
private float[] ts;
private int numTs;
void init(final float[] pts, final int type,
final float[] ts, final int numTs) {
this.pts = pts;
this.type = type;
this.ts = ts;
this.numTs = numTs;
nextCurveIdx = 0;
curCurveOff = 0;
prevT = 0f;
}
public boolean hasNext() {
return nextCurveIdx <= numTs;
}
public int next() {
int ret;
if (nextCurveIdx < numTs) {
float curT = ts[nextCurveIdx];
float splitT = (curT - prevT) / (1f - prevT);
Helpers.subdivideAt(splitT,
pts, curCurveOff,
pts, 0,
pts, type, type);
prevT = curT;
ret = 0;
curCurveOff = type;
} else {
ret = curCurveOff;
}
nextCurveIdx++;
return ret;
}
}
}

View File

@ -0,0 +1,154 @@
/*
* Copyright (c) 2015, 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
* 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.marlin;
final class DCollinearSimplifier implements DPathConsumer2D {
enum SimplifierState {
Empty, PreviousPoint, PreviousLine
};
// slope precision threshold
static final double EPS = 1e-4d; // aaime proposed 1e-3d
DPathConsumer2D delegate;
SimplifierState state;
double px1, py1, px2, py2;
double pslope;
DCollinearSimplifier() {
}
public DCollinearSimplifier init(DPathConsumer2D delegate) {
this.delegate = delegate;
this.state = SimplifierState.Empty;
return this; // fluent API
}
@Override
public void pathDone() {
emitStashedLine();
state = SimplifierState.Empty;
delegate.pathDone();
}
@Override
public void closePath() {
emitStashedLine();
state = SimplifierState.Empty;
delegate.closePath();
}
@Override
public long getNativeConsumer() {
return 0;
}
@Override
public void quadTo(double x1, double y1, double x2, double y2) {
emitStashedLine();
delegate.quadTo(x1, y1, x2, y2);
// final end point:
state = SimplifierState.PreviousPoint;
px1 = x2;
py1 = y2;
}
@Override
public void curveTo(double x1, double y1, double x2, double y2,
double x3, double y3) {
emitStashedLine();
delegate.curveTo(x1, y1, x2, y2, x3, y3);
// final end point:
state = SimplifierState.PreviousPoint;
px1 = x3;
py1 = y3;
}
@Override
public void moveTo(double x, double y) {
emitStashedLine();
delegate.moveTo(x, y);
state = SimplifierState.PreviousPoint;
px1 = x;
py1 = y;
}
@Override
public void lineTo(final double x, final double y) {
switch (state) {
case Empty:
delegate.lineTo(x, y);
state = SimplifierState.PreviousPoint;
px1 = x;
py1 = y;
return;
case PreviousPoint:
state = SimplifierState.PreviousLine;
px2 = x;
py2 = y;
pslope = getSlope(px1, py1, x, y);
return;
case PreviousLine:
final double slope = getSlope(px2, py2, x, y);
// test for collinearity
if ((slope == pslope) || (Math.abs(pslope - slope) < EPS)) {
// merge segments
px2 = x;
py2 = y;
return;
}
// emit previous segment
delegate.lineTo(px2, py2);
px1 = px2;
py1 = py2;
px2 = x;
py2 = y;
pslope = slope;
return;
default:
}
}
private void emitStashedLine() {
if (state == SimplifierState.PreviousLine) {
delegate.lineTo(px2, py2);
}
}
private static double getSlope(double x1, double y1, double x2, double y2) {
double dy = y2 - y1;
if (dy == 0.0d) {
return (x2 > x1) ? Double.POSITIVE_INFINITY
: Double.NEGATIVE_INFINITY;
}
return (x2 - x1) / dy;
}
}

View File

@ -0,0 +1,237 @@
/*
* 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
* 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.marlin;
final class DCurve {
double ax, ay, bx, by, cx, cy, dx, dy;
double dax, day, dbx, dby;
DCurve() {
}
void set(double[] points, int type) {
switch(type) {
case 8:
set(points[0], points[1],
points[2], points[3],
points[4], points[5],
points[6], points[7]);
return;
case 6:
set(points[0], points[1],
points[2], points[3],
points[4], points[5]);
return;
default:
throw new InternalError("Curves can only be cubic or quadratic");
}
}
void set(double x1, double y1,
double x2, double y2,
double x3, double y3,
double x4, double y4)
{
ax = 3.0d * (x2 - x3) + x4 - x1;
ay = 3.0d * (y2 - y3) + y4 - y1;
bx = 3.0d * (x1 - 2.0d * x2 + x3);
by = 3.0d * (y1 - 2.0d * y2 + y3);
cx = 3.0d * (x2 - x1);
cy = 3.0d * (y2 - y1);
dx = x1;
dy = y1;
dax = 3.0d * ax; day = 3.0d * ay;
dbx = 2.0d * bx; dby = 2.0d * by;
}
void set(double x1, double y1,
double x2, double y2,
double x3, double y3)
{
ax = 0.0d; ay = 0.0d;
bx = x1 - 2.0d * x2 + x3;
by = y1 - 2.0d * y2 + y3;
cx = 2.0d * (x2 - x1);
cy = 2.0d * (y2 - y1);
dx = x1;
dy = y1;
dax = 0.0d; day = 0.0d;
dbx = 2.0d * bx; dby = 2.0d * by;
}
double xat(double t) {
return t * (t * (t * ax + bx) + cx) + dx;
}
double yat(double t) {
return t * (t * (t * ay + by) + cy) + dy;
}
double dxat(double t) {
return t * (t * dax + dbx) + cx;
}
double dyat(double t) {
return t * (t * day + dby) + cy;
}
int dxRoots(double[] roots, int off) {
return DHelpers.quadraticRoots(dax, dbx, cx, roots, off);
}
int dyRoots(double[] roots, int off) {
return DHelpers.quadraticRoots(day, dby, cy, roots, off);
}
int infPoints(double[] 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 double a = dax * dby - dbx * day;
final double b = 2.0d * (cy * dax - day * cx);
final double c = cy * dbx - cx * dby;
return DHelpers.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(double[] pts, int off) {
assert pts.length >= off + 4;
// these are the coefficients of some multiple of g(t) (not g(t),
// because the roots of a polynomial are not changed after multiplication
// by a constant, and this way we save a few multiplications).
final double a = 2.0d * (dax*dax + day*day);
final double b = 3.0d * (dax*dbx + day*dby);
final double c = 2.0d * (dax*cx + day*cy) + dbx*dbx + dby*dby;
final double d = dbx*cx + dby*cy;
return DHelpers.cubicRootsInAB(a, b, c, d, pts, off, 0.0d, 1.0d);
}
// 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(double[] roots, int off, final double w, final double 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);
double t0 = 0.0d, ft0 = ROCsq(t0) - w*w;
roots[off + numPerpdfddf] = 1.0d; // always check interval end points
numPerpdfddf++;
for (int i = off; i < off + numPerpdfddf; i++) {
double t1 = roots[i], ft1 = ROCsq(t1) - w*w;
if (ft0 == 0.0d) {
roots[ret++] = t0;
} else if (ft1 * ft0 < 0.0d) { // 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 double eliminateInf(double x) {
return (x == Double.POSITIVE_INFINITY ? Double.MAX_VALUE :
(x == Double.NEGATIVE_INFINITY ? Double.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.
// TODO: It is something to consider for java8 (or whenever lambda
// expressions make it into the language), depending on how closures
// and turn out. Same goes for the newton's method
// algorithm in DHelpers.java
private double falsePositionROCsqMinusX(double x0, double x1,
final double x, final double err)
{
final int iterLimit = 100;
int side = 0;
double t = x1, ft = eliminateInf(ROCsq(t) - x);
double s = x0, fs = eliminateInf(ROCsq(s) - x);
double 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 (sameSign(fr, ft)) {
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;
}
private static boolean sameSign(double x, double y) {
// another way is to test if x*y > 0. This is bad for small x, y.
return (x < 0.0d && y < 0.0d) || (x > 0.0d && y > 0.0d);
}
// returns the radius of curvature squared at t of this curve
// see http://en.wikipedia.org/wiki/Radius_of_curvature_(applications)
private double ROCsq(final double t) {
// dx=xat(t) and dy=yat(t). These calls have been inlined for efficiency
final double dx = t * (t * dax + dbx) + cx;
final double dy = t * (t * day + dby) + cy;
final double ddx = 2.0d * dax * t + dbx;
final double ddy = 2.0d * day * t + dby;
final double dx2dy2 = dx*dx + dy*dy;
final double ddx2ddy2 = ddx*ddx + ddy*ddy;
final double ddxdxddydy = ddx*dx + ddy*dy;
return dx2dy2*((dx2dy2*dx2dy2) / (dx2dy2 * ddx2ddy2 - ddxdxddydy*ddxdxddydy));
}
}

View File

@ -0,0 +1,746 @@
/*
* 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
* 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.marlin;
import java.util.Arrays;
/**
* The <code>DDasher</code> class takes a series of linear commands
* (<code>moveTo</code>, <code>lineTo</code>, <code>close</code> and
* <code>end</code>) and breaks them into smaller segments according to a
* dash pattern array and a starting dash phase.
*
* <p> Issues: in J2Se, a zero length dash segment as drawn as a very
* short dash, whereas Pisces does not draw anything. The PostScript
* semantics are unclear.
*
*/
final class DDasher implements DPathConsumer2D, MarlinConst {
static final int REC_LIMIT = 4;
static final double ERR = 0.01d;
static final double MIN_T_INC = 1.0d / (1 << REC_LIMIT);
// More than 24 bits of mantissa means we can no longer accurately
// measure the number of times cycled through the dash array so we
// punt and override the phase to just be 0 past that point.
static final double MAX_CYCLES = 16000000.0d;
private DPathConsumer2D out;
private double[] dash;
private int dashLen;
private double startPhase;
private boolean startDashOn;
private int startIdx;
private boolean starting;
private boolean needsMoveTo;
private int idx;
private boolean dashOn;
private double phase;
private double sx, sy;
private double x0, y0;
// temporary storage for the current curve
private final double[] curCurvepts;
// per-thread renderer context
final DRendererContext rdrCtx;
// flag to recycle dash array copy
boolean recycleDashes;
// dashes ref (dirty)
final DoubleArrayCache.Reference dashes_ref;
// firstSegmentsBuffer ref (dirty)
final DoubleArrayCache.Reference firstSegmentsBuffer_ref;
/**
* Constructs a <code>DDasher</code>.
* @param rdrCtx per-thread renderer context
*/
DDasher(final DRendererContext rdrCtx) {
this.rdrCtx = rdrCtx;
dashes_ref = rdrCtx.newDirtyDoubleArrayRef(INITIAL_ARRAY); // 1K
firstSegmentsBuffer_ref = rdrCtx.newDirtyDoubleArrayRef(INITIAL_ARRAY); // 1K
firstSegmentsBuffer = firstSegmentsBuffer_ref.initial;
// we need curCurvepts to be able to contain 2 curves because when
// dashing curves, we need to subdivide it
curCurvepts = new double[8 * 2];
}
/**
* Initialize the <code>DDasher</code>.
*
* @param out an output <code>DPathConsumer2D</code>.
* @param dash an array of <code>double</code>s containing the dash pattern
* @param dashLen length of the given dash array
* @param phase a <code>double</code> containing the dash phase
* @param recycleDashes true to indicate to recycle the given dash array
* @return this instance
*/
DDasher init(final DPathConsumer2D out, double[] dash, int dashLen,
double phase, boolean recycleDashes)
{
this.out = out;
// Normalize so 0 <= phase < dash[0]
int sidx = 0;
dashOn = true;
double sum = 0.0d;
for (double d : dash) {
sum += d;
}
double cycles = phase / sum;
if (phase < 0.0d) {
if (-cycles >= MAX_CYCLES) {
phase = 0.0d;
} else {
int fullcycles = FloatMath.floor_int(-cycles);
if ((fullcycles & dash.length & 1) != 0) {
dashOn = !dashOn;
}
phase += fullcycles * sum;
while (phase < 0.0d) {
if (--sidx < 0) {
sidx = dash.length - 1;
}
phase += dash[sidx];
dashOn = !dashOn;
}
}
} else if (phase > 0) {
if (cycles >= MAX_CYCLES) {
phase = 0.0d;
} else {
int fullcycles = FloatMath.floor_int(cycles);
if ((fullcycles & dash.length & 1) != 0) {
dashOn = !dashOn;
}
phase -= fullcycles * sum;
double d;
while (phase >= (d = dash[sidx])) {
phase -= d;
sidx = (sidx + 1) % dash.length;
dashOn = !dashOn;
}
}
}
this.dash = dash;
this.dashLen = dashLen;
this.startPhase = this.phase = phase;
this.startDashOn = dashOn;
this.startIdx = sidx;
this.starting = true;
needsMoveTo = false;
firstSegidx = 0;
this.recycleDashes = recycleDashes;
return this; // fluent API
}
/**
* Disposes this dasher:
* clean up before reusing this instance
*/
void dispose() {
if (DO_CLEAN_DIRTY) {
// Force zero-fill dirty arrays:
Arrays.fill(curCurvepts, 0.0d);
}
// Return arrays:
if (recycleDashes) {
dash = dashes_ref.putArray(dash);
}
firstSegmentsBuffer = firstSegmentsBuffer_ref.putArray(firstSegmentsBuffer);
}
double[] copyDashArray(final float[] dashes) {
final int len = dashes.length;
final double[] newDashes;
if (len <= MarlinConst.INITIAL_ARRAY) {
newDashes = dashes_ref.initial;
} else {
if (DO_STATS) {
rdrCtx.stats.stat_array_dasher_dasher.add(len);
}
newDashes = dashes_ref.getArray(len);
}
for (int i = 0; i < len; i++) { newDashes[i] = dashes[i]; }
return newDashes;
}
@Override
public void moveTo(double x0, double y0) {
if (firstSegidx > 0) {
out.moveTo(sx, sy);
emitFirstSegments();
}
needsMoveTo = true;
this.idx = startIdx;
this.dashOn = this.startDashOn;
this.phase = this.startPhase;
this.sx = this.x0 = x0;
this.sy = this.y0 = y0;
this.starting = true;
}
private void emitSeg(double[] 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]);
return;
case 6:
out.quadTo(buf[off+0], buf[off+1],
buf[off+2], buf[off+3]);
return;
case 4:
out.lineTo(buf[off], buf[off+1]);
return;
default:
}
}
private void emitFirstSegments() {
final double[] fSegBuf = firstSegmentsBuffer;
for (int i = 0; i < firstSegidx; ) {
int type = (int)fSegBuf[i];
emitSeg(fSegBuf, i + 1, type);
i += (type - 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 double[] firstSegmentsBuffer; // dynamic array
private int firstSegidx;
// precondition: pts must be in relative coordinates (relative to x0,y0)
private void goTo(double[] pts, int off, final int type) {
double x = pts[off + type - 4];
double y = pts[off + type - 3];
if (dashOn) {
if (starting) {
int len = type - 1; // - 2 + 1
int segIdx = firstSegidx;
double[] buf = firstSegmentsBuffer;
if (segIdx + len > buf.length) {
if (DO_STATS) {
rdrCtx.stats.stat_array_dasher_firstSegmentsBuffer
.add(segIdx + len);
}
firstSegmentsBuffer = buf
= firstSegmentsBuffer_ref.widenArray(buf, segIdx,
segIdx + len);
}
buf[segIdx++] = type;
len--;
// small arraycopy (2, 4 or 6) but with offset:
System.arraycopy(pts, off, buf, segIdx, len);
segIdx += len;
firstSegidx = segIdx;
} else {
if (needsMoveTo) {
out.moveTo(x0, y0);
needsMoveTo = false;
}
emitSeg(pts, off, type);
}
} else {
starting = false;
needsMoveTo = true;
}
this.x0 = x;
this.y0 = y;
}
@Override
public void lineTo(double x1, double y1) {
double dx = x1 - x0;
double dy = y1 - y0;
double len = dx*dx + dy*dy;
if (len == 0.0d) {
return;
}
len = Math.sqrt(len);
// The scaling factors needed to get the dx and dy of the
// transformed dash segments.
final double cx = dx / len;
final double cy = dy / len;
final double[] _curCurvepts = curCurvepts;
final double[] _dash = dash;
double leftInThisDashSegment;
double dashdx, dashdy, p;
while (true) {
leftInThisDashSegment = _dash[idx] - phase;
if (len <= leftInThisDashSegment) {
_curCurvepts[0] = x1;
_curCurvepts[1] = y1;
goTo(_curCurvepts, 0, 4);
// Advance phase within current dash segment
phase += len;
// TODO: compare double values using epsilon:
if (len == leftInThisDashSegment) {
phase = 0.0d;
idx = (idx + 1) % dashLen;
dashOn = !dashOn;
}
return;
}
dashdx = _dash[idx] * cx;
dashdy = _dash[idx] * cy;
if (phase == 0.0d) {
_curCurvepts[0] = x0 + dashdx;
_curCurvepts[1] = y0 + dashdy;
} else {
p = leftInThisDashSegment / _dash[idx];
_curCurvepts[0] = x0 + p * dashdx;
_curCurvepts[1] = y0 + p * dashdy;
}
goTo(_curCurvepts, 0, 4);
len -= leftInThisDashSegment;
// Advance to next dash segment
idx = (idx + 1) % dashLen;
dashOn = !dashOn;
phase = 0.0d;
}
}
// shared instance in DDasher
private final LengthIterator li = new LengthIterator();
// 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;
}
li.initializeIterationOnCurve(curCurvepts, type);
// initially the current curve is at curCurvepts[0...type]
int curCurveoff = 0;
double lastSplitT = 0.0d;
double t;
double leftInThisDashSegment = dash[idx] - phase;
while ((t = li.next(leftInThisDashSegment)) < 1.0d) {
if (t != 0.0d) {
DHelpers.subdivideAt((t - lastSplitT) / (1.0d - lastSplitT),
curCurvepts, curCurveoff,
curCurvepts, 0,
curCurvepts, type, type);
lastSplitT = t;
goTo(curCurvepts, 2, type);
curCurveoff = type;
}
// Advance to next dash segment
idx = (idx + 1) % dashLen;
dashOn = !dashOn;
phase = 0.0d;
leftInThisDashSegment = dash[idx];
}
goTo(curCurvepts, curCurveoff+2, type);
phase += li.lastSegLen();
if (phase >= dash[idx]) {
phase = 0.0d;
idx = (idx + 1) % dashLen;
dashOn = !dashOn;
}
// reset LengthIterator:
li.reset();
}
private static boolean pointCurve(double[] 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
static final 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 final double[][] recCurveStack; // dirty
// 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 final Side[] sides; // dirty
private int curveType;
// lastT and nextT delimit the current leaf.
private double nextT;
private double lenAtNextT;
private double lastT;
private double lenAtLastT;
private double lenAtLastSplit;
private double lastSegLen;
// the current level in the recursion tree. 0 is the root. limit
// is the deepest possible leaf.
private int recLevel;
private boolean done;
// the lengths of the lines of the control polygon. Only its first
// curveType/2 - 1 elements are valid. This is an optimization. See
// next() for more detail.
private final double[] curLeafCtrlPolyLengths = new double[3];
LengthIterator() {
this.recCurveStack = new double[REC_LIMIT + 1][8];
this.sides = new Side[REC_LIMIT];
// if any methods are called without first initializing this object
// on a curve, we want it to fail ASAP.
this.nextT = Double.MAX_VALUE;
this.lenAtNextT = Double.MAX_VALUE;
this.lenAtLastSplit = Double.MIN_VALUE;
this.recLevel = Integer.MIN_VALUE;
this.lastSegLen = Double.MAX_VALUE;
this.done = true;
}
/**
* Reset this LengthIterator.
*/
void reset() {
// keep data dirty
// as it appears not useful to reset data:
if (DO_CLEAN_DIRTY) {
final int recLimit = recCurveStack.length - 1;
for (int i = recLimit; i >= 0; i--) {
Arrays.fill(recCurveStack[i], 0.0d);
}
Arrays.fill(sides, Side.LEFT);
Arrays.fill(curLeafCtrlPolyLengths, 0.0d);
Arrays.fill(nextRoots, 0.0d);
Arrays.fill(flatLeafCoefCache, 0.0d);
flatLeafCoefCache[2] = -1.0d;
}
}
void initializeIterationOnCurve(double[] pts, int type) {
// optimize arraycopy (8 values faster than 6 = type):
System.arraycopy(pts, 0, recCurveStack[0], 0, 8);
this.curveType = type;
this.recLevel = 0;
this.lastT = 0.0d;
this.lenAtLastT = 0.0d;
this.nextT = 0.0d;
this.lenAtNextT = 0.0d;
goLeft(); // initializes nextT and lenAtNextT properly
this.lenAtLastSplit = 0.0d;
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.0d;
}
// 0 == false, 1 == true, -1 == invalid cached value.
private int cachedHaveLowAcceleration = -1;
private boolean haveLowAcceleration(double err) {
if (cachedHaveLowAcceleration == -1) {
final double len1 = curLeafCtrlPolyLengths[0];
final double len2 = curLeafCtrlPolyLengths[1];
// the test below is equivalent to !within(len1/len2, 1, err).
// It is using a multiplication instead of a division, so it
// should be a bit faster.
if (!DHelpers.within(len1, len2, err * len2)) {
cachedHaveLowAcceleration = 0;
return false;
}
if (curveType == 8) {
final double len3 = curLeafCtrlPolyLengths[2];
// if len1 is close to 2 and 2 is close to 3, that probably
// means 1 is close to 3 so the second part of this test might
// not be needed, but it doesn't hurt to include it.
final double errLen3 = err * len3;
if (!(DHelpers.within(len2, len3, errLen3) &&
DHelpers.within(len1, len3, errLen3))) {
cachedHaveLowAcceleration = 0;
return false;
}
}
cachedHaveLowAcceleration = 1;
return true;
}
return (cachedHaveLowAcceleration == 1);
}
// we want to avoid allocations/gc so we keep this array so we
// can put roots in it,
private final double[] nextRoots = new double[4];
// caches the coefficients of the current leaf in its flattened
// form (see inside next() for what that means). The cache is
// invalid when it's third element is negative, since in any
// valid flattened curve, this would be >= 0.
private final double[] flatLeafCoefCache = new double[]{0.0d, 0.0d, -1.0d, 0.0d};
// 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.
double next(final double len) {
final double targetLength = lenAtLastSplit + len;
while (lenAtNextT < targetLength) {
if (done) {
lastSegLen = lenAtNextT - lenAtLastSplit;
return 1.0d;
}
goToNextLeaf();
}
lenAtLastSplit = targetLength;
final double leaflen = lenAtNextT - lenAtLastT;
double t = (targetLength - lenAtLastT) / leaflen;
// cubicRootsInAB is a fairly expensive call, so we just don't do it
// if the acceleration in this section of the curve is small enough.
if (!haveLowAcceleration(0.05d)) {
// We flatten the current leaf along the x axis, so that we're
// left with a, b, c which define a 1D Bezier curve. We then
// solve this to get the parameter of the original leaf that
// gives us the desired length.
final double[] _flatLeafCoefCache = flatLeafCoefCache;
if (_flatLeafCoefCache[2] < 0.0d) {
double x = curLeafCtrlPolyLengths[0],
y = x + curLeafCtrlPolyLengths[1];
if (curveType == 8) {
double z = y + curLeafCtrlPolyLengths[2];
_flatLeafCoefCache[0] = 3.0d * (x - y) + z;
_flatLeafCoefCache[1] = 3.0d * (y - 2.0d * x);
_flatLeafCoefCache[2] = 3.0d * x;
_flatLeafCoefCache[3] = -z;
} else if (curveType == 6) {
_flatLeafCoefCache[0] = 0.0d;
_flatLeafCoefCache[1] = y - 2.0d * x;
_flatLeafCoefCache[2] = 2.0d * x;
_flatLeafCoefCache[3] = -y;
}
}
double a = _flatLeafCoefCache[0];
double b = _flatLeafCoefCache[1];
double c = _flatLeafCoefCache[2];
double d = t * _flatLeafCoefCache[3];
// we use cubicRootsInAB here, because we want only roots in 0, 1,
// and our quadratic root finder doesn't filter, so it's just a
// matter of convenience.
int n = DHelpers.cubicRootsInAB(a, b, c, d, nextRoots, 0, 0.0d, 1.0d);
if (n == 1 && !Double.isNaN(nextRoots[0])) {
t = nextRoots[0];
}
}
// 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.0d) {
t = 1.0d;
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;
}
double lastSegLen() {
return lastSegLen;
}
// 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.
int _recLevel = recLevel;
final Side[] _sides = sides;
_recLevel--;
while(_sides[_recLevel] == Side.RIGHT) {
if (_recLevel == 0) {
recLevel = 0;
done = true;
return;
}
_recLevel--;
}
_sides[_recLevel] = Side.RIGHT;
// optimize arraycopy (8 values faster than 6 = type):
System.arraycopy(recCurveStack[_recLevel], 0,
recCurveStack[_recLevel+1], 0, 8);
_recLevel++;
recLevel = _recLevel;
goLeft();
}
// go to the leftmost node from the current node. Return its length.
private void goLeft() {
double len = onLeaf();
if (len >= 0.0d) {
lastT = nextT;
lenAtLastT = lenAtNextT;
nextT += (1 << (REC_LIMIT - recLevel)) * MIN_T_INC;
lenAtNextT += len;
// invalidate caches
flatLeafCoefCache[2] = -1.0d;
cachedHaveLowAcceleration = -1;
} else {
DHelpers.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 double onLeaf() {
double[] curve = recCurveStack[recLevel];
double polyLen = 0.0d;
double x0 = curve[0], y0 = curve[1];
for (int i = 2; i < curveType; i += 2) {
final double x1 = curve[i], y1 = curve[i+1];
final double len = DHelpers.linelen(x0, y0, x1, y1);
polyLen += len;
curLeafCtrlPolyLengths[i/2 - 1] = len;
x0 = x1;
y0 = y1;
}
final double lineLen = DHelpers.linelen(curve[0], curve[1],
curve[curveType-2],
curve[curveType-1]);
if ((polyLen - lineLen) < ERR || recLevel == REC_LIMIT) {
return (polyLen + lineLen) / 2.0d;
}
return -1.0d;
}
}
@Override
public void curveTo(double x1, double y1,
double x2, double y2,
double x3, double y3)
{
final double[] _curCurvepts = curCurvepts;
_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(double x1, double y1, double x2, double y2) {
final double[] _curCurvepts = curCurvepts;
_curCurvepts[0] = x0; _curCurvepts[1] = y0;
_curCurvepts[2] = x1; _curCurvepts[3] = y1;
_curCurvepts[4] = x2; _curCurvepts[5] = y2;
somethingTo(6);
}
@Override
public void closePath() {
lineTo(sx, sy);
if (firstSegidx > 0) {
if (!dashOn || needsMoveTo) {
out.moveTo(sx, sy);
}
emitFirstSegments();
}
moveTo(sx, sy);
}
@Override
public void pathDone() {
if (firstSegidx > 0) {
out.moveTo(sx, sy);
emitFirstSegments();
}
out.pathDone();
// Dispose this instance:
dispose();
}
@Override
public long getNativeConsumer() {
throw new InternalError("DDasher does not use a native consumer");
}
}

View File

@ -0,0 +1,436 @@
/*
* 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
* 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.marlin;
import static java.lang.Math.PI;
import static java.lang.Math.cos;
import static java.lang.Math.sqrt;
import static java.lang.Math.cbrt;
import static java.lang.Math.acos;
final class DHelpers implements MarlinConst {
private DHelpers() {
throw new Error("This is a non instantiable class");
}
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 double a, final double b,
final double c, double[] zeroes, final int off)
{
int ret = off;
double t;
if (a != 0.0d) {
final double dis = b*b - 4*a*c;
if (dis > 0.0d) {
final double sqrtDis = 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.0d) {
zeroes[ret++] = (2.0d * c) / (-b - sqrtDis);
zeroes[ret++] = (-b - sqrtDis) / (2.0d * a);
} else {
zeroes[ret++] = (-b + sqrtDis) / (2.0d * a);
zeroes[ret++] = (2.0d * c) / (-b + sqrtDis);
}
} else if (dis == 0.0d) {
t = (-b) / (2.0d * a);
zeroes[ret++] = t;
}
} else {
if (b != 0.0d) {
t = (-c) / b;
zeroes[ret++] = t;
}
}
return ret - off;
}
// find the roots of g(t) = d*t^3 + a*t^2 + b*t + c in [A,B)
static int cubicRootsInAB(double d, double a, double b, double c,
double[] pts, final int off,
final double A, final double B)
{
if (d == 0.0d) {
int num = quadraticRoots(a, b, c, pts, off);
return filterOutNotInAB(pts, off, num, A, B) - off;
}
// From Graphics Gems:
// http://tog.acm.org/resources/GraphicsGems/gems/Roots3And4.c
// (also from awt.geom.CubicCurve2D. But here we don't need as
// much accuracy and we don't want to create arrays so we use
// our own customized version).
// normal form: x^3 + ax^2 + bx + c = 0
a /= d;
b /= d;
c /= d;
// substitute x = y - A/3 to eliminate quadratic term:
// x^3 +Px + Q = 0
//
// Since we actually need P/3 and Q/2 for all of the
// calculations that follow, we will calculate
// p = P/3
// q = Q/2
// instead and use those values for simplicity of the code.
double sq_A = a * a;
double p = (1.0d/3.0d) * ((-1.0d/3.0d) * sq_A + b);
double q = (1.0d/2.0d) * ((2.0d/27.0d) * a * sq_A - (1.0d/3.0d) * a * b + c);
// use Cardano's formula
double cb_p = p * p * p;
double D = q * q + cb_p;
int num;
if (D < 0.0d) {
// see: http://en.wikipedia.org/wiki/Cubic_function#Trigonometric_.28and_hyperbolic.29_method
final double phi = (1.0d/3.0d) * acos(-q / sqrt(-cb_p));
final double t = 2.0d * sqrt(-p);
pts[ off+0 ] = ( t * cos(phi));
pts[ off+1 ] = (-t * cos(phi + (PI / 3.0d)));
pts[ off+2 ] = (-t * cos(phi - (PI / 3.0d)));
num = 3;
} else {
final double sqrt_D = sqrt(D);
final double u = cbrt(sqrt_D - q);
final double v = - cbrt(sqrt_D + q);
pts[ off ] = (u + v);
num = 1;
if (within(D, 0.0d, 1e-8d)) {
pts[off+1] = -(pts[off] / 2.0d);
num = 2;
}
}
final double sub = (1.0d/3.0d) * a;
for (int i = 0; i < num; ++i) {
pts[ off+i ] -= sub;
}
return filterOutNotInAB(pts, off, num, A, B) - off;
}
static double evalCubic(final double a, final double b,
final double c, final double d,
final double t)
{
return t * (t * (t * a + b) + c) + d;
}
static double evalQuad(final double a, final double b,
final double c, final double t)
{
return t * (t * a + b) + c;
}
// returns the index 1 past the last valid element remaining after filtering
static int filterOutNotInAB(double[] nums, final int off, final int len,
final double a, final double b)
{
int ret = off;
for (int i = off, end = off + len; i < end; i++) {
if (nums[i] >= a && nums[i] < b) {
nums[ret++] = nums[i];
}
}
return ret;
}
static double polyLineLength(double[] poly, final int off, final int nCoords) {
assert nCoords % 2 == 0 && poly.length >= off + nCoords : "";
double acc = 0.0d;
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 double linelen(double x1, double y1, double x2, double y2) {
final double dx = x2 - x1;
final double dy = y2 - y1;
return Math.sqrt(dx*dx + dy*dy);
}
static void subdivide(double[] src, int srcoff, double[] left, int leftoff,
double[] right, int rightoff, int type)
{
switch(type) {
case 6:
DHelpers.subdivideQuad(src, srcoff, left, leftoff, right, rightoff);
return;
case 8:
DHelpers.subdivideCubic(src, srcoff, left, leftoff, right, rightoff);
return;
default:
throw new InternalError("Unsupported curve type");
}
}
static void isort(double[] a, int off, int len) {
for (int i = off + 1, end = off + len; i < end; i++) {
double 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
// both single and double precision variants 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>&nbsp;+&nbsp;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(double[] src, int srcoff,
double[] left, int leftoff,
double[] right, int rightoff)
{
double x1 = src[srcoff + 0];
double y1 = src[srcoff + 1];
double ctrlx1 = src[srcoff + 2];
double ctrly1 = src[srcoff + 3];
double ctrlx2 = src[srcoff + 4];
double ctrly2 = src[srcoff + 5];
double x2 = src[srcoff + 6];
double 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.0d;
y1 = (y1 + ctrly1) / 2.0d;
x2 = (x2 + ctrlx2) / 2.0d;
y2 = (y2 + ctrly2) / 2.0d;
double centerx = (ctrlx1 + ctrlx2) / 2.0d;
double centery = (ctrly1 + ctrly2) / 2.0d;
ctrlx1 = (x1 + centerx) / 2.0d;
ctrly1 = (y1 + centery) / 2.0d;
ctrlx2 = (x2 + centerx) / 2.0d;
ctrly2 = (y2 + centery) / 2.0d;
centerx = (ctrlx1 + ctrlx2) / 2.0d;
centery = (ctrly1 + ctrly2) / 2.0d;
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(double t, double[] src, int srcoff,
double[] left, int leftoff,
double[] right, int rightoff)
{
double x1 = src[srcoff + 0];
double y1 = src[srcoff + 1];
double ctrlx1 = src[srcoff + 2];
double ctrly1 = src[srcoff + 3];
double ctrlx2 = src[srcoff + 4];
double ctrly2 = src[srcoff + 5];
double x2 = src[srcoff + 6];
double 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);
double centerx = ctrlx1 + t * (ctrlx2 - ctrlx1);
double 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(double[] src, int srcoff,
double[] left, int leftoff,
double[] right, int rightoff)
{
double x1 = src[srcoff + 0];
double y1 = src[srcoff + 1];
double ctrlx = src[srcoff + 2];
double ctrly = src[srcoff + 3];
double x2 = src[srcoff + 4];
double 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.0d;
y1 = (y1 + ctrly) / 2.0d;
x2 = (x2 + ctrlx) / 2.0d;
y2 = (y2 + ctrly) / 2.0d;
ctrlx = (x1 + x2) / 2.0d;
ctrly = (y1 + y2) / 2.0d;
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(double t, double[] src, int srcoff,
double[] left, int leftoff,
double[] right, int rightoff)
{
double x1 = src[srcoff + 0];
double y1 = src[srcoff + 1];
double ctrlx = src[srcoff + 2];
double ctrly = src[srcoff + 3];
double x2 = src[srcoff + 4];
double 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(double t, double[] src, int srcoff,
double[] left, int leftoff,
double[] right, int rightoff, int size)
{
switch(size) {
case 8:
subdivideCubicAt(t, src, srcoff, left, leftoff, right, rightoff);
return;
case 6:
subdivideQuadAt(t, src, srcoff, left, leftoff, right, rightoff);
return;
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,78 @@
/*
* 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
* 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.marlin;
public interface DPathConsumer2D {
/**
* @see java.awt.geom.Path2D.Float#moveTo
*/
public void moveTo(double x, double y);
/**
* @see java.awt.geom.Path2D.Float#lineTo
*/
public void lineTo(double x, double y);
/**
* @see java.awt.geom.Path2D.Float#quadTo
*/
public void quadTo(double x1, double y1,
double x2, double y2);
/**
* @see java.awt.geom.Path2D.Float#curveTo
*/
public void curveTo(double x1, double y1,
double x2, double y2,
double x3, double y3);
/**
* @see java.awt.geom.Path2D.Float#closePath
*/
public void closePath();
/**
* Called after the last segment of the last subpath when the
* iteration of the path segments is completely done. This
* method serves to trigger the end of path processing in the
* consumer that would normally be triggered when a
* {@link java.awt.geom.PathIterator PathIterator}
* returns {@code true} from its {@code done} method.
*/
public void pathDone();
/**
* If a given PathConsumer performs all or most of its work
* natively then it can return a (non-zero) pointer to a
* native function vector that defines C functions for all
* of the above methods.
* The specific pointer it returns is a pointer to a
* PathConsumerVec structure as defined in the include file
* src/share/native/sun/java2d/pipe/PathConsumer2D.h
* @return a native pointer to a PathConsumerVec structure.
*/
public long getNativeConsumer();
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,260 @@
/*
* Copyright (c) 2015, 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
* 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.marlin;
import java.awt.geom.Path2D;
import java.lang.ref.WeakReference;
import java.util.concurrent.atomic.AtomicInteger;
import sun.java2d.ReentrantContext;
import sun.java2d.marlin.ArrayCacheConst.CacheStats;
import sun.java2d.marlin.DMarlinRenderingEngine.NormalizingPathIterator;
/**
* This class is a renderer context dedicated to a single thread
*/
final class DRendererContext extends ReentrantContext implements IRendererContext {
// RendererContext creation counter
private static final AtomicInteger CTX_COUNT = new AtomicInteger(1);
/**
* Create a new renderer context
*
* @return new RendererContext instance
*/
static DRendererContext createContext() {
return new DRendererContext("ctx"
+ Integer.toString(CTX_COUNT.getAndIncrement()));
}
// Smallest object used as Cleaner's parent reference
private final Object cleanerObj;
// dirty flag indicating an exception occured during pipeline in pathTo()
boolean dirty = false;
// shared data
final double[] double6 = new double[6];
// shared curve (dirty) (Renderer / Stroker)
final DCurve curve = new DCurve();
// MarlinRenderingEngine NormalizingPathIterator NearestPixelCenter:
final NormalizingPathIterator nPCPathIterator;
// MarlinRenderingEngine NearestPixelQuarter NormalizingPathIterator:
final NormalizingPathIterator nPQPathIterator;
// MarlinRenderingEngine.TransformingPathConsumer2D
final DTransformingPathConsumer2D transformerPC2D;
// recycled Path2D instance (weak)
private WeakReference<Path2D.Double> refPath2D = null;
final DRenderer renderer;
final DStroker stroker;
// Simplifies out collinear lines
final DCollinearSimplifier simplifier = new DCollinearSimplifier();
final DDasher dasher;
final MarlinTileGenerator ptg;
final MarlinCache cache;
// flag indicating the shape is stroked (1) or filled (0)
int stroking = 0;
// Array caches:
/* clean int[] cache (zero-filled) = 5 refs */
private final IntArrayCache cleanIntCache = new IntArrayCache(true, 5);
/* dirty int[] cache = 4 refs */
private final IntArrayCache dirtyIntCache = new IntArrayCache(false, 4);
/* dirty double[] cache = 3 refs */
private final DoubleArrayCache dirtyDoubleCache = new DoubleArrayCache(false, 3);
/* dirty byte[] cache = 1 ref */
private final ByteArrayCache dirtyByteCache = new ByteArrayCache(false, 1);
// RendererContext statistics
final RendererStats stats;
final PathConsumer2DAdapter p2dAdapter = new PathConsumer2DAdapter();
/**
* Constructor
*
* @param name context name (debugging)
*/
DRendererContext(final String name) {
if (LOG_CREATE_CONTEXT) {
MarlinUtils.logInfo("new RendererContext = " + name);
}
this.cleanerObj = new Object();
// create first stats (needed by newOffHeapArray):
if (DO_STATS || DO_MONITORS) {
stats = RendererStats.createInstance(cleanerObj, name);
// push cache stats:
stats.cacheStats = new CacheStats[] { cleanIntCache.stats,
dirtyIntCache.stats, dirtyDoubleCache.stats, dirtyByteCache.stats
};
} else {
stats = null;
}
// NormalizingPathIterator instances:
nPCPathIterator = new NormalizingPathIterator.NearestPixelCenter(double6);
nPQPathIterator = new NormalizingPathIterator.NearestPixelQuarter(double6);
// MarlinRenderingEngine.TransformingPathConsumer2D
transformerPC2D = new DTransformingPathConsumer2D();
// Renderer:
cache = new MarlinCache(this);
renderer = new DRenderer(this); // needs MarlinCache from rdrCtx.cache
ptg = new MarlinTileGenerator(stats, renderer, cache);
stroker = new DStroker(this);
dasher = new DDasher(this);
}
/**
* Disposes this renderer context:
* clean up before reusing this context
*/
void dispose() {
if (DO_STATS) {
if (stats.totalOffHeap > stats.totalOffHeapMax) {
stats.totalOffHeapMax = stats.totalOffHeap;
}
stats.totalOffHeap = 0L;
}
stroking = 0;
// if context is maked as DIRTY:
if (dirty) {
// may happen if an exception if thrown in the pipeline processing:
// force cleanup of all possible pipelined blocks (except Renderer):
// NormalizingPathIterator instances:
this.nPCPathIterator.dispose();
this.nPQPathIterator.dispose();
// Dasher:
this.dasher.dispose();
// Stroker:
this.stroker.dispose();
// mark context as CLEAN:
dirty = false;
}
}
Path2D.Double getPath2D() {
// resolve reference:
Path2D.Double p2d
= (refPath2D != null) ? refPath2D.get() : null;
// create a new Path2D ?
if (p2d == null) {
p2d = new Path2D.Double(Path2D.WIND_NON_ZERO, INITIAL_EDGES_COUNT); // 32K
// update weak reference:
refPath2D = new WeakReference<Path2D.Double>(p2d);
}
// reset the path anyway:
p2d.reset();
return p2d;
}
@Override
public RendererStats stats() {
return stats;
}
@Override
public OffHeapArray newOffHeapArray(final long initialSize) {
if (DO_STATS) {
stats.totalOffHeapInitial += initialSize;
}
return new OffHeapArray(cleanerObj, initialSize);
}
@Override
public IntArrayCache.Reference newCleanIntArrayRef(final int initialSize) {
return cleanIntCache.createRef(initialSize);
}
IntArrayCache.Reference newDirtyIntArrayRef(final int initialSize) {
return dirtyIntCache.createRef(initialSize);
}
DoubleArrayCache.Reference newDirtyDoubleArrayRef(final int initialSize) {
return dirtyDoubleCache.createRef(initialSize);
}
ByteArrayCache.Reference newDirtyByteArrayRef(final int initialSize) {
return dirtyByteCache.createRef(initialSize);
}
static final class PathConsumer2DAdapter implements DPathConsumer2D {
private sun.awt.geom.PathConsumer2D out;
PathConsumer2DAdapter() {}
PathConsumer2DAdapter init(sun.awt.geom.PathConsumer2D out) {
this.out = out;
return this;
}
@Override
public void moveTo(double x0, double y0) {
out.moveTo((float)x0, (float)y0);
}
@Override
public void lineTo(double x1, double y1) {
out.lineTo((float)x1, (float)y1);
}
@Override
public void closePath() {
out.closePath();
}
@Override
public void pathDone() {
out.pathDone();
}
@Override
public void curveTo(double x1, double y1,
double x2, double y2,
double x3, double y3)
{
out.curveTo((float)x1, (float)y1,
(float)x2, (float)y2,
(float)x3, (float)y3);
}
@Override
public void quadTo(double x1, double y1, double x2, double y2) {
out.quadTo((float)x1, (float)y1, (float)x2, (float)y2);
}
@Override
public long getNativeConsumer() {
throw new InternalError("Not using a native peer");
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,277 @@
/*
* 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
* 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.marlin;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
final class DTransformingPathConsumer2D {
DTransformingPathConsumer2D() {
// used by DRendererContext
}
// recycled DPathConsumer2D instance from wrapPath2d()
private final Path2DWrapper wp_Path2DWrapper = new Path2DWrapper();
DPathConsumer2D wrapPath2d(Path2D.Double p2d)
{
return wp_Path2DWrapper.init(p2d);
}
// recycled DPathConsumer2D instances from deltaTransformConsumer()
private final DeltaScaleFilter dt_DeltaScaleFilter = new DeltaScaleFilter();
private final DeltaTransformFilter dt_DeltaTransformFilter = new DeltaTransformFilter();
DPathConsumer2D deltaTransformConsumer(DPathConsumer2D out,
AffineTransform at)
{
if (at == null) {
return out;
}
double mxx = at.getScaleX();
double mxy = at.getShearX();
double myx = at.getShearY();
double myy = at.getScaleY();
if (mxy == 0.0d && myx == 0.0d) {
if (mxx == 1.0d && myy == 1.0d) {
return out;
} else {
return dt_DeltaScaleFilter.init(out, mxx, myy);
}
} else {
return dt_DeltaTransformFilter.init(out, mxx, mxy, myx, myy);
}
}
// recycled DPathConsumer2D instances from inverseDeltaTransformConsumer()
private final DeltaScaleFilter iv_DeltaScaleFilter = new DeltaScaleFilter();
private final DeltaTransformFilter iv_DeltaTransformFilter = new DeltaTransformFilter();
DPathConsumer2D inverseDeltaTransformConsumer(DPathConsumer2D out,
AffineTransform at)
{
if (at == null) {
return out;
}
double mxx = at.getScaleX();
double mxy = at.getShearX();
double myx = at.getShearY();
double myy = at.getScaleY();
if (mxy == 0.0d && myx == 0.0d) {
if (mxx == 1.0d && myy == 1.0d) {
return out;
} else {
return iv_DeltaScaleFilter.init(out, 1.0d/mxx, 1.0d/myy);
}
} else {
double det = mxx * myy - mxy * myx;
return iv_DeltaTransformFilter.init(out,
myy / det,
-mxy / det,
-myx / det,
mxx / det);
}
}
static final class DeltaScaleFilter implements DPathConsumer2D {
private DPathConsumer2D out;
private double sx, sy;
DeltaScaleFilter() {}
DeltaScaleFilter init(DPathConsumer2D out,
double mxx, double myy)
{
this.out = out;
sx = mxx;
sy = myy;
return this; // fluent API
}
@Override
public void moveTo(double x0, double y0) {
out.moveTo(x0 * sx, y0 * sy);
}
@Override
public void lineTo(double x1, double y1) {
out.lineTo(x1 * sx, y1 * sy);
}
@Override
public void quadTo(double x1, double y1,
double x2, double y2)
{
out.quadTo(x1 * sx, y1 * sy,
x2 * sx, y2 * sy);
}
@Override
public void curveTo(double x1, double y1,
double x2, double y2,
double x3, double y3)
{
out.curveTo(x1 * sx, y1 * sy,
x2 * sx, y2 * sy,
x3 * sx, y3 * sy);
}
@Override
public void closePath() {
out.closePath();
}
@Override
public void pathDone() {
out.pathDone();
}
@Override
public long getNativeConsumer() {
return 0;
}
}
static final class DeltaTransformFilter implements DPathConsumer2D {
private DPathConsumer2D out;
private double mxx, mxy, myx, myy;
DeltaTransformFilter() {}
DeltaTransformFilter init(DPathConsumer2D out,
double mxx, double mxy,
double myx, double myy)
{
this.out = out;
this.mxx = mxx;
this.mxy = mxy;
this.myx = myx;
this.myy = myy;
return this; // fluent API
}
@Override
public void moveTo(double x0, double y0) {
out.moveTo(x0 * mxx + y0 * mxy,
x0 * myx + y0 * myy);
}
@Override
public void lineTo(double x1, double y1) {
out.lineTo(x1 * mxx + y1 * mxy,
x1 * myx + y1 * myy);
}
@Override
public void quadTo(double x1, double y1,
double x2, double y2)
{
out.quadTo(x1 * mxx + y1 * mxy,
x1 * myx + y1 * myy,
x2 * mxx + y2 * mxy,
x2 * myx + y2 * myy);
}
@Override
public void curveTo(double x1, double y1,
double x2, double y2,
double x3, double y3)
{
out.curveTo(x1 * mxx + y1 * mxy,
x1 * myx + y1 * myy,
x2 * mxx + y2 * mxy,
x2 * myx + y2 * myy,
x3 * mxx + y3 * mxy,
x3 * myx + y3 * myy);
}
@Override
public void closePath() {
out.closePath();
}
@Override
public void pathDone() {
out.pathDone();
}
@Override
public long getNativeConsumer() {
return 0;
}
}
static final class Path2DWrapper implements DPathConsumer2D {
private Path2D.Double p2d;
Path2DWrapper() {}
Path2DWrapper init(Path2D.Double p2d) {
this.p2d = p2d;
return this;
}
@Override
public void moveTo(double x0, double y0) {
p2d.moveTo(x0, y0);
}
@Override
public void lineTo(double x1, double y1) {
p2d.lineTo(x1, y1);
}
@Override
public void closePath() {
p2d.closePath();
}
@Override
public void pathDone() {}
@Override
public void curveTo(double x1, double y1,
double x2, double y2,
double x3, double y3)
{
p2d.curveTo(x1, y1, x2, y2, x3, y3);
}
@Override
public void quadTo(double x1, double y1, double x2, double y2) {
p2d.quadTo(x1, y1, x2, y2);
}
@Override
public long getNativeConsumer() {
throw new InternalError("Not using a native peer");
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved.
* 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
@ -39,11 +39,16 @@ import sun.awt.geom.PathConsumer2D;
* semantics are unclear.
*
*/
final class Dasher implements sun.awt.geom.PathConsumer2D, MarlinConst {
final class Dasher implements PathConsumer2D, MarlinConst {
static final int REC_LIMIT = 4;
static final float ERR = 0.01f;
static final float MIN_T_INC = 1f / (1 << REC_LIMIT);
static final float MIN_T_INC = 1.0f / (1 << REC_LIMIT);
// More than 24 bits of mantissa means we can no longer accurately
// measure the number of times cycled through the dash array so we
// punt and override the phase to just be 0 past that point.
static final float MAX_CYCLES = 16000000.0f;
private PathConsumer2D out;
private float[] dash;
@ -106,26 +111,56 @@ final class Dasher implements sun.awt.geom.PathConsumer2D, MarlinConst {
Dasher init(final PathConsumer2D out, float[] dash, int dashLen,
float phase, boolean recycleDashes)
{
if (phase < 0f) {
throw new IllegalArgumentException("phase < 0 !");
}
this.out = out;
// Normalize so 0 <= phase < dash[0]
int idx = 0;
int sidx = 0;
dashOn = true;
float d;
while (phase >= (d = dash[idx])) {
phase -= d;
idx = (idx + 1) % dashLen;
dashOn = !dashOn;
float sum = 0.0f;
for (float d : dash) {
sum += d;
}
float cycles = phase / sum;
if (phase < 0.0f) {
if (-cycles >= MAX_CYCLES) {
phase = 0.0f;
} else {
int fullcycles = FloatMath.floor_int(-cycles);
if ((fullcycles & dash.length & 1) != 0) {
dashOn = !dashOn;
}
phase += fullcycles * sum;
while (phase < 0.0f) {
if (--sidx < 0) {
sidx = dash.length - 1;
}
phase += dash[sidx];
dashOn = !dashOn;
}
}
} else if (phase > 0) {
if (cycles >= MAX_CYCLES) {
phase = 0.0f;
} else {
int fullcycles = FloatMath.floor_int(cycles);
if ((fullcycles & dash.length & 1) != 0) {
dashOn = !dashOn;
}
phase -= fullcycles * sum;
float d;
while (phase >= (d = dash[sidx])) {
phase -= d;
sidx = (sidx + 1) % dash.length;
dashOn = !dashOn;
}
}
}
this.dash = dash;
this.dashLen = dashLen;
this.startPhase = this.phase = phase;
this.startDashOn = dashOn;
this.startIdx = idx;
this.startIdx = sidx;
this.starting = true;
needsMoveTo = false;
firstSegidx = 0;
@ -142,7 +177,7 @@ final class Dasher implements sun.awt.geom.PathConsumer2D, MarlinConst {
void dispose() {
if (DO_CLEAN_DIRTY) {
// Force zero-fill dirty arrays:
Arrays.fill(curCurvepts, 0f);
Arrays.fill(curCurvepts, 0.0f);
}
// Return arrays:
if (recycleDashes) {
@ -151,6 +186,21 @@ final class Dasher implements sun.awt.geom.PathConsumer2D, MarlinConst {
firstSegmentsBuffer = firstSegmentsBuffer_ref.putArray(firstSegmentsBuffer);
}
float[] copyDashArray(final float[] dashes) {
final int len = dashes.length;
final float[] newDashes;
if (len <= MarlinConst.INITIAL_ARRAY) {
newDashes = dashes_ref.initial;
} else {
if (DO_STATS) {
rdrCtx.stats.stat_array_dasher_dasher.add(len);
}
newDashes = dashes_ref.getArray(len);
}
System.arraycopy(dashes, 0, newDashes, 0, len);
return newDashes;
}
@Override
public void moveTo(float x0, float y0) {
if (firstSegidx > 0) {
@ -202,13 +252,12 @@ final class Dasher implements sun.awt.geom.PathConsumer2D, MarlinConst {
private int firstSegidx;
// 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) {
int len = type - 2 + 1;
int len = type - 1; // - 2 + 1
int segIdx = firstSegidx;
float[] buf = firstSegmentsBuffer;
if (segIdx + len > buf.length) {
@ -247,7 +296,7 @@ final class Dasher implements sun.awt.geom.PathConsumer2D, MarlinConst {
float dy = y1 - y0;
float len = dx*dx + dy*dy;
if (len == 0f) {
if (len == 0.0f) {
return;
}
len = (float) Math.sqrt(len);
@ -275,7 +324,7 @@ final class Dasher implements sun.awt.geom.PathConsumer2D, MarlinConst {
phase += len;
// TODO: compare float values using epsilon:
if (len == leftInThisDashSegment) {
phase = 0f;
phase = 0.0f;
idx = (idx + 1) % dashLen;
dashOn = !dashOn;
}
@ -285,7 +334,7 @@ final class Dasher implements sun.awt.geom.PathConsumer2D, MarlinConst {
dashdx = _dash[idx] * cx;
dashdy = _dash[idx] * cy;
if (phase == 0f) {
if (phase == 0.0f) {
_curCurvepts[0] = x0 + dashdx;
_curCurvepts[1] = y0 + dashdy;
} else {
@ -300,7 +349,7 @@ final class Dasher implements sun.awt.geom.PathConsumer2D, MarlinConst {
// Advance to next dash segment
idx = (idx + 1) % dashLen;
dashOn = !dashOn;
phase = 0f;
phase = 0.0f;
}
}
@ -317,13 +366,13 @@ final class Dasher implements sun.awt.geom.PathConsumer2D, MarlinConst {
// initially the current curve is at curCurvepts[0...type]
int curCurveoff = 0;
float lastSplitT = 0f;
float lastSplitT = 0.0f;
float t;
float leftInThisDashSegment = dash[idx] - phase;
while ((t = li.next(leftInThisDashSegment)) < 1f) {
if (t != 0f) {
Helpers.subdivideAt((t - lastSplitT) / (1f - lastSplitT),
while ((t = li.next(leftInThisDashSegment)) < 1.0f) {
if (t != 0.0f) {
Helpers.subdivideAt((t - lastSplitT) / (1.0f - lastSplitT),
curCurvepts, curCurveoff,
curCurvepts, 0,
curCurvepts, type, type);
@ -334,13 +383,13 @@ final class Dasher implements sun.awt.geom.PathConsumer2D, MarlinConst {
// Advance to next dash segment
idx = (idx + 1) % dashLen;
dashOn = !dashOn;
phase = 0f;
phase = 0.0f;
leftInThisDashSegment = dash[idx];
}
goTo(curCurvepts, curCurveoff+2, type);
phase += li.lastSegLen();
if (phase >= dash[idx]) {
phase = 0f;
phase = 0.0f;
idx = (idx + 1) % dashLen;
dashOn = !dashOn;
}
@ -395,7 +444,7 @@ final class Dasher implements sun.awt.geom.PathConsumer2D, MarlinConst {
// the lengths of the lines of the control polygon. Only its first
// curveType/2 - 1 elements are valid. This is an optimization. See
// next(float) for more detail.
// next() for more detail.
private final float[] curLeafCtrlPolyLengths = new float[3];
LengthIterator() {
@ -420,13 +469,13 @@ final class Dasher implements sun.awt.geom.PathConsumer2D, MarlinConst {
if (DO_CLEAN_DIRTY) {
final int recLimit = recCurveStack.length - 1;
for (int i = recLimit; i >= 0; i--) {
Arrays.fill(recCurveStack[i], 0f);
Arrays.fill(recCurveStack[i], 0.0f);
}
Arrays.fill(sides, Side.LEFT);
Arrays.fill(curLeafCtrlPolyLengths, 0f);
Arrays.fill(nextRoots, 0f);
Arrays.fill(flatLeafCoefCache, 0f);
flatLeafCoefCache[2] = -1f;
Arrays.fill(curLeafCtrlPolyLengths, 0.0f);
Arrays.fill(nextRoots, 0.0f);
Arrays.fill(flatLeafCoefCache, 0.0f);
flatLeafCoefCache[2] = -1.0f;
}
}
@ -435,12 +484,12 @@ final class Dasher implements sun.awt.geom.PathConsumer2D, MarlinConst {
System.arraycopy(pts, 0, recCurveStack[0], 0, 8);
this.curveType = type;
this.recLevel = 0;
this.lastT = 0f;
this.lenAtLastT = 0f;
this.nextT = 0f;
this.lenAtNextT = 0f;
this.lastT = 0.0f;
this.lenAtLastT = 0.0f;
this.nextT = 0.0f;
this.lenAtNextT = 0.0f;
goLeft(); // initializes nextT and lenAtNextT properly
this.lenAtLastSplit = 0f;
this.lenAtLastSplit = 0.0f;
if (recLevel > 0) {
this.sides[0] = Side.LEFT;
this.done = false;
@ -449,7 +498,7 @@ final class Dasher implements sun.awt.geom.PathConsumer2D, MarlinConst {
this.sides[0] = Side.RIGHT;
this.done = true;
}
this.lastSegLen = 0f;
this.lastSegLen = 0.0f;
}
// 0 == false, 1 == true, -1 == invalid cached value.
@ -462,7 +511,7 @@ final class Dasher implements sun.awt.geom.PathConsumer2D, MarlinConst {
// the test below is equivalent to !within(len1/len2, 1, err).
// It is using a multiplication instead of a division, so it
// should be a bit faster.
if (!Helpers.within(len1, len2, err*len2)) {
if (!Helpers.within(len1, len2, err * len2)) {
cachedHaveLowAcceleration = 0;
return false;
}
@ -493,7 +542,7 @@ final class Dasher implements sun.awt.geom.PathConsumer2D, MarlinConst {
// form (see inside next() for what that means). The cache is
// invalid when it's third element is negative, since in any
// valid flattened curve, this would be >= 0.
private final float[] flatLeafCoefCache = new float[]{0f, 0f, -1f, 0f};
private final float[] flatLeafCoefCache = new float[]{0.0f, 0.0f, -1.0f, 0.0f};
// returns the t value where the remaining curve should be split in
// order for the left subdivided curve to have length len. If len
@ -503,7 +552,7 @@ final class Dasher implements sun.awt.geom.PathConsumer2D, MarlinConst {
while (lenAtNextT < targetLength) {
if (done) {
lastSegLen = lenAtNextT - lenAtLastSplit;
return 1f;
return 1.0f;
}
goToNextLeaf();
}
@ -520,19 +569,19 @@ final class Dasher implements sun.awt.geom.PathConsumer2D, MarlinConst {
// gives us the desired length.
final float[] _flatLeafCoefCache = flatLeafCoefCache;
if (_flatLeafCoefCache[2] < 0) {
float x = 0f + curLeafCtrlPolyLengths[0],
y = x + curLeafCtrlPolyLengths[1];
if (_flatLeafCoefCache[2] < 0.0f) {
float x = curLeafCtrlPolyLengths[0],
y = x + curLeafCtrlPolyLengths[1];
if (curveType == 8) {
float z = y + curLeafCtrlPolyLengths[2];
_flatLeafCoefCache[0] = 3f * (x - y) + z;
_flatLeafCoefCache[1] = 3f * (y - 2f * x);
_flatLeafCoefCache[2] = 3f * x;
_flatLeafCoefCache[0] = 3.0f * (x - y) + z;
_flatLeafCoefCache[1] = 3.0f * (y - 2.0f * x);
_flatLeafCoefCache[2] = 3.0f * x;
_flatLeafCoefCache[3] = -z;
} else if (curveType == 6) {
_flatLeafCoefCache[0] = 0f;
_flatLeafCoefCache[1] = y - 2f * x;
_flatLeafCoefCache[2] = 2f * x;
_flatLeafCoefCache[0] = 0.0f;
_flatLeafCoefCache[1] = y - 2.0f * x;
_flatLeafCoefCache[2] = 2.0f * x;
_flatLeafCoefCache[3] = -y;
}
}
@ -544,7 +593,7 @@ final class Dasher implements sun.awt.geom.PathConsumer2D, MarlinConst {
// we use cubicRootsInAB here, because we want only roots in 0, 1,
// and our quadratic root finder doesn't filter, so it's just a
// matter of convenience.
int n = Helpers.cubicRootsInAB(a, b, c, d, nextRoots, 0, 0, 1);
int n = Helpers.cubicRootsInAB(a, b, c, d, nextRoots, 0, 0.0f, 1.0f);
if (n == 1 && !Float.isNaN(nextRoots[0])) {
t = nextRoots[0];
}
@ -552,8 +601,8 @@ final class Dasher implements sun.awt.geom.PathConsumer2D, MarlinConst {
// 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 >= 1f) {
t = 1f;
if (t >= 1.0f) {
t = 1.0f;
done = true;
}
// even if done = true, if we're here, that means targetLength
@ -600,13 +649,13 @@ final class Dasher implements sun.awt.geom.PathConsumer2D, MarlinConst {
// go to the leftmost node from the current node. Return its length.
private void goLeft() {
float len = onLeaf();
if (len >= 0f) {
if (len >= 0.0f) {
lastT = nextT;
lenAtLastT = lenAtNextT;
nextT += (1 << (REC_LIMIT - recLevel)) * MIN_T_INC;
lenAtNextT += len;
// invalidate caches
flatLeafCoefCache[2] = -1f;
flatLeafCoefCache[2] = -1.0f;
cachedHaveLowAcceleration = -1;
} else {
Helpers.subdivide(recCurveStack[recLevel], 0,
@ -622,7 +671,7 @@ final class Dasher implements sun.awt.geom.PathConsumer2D, MarlinConst {
// the length of the leaf if we are on a leaf.
private float onLeaf() {
float[] curve = recCurveStack[recLevel];
float polyLen = 0f;
float polyLen = 0.0f;
float x0 = curve[0], y0 = curve[1];
for (int i = 2; i < curveType; i += 2) {
@ -638,9 +687,9 @@ final class Dasher implements sun.awt.geom.PathConsumer2D, MarlinConst {
curve[curveType-2],
curve[curveType-1]);
if ((polyLen - lineLen) < ERR || recLevel == REC_LIMIT) {
return (polyLen + lineLen) / 2f;
return (polyLen + lineLen) / 2.0f;
}
return -1f;
return -1.0f;
}
}

View File

@ -0,0 +1,273 @@
/*
* Copyright (c) 2015, 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
* 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.marlin;
import static sun.java2d.marlin.ArrayCacheConst.ARRAY_SIZES;
import static sun.java2d.marlin.ArrayCacheConst.BUCKETS;
import static sun.java2d.marlin.ArrayCacheConst.MAX_ARRAY_SIZE;
import static sun.java2d.marlin.MarlinUtils.logInfo;
import static sun.java2d.marlin.MarlinUtils.logException;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import sun.java2d.marlin.ArrayCacheConst.BucketStats;
import sun.java2d.marlin.ArrayCacheConst.CacheStats;
/*
* Note that the [BYTE/INT/FLOAT/DOUBLE]ArrayCache files are nearly identical except
* for a few type and name differences. Typically, the [BYTE]ArrayCache.java file
* is edited manually and then [INT/FLOAT/DOUBLE]ArrayCache.java
* files are generated with the following command lines:
*/
// % sed -e 's/(b\yte)[ ]*//g' -e 's/b\yte/int/g' -e 's/B\yte/Int/g' < B\yteArrayCache.java > IntArrayCache.java
// % sed -e 's/(b\yte)[ ]*0/0.0f/g' -e 's/(b\yte)[ ]*/(float) /g' -e 's/b\yte/float/g' -e 's/B\yte/Float/g' < B\yteArrayCache.java > FloatArrayCache.java
// % sed -e 's/(b\yte)[ ]*0/0.0d/g' -e 's/(b\yte)[ ]*/(double) /g' -e 's/b\yte/double/g' -e 's/B\yte/Double/g' < B\yteArrayCache.java > DoubleArrayCache.java
final class DoubleArrayCache implements MarlinConst {
final boolean clean;
private final int bucketCapacity;
private WeakReference<Bucket[]> refBuckets = null;
final CacheStats stats;
DoubleArrayCache(final boolean clean, final int bucketCapacity) {
this.clean = clean;
this.bucketCapacity = bucketCapacity;
this.stats = (DO_STATS) ?
new CacheStats(getLogPrefix(clean) + "DoubleArrayCache") : null;
}
Bucket getCacheBucket(final int length) {
final int bucket = ArrayCacheConst.getBucket(length);
return getBuckets()[bucket];
}
private Bucket[] getBuckets() {
// resolve reference:
Bucket[] buckets = (refBuckets != null) ? refBuckets.get() : null;
// create a new buckets ?
if (buckets == null) {
buckets = new Bucket[BUCKETS];
for (int i = 0; i < BUCKETS; i++) {
buckets[i] = new Bucket(clean, ARRAY_SIZES[i], bucketCapacity,
(DO_STATS) ? stats.bucketStats[i] : null);
}
// update weak reference:
refBuckets = new WeakReference<Bucket[]>(buckets);
}
return buckets;
}
Reference createRef(final int initialSize) {
return new Reference(this, initialSize);
}
static final class Reference {
// initial array reference (direct access)
final double[] initial;
private final boolean clean;
private final DoubleArrayCache cache;
Reference(final DoubleArrayCache cache, final int initialSize) {
this.cache = cache;
this.clean = cache.clean;
this.initial = createArray(initialSize, clean);
if (DO_STATS) {
cache.stats.totalInitial += initialSize;
}
}
double[] getArray(final int length) {
if (length <= MAX_ARRAY_SIZE) {
return cache.getCacheBucket(length).getArray();
}
if (DO_STATS) {
cache.stats.oversize++;
}
if (DO_LOG_OVERSIZE) {
logInfo(getLogPrefix(clean) + "DoubleArrayCache: "
+ "getArray[oversize]: length=\t" + length);
}
return createArray(length, clean);
}
double[] widenArray(final double[] array, final int usedSize,
final int needSize)
{
final int length = array.length;
if (DO_CHECKS && length >= needSize) {
return array;
}
if (DO_STATS) {
cache.stats.resize++;
}
// maybe change bucket:
// ensure getNewSize() > newSize:
final double[] res = getArray(ArrayCacheConst.getNewSize(usedSize, needSize));
// use wrapper to ensure proper copy:
System.arraycopy(array, 0, res, 0, usedSize); // copy only used elements
// maybe return current array:
putArray(array, 0, usedSize); // ensure array is cleared
if (DO_LOG_WIDEN_ARRAY) {
logInfo(getLogPrefix(clean) + "DoubleArrayCache: "
+ "widenArray[" + res.length
+ "]: usedSize=\t" + usedSize + "\tlength=\t" + length
+ "\tneeded length=\t" + needSize);
}
return res;
}
double[] putArray(final double[] array)
{
// dirty array helper:
return putArray(array, 0, array.length);
}
double[] putArray(final double[] array, final int fromIndex,
final int toIndex)
{
if (array.length <= MAX_ARRAY_SIZE) {
if ((clean || DO_CLEAN_DIRTY) && (toIndex != 0)) {
// clean-up array of dirty part[fromIndex; toIndex[
fill(array, fromIndex, toIndex, 0.0d);
}
// ensure to never store initial arrays in cache:
if (array != initial) {
cache.getCacheBucket(array.length).putArray(array);
}
}
return initial;
}
}
static final class Bucket {
private int tail = 0;
private final int arraySize;
private final boolean clean;
private final double[][] arrays;
private final BucketStats stats;
Bucket(final boolean clean, final int arraySize,
final int capacity, final BucketStats stats)
{
this.arraySize = arraySize;
this.clean = clean;
this.stats = stats;
this.arrays = new double[capacity][];
}
double[] getArray() {
if (DO_STATS) {
stats.getOp++;
}
// use cache:
if (tail != 0) {
final double[] array = arrays[--tail];
arrays[tail] = null;
return array;
}
if (DO_STATS) {
stats.createOp++;
}
return createArray(arraySize, clean);
}
void putArray(final double[] array)
{
if (DO_CHECKS && (array.length != arraySize)) {
logInfo(getLogPrefix(clean) + "DoubleArrayCache: "
+ "bad length = " + array.length);
return;
}
if (DO_STATS) {
stats.returnOp++;
}
// fill cache:
if (arrays.length > tail) {
arrays[tail++] = array;
if (DO_STATS) {
stats.updateMaxSize(tail);
}
} else if (DO_CHECKS) {
logInfo(getLogPrefix(clean) + "DoubleArrayCache: "
+ "array capacity exceeded !");
}
}
}
static double[] createArray(final int length, final boolean clean) {
if (clean) {
return new double[length];
}
// use JDK9 Unsafe.allocateUninitializedArray(class, length):
return (double[]) OffHeapArray.UNSAFE.allocateUninitializedArray(double.class, length);
}
static void fill(final double[] array, final int fromIndex,
final int toIndex, final double value)
{
// clear array data:
Arrays.fill(array, fromIndex, toIndex, value);
if (DO_CHECKS) {
check(array, fromIndex, toIndex, value);
}
}
static void check(final double[] array, final int fromIndex,
final int toIndex, final double value)
{
if (DO_CHECKS) {
// check zero on full array:
for (int i = 0; i < array.length; i++) {
if (array[i] != value) {
logException("Invalid value at: " + i + " = " + array[i]
+ " from: " + fromIndex + " to: " + toIndex + "\n"
+ Arrays.toString(array), new Throwable());
// ensure array is correctly filled:
Arrays.fill(array, value);
return;
}
}
}
}
static String getLogPrefix(final boolean clean) {
return (clean) ? "Clean" : "Dirty";
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 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
@ -22,6 +22,7 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.marlin;
import static sun.java2d.marlin.ArrayCacheConst.ARRAY_SIZES;
@ -37,13 +38,14 @@ import sun.java2d.marlin.ArrayCacheConst.BucketStats;
import sun.java2d.marlin.ArrayCacheConst.CacheStats;
/*
* Note that the [BYTE/INT/FLOAT]ArrayCache files are nearly identical except
* Note that the [BYTE/INT/FLOAT/DOUBLE]ArrayCache files are nearly identical except
* for a few type and name differences. Typically, the [BYTE]ArrayCache.java file
* is edited manually and then [INT]ArrayCache.java and [FLOAT]ArrayCache.java
* is edited manually and then [INT/FLOAT/DOUBLE]ArrayCache.java
* files are generated with the following command lines:
*/
// % sed -e 's/(b\yte)[ ]*//g' -e 's/b\yte/int/g' -e 's/B\yte/Int/g' < B\yteArrayCache.java > IntArrayCache.java
// % sed -e 's/(b\yte)[ ]*/(float) /g' -e 's/b\yte/float/g' -e 's/B\yte/Float/g' < B\yteArrayCache.java > FloatArrayCache.java
// % sed -e 's/(b\yte)[ ]*0/0.0f/g' -e 's/(b\yte)[ ]*/(float) /g' -e 's/b\yte/float/g' -e 's/B\yte/Float/g' < B\yteArrayCache.java > FloatArrayCache.java
// % sed -e 's/(b\yte)[ ]*0/0.0d/g' -e 's/(b\yte)[ ]*/(double) /g' -e 's/b\yte/double/g' -e 's/B\yte/Double/g' < B\yteArrayCache.java > DoubleArrayCache.java
final class FloatArrayCache implements MarlinConst {
@ -159,7 +161,7 @@ final class FloatArrayCache implements MarlinConst {
if (array.length <= MAX_ARRAY_SIZE) {
if ((clean || DO_CLEAN_DIRTY) && (toIndex != 0)) {
// clean-up array of dirty part[fromIndex; toIndex[
fill(array, fromIndex, toIndex, (float) 0);
fill(array, fromIndex, toIndex, 0.0f);
}
// ensure to never store initial arrays in cache:
if (array != initial) {
@ -231,8 +233,8 @@ final class FloatArrayCache implements MarlinConst {
if (clean) {
return new float[length];
}
// use JDK9 Unsafe.allocateUninitializedArray(class, length):
return (float[]) OffHeapArray.UNSAFE.allocateUninitializedArray(float.class, length);
// use JDK9 Unsafe.allocateUninitializedArray(class, length):
return (float[]) OffHeapArray.UNSAFE.allocateUninitializedArray(float.class, length);
}
static void fill(final float[] array, final int fromIndex,

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 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
@ -22,9 +22,8 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.marlin;
import jdk.internal.math.FloatConsts;
package sun.java2d.marlin;
/**
* Faster Math ceil / floor routines derived from StrictMath
@ -34,17 +33,17 @@ public final class FloatMath implements MarlinConst {
// overflow / NaN handling enabled:
static final boolean CHECK_OVERFLOW = true;
static final boolean CHECK_NAN = true;
// Copied from sun.misc.FloatConsts:
public static final int FLOAT_SIGNIFICAND_WIDTH = 24; // sun.misc.FloatConsts.SIGNIFICAND_WIDTH
public static final int FLOAT_EXP_BIAS = 127; // sun.misc.FloatConsts.EXP_BIAS
public static final int FLOAT_EXP_BIT_MASK = 2139095040;// sun.misc.FloatConsts.EXP_BIT_MASK
public static final int FLOAT_SIGNIF_BIT_MASK = 8388607;// sun.misc.FloatConsts.SIGNIF_BIT_MASK
private FloatMath() {
// utility class
}
// faster inlined min/max functions in the branch prediction is high
static float max(final float a, final float b) {
// no NaN handling
return (a >= b) ? a : b;
}
static int max(final int a, final int b) {
return (a >= b) ? a : b;
}
@ -77,9 +76,9 @@ public final class FloatMath implements MarlinConst {
// compute only once Float.floatToRawIntBits(a)
final int doppel = Float.floatToRawIntBits(a);
final int exponent = ((doppel & FloatConsts.EXP_BIT_MASK)
>> (FloatConsts.SIGNIFICAND_WIDTH - 1))
- FloatConsts.EXP_BIAS;
final int exponent = ((doppel & FLOAT_EXP_BIT_MASK)
>> (FLOAT_SIGNIFICAND_WIDTH - 1))
- FLOAT_EXP_BIAS;
if (exponent < 0) {
/*
@ -87,8 +86,8 @@ public final class FloatMath implements MarlinConst {
* floorOrceil(-0.0) => -0.0
* floorOrceil(+0.0) => +0.0
*/
return ((a == 0) ? a :
( (a < 0f) ? -0f : 1f) );
return ((a == 0.0f) ? a :
( (a < 0.0f) ? -0.0f : 1.0f) );
}
if (CHECK_OVERFLOW && (exponent >= 23)) { // 52 for double
/*
@ -101,7 +100,7 @@ public final class FloatMath implements MarlinConst {
assert exponent >= 0 && exponent <= 22; // 51 for double
final int intpart = doppel
& (~(FloatConsts.SIGNIF_BIT_MASK >> exponent));
& (~(FLOAT_SIGNIF_BIT_MASK >> exponent));
if (intpart == doppel) {
return a; // integral value (including 0)
@ -134,9 +133,9 @@ public final class FloatMath implements MarlinConst {
// compute only once Float.floatToRawIntBits(a)
final int doppel = Float.floatToRawIntBits(a);
final int exponent = ((doppel & FloatConsts.EXP_BIT_MASK)
>> (FloatConsts.SIGNIFICAND_WIDTH - 1))
- FloatConsts.EXP_BIAS;
final int exponent = ((doppel & FLOAT_EXP_BIT_MASK)
>> (FLOAT_SIGNIFICAND_WIDTH - 1))
- FLOAT_EXP_BIAS;
if (exponent < 0) {
/*
@ -144,8 +143,8 @@ public final class FloatMath implements MarlinConst {
* floorOrceil(-0.0) => -0.0
* floorOrceil(+0.0) => +0.0
*/
return ((a == 0) ? a :
( (a < 0f) ? -1f : 0f) );
return ((a == 0.0f) ? a :
( (a < 0.0f) ? -1.0f : 0.0f) );
}
if (CHECK_OVERFLOW && (exponent >= 23)) { // 52 for double
/*
@ -158,7 +157,7 @@ public final class FloatMath implements MarlinConst {
assert exponent >= 0 && exponent <= 22; // 51 for double
final int intpart = doppel
& (~(FloatConsts.SIGNIF_BIT_MASK >> exponent));
& (~(FLOAT_SIGNIF_BIT_MASK >> exponent));
if (intpart == doppel) {
return a; // integral value (including 0)
@ -190,6 +189,26 @@ public final class FloatMath implements MarlinConst {
return intpart + 1;
}
/**
* Faster alternative to ceil(double) optimized for the integer domain
* and supporting NaN and +/-Infinity.
*
* @param a a value.
* @return the largest (closest to positive infinity) integer value
* that less than or equal to the argument and is equal to a mathematical
* integer.
*/
public static int ceil_int(final double a) {
final int intpart = (int) a;
if (a <= intpart
|| (CHECK_OVERFLOW && intpart == Integer.MAX_VALUE)
|| CHECK_NAN && Double.isNaN(a)) {
return intpart;
}
return intpart + 1;
}
/**
* Faster alternative to floor(float) optimized for the integer domain
* and supporting NaN and +/-Infinity.
@ -209,4 +228,24 @@ public final class FloatMath implements MarlinConst {
}
return intpart - 1;
}
/**
* Faster alternative to floor(double) optimized for the integer domain
* and supporting NaN and +/-Infinity.
*
* @param a a value.
* @return the largest (closest to positive infinity) floating-point value
* that less than or equal to the argument and is equal to a mathematical
* integer.
*/
public static int floor_int(final double a) {
final int intpart = (int) a;
if (a >= intpart
|| (CHECK_OVERFLOW && intpart == Integer.MIN_VALUE)
|| CHECK_NAN && Double.isNaN(a)) {
return intpart;
}
return intpart - 1;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved.
* 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
@ -52,27 +52,27 @@ final class Helpers implements MarlinConst {
{
int ret = off;
float t;
if (a != 0f) {
if (a != 0.0f) {
final float dis = b*b - 4*a*c;
if (dis > 0f) {
final float sqrtDis = (float)Math.sqrt(dis);
if (dis > 0.0f) {
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 >= 0f) {
zeroes[ret++] = (2f * c) / (-b - sqrtDis);
zeroes[ret++] = (-b - sqrtDis) / (2f * a);
if (b >= 0.0f) {
zeroes[ret++] = (2.0f * c) / (-b - sqrtDis);
zeroes[ret++] = (-b - sqrtDis) / (2.0f * a);
} else {
zeroes[ret++] = (-b + sqrtDis) / (2f * a);
zeroes[ret++] = (2f * c) / (-b + sqrtDis);
zeroes[ret++] = (-b + sqrtDis) / (2.0f * a);
zeroes[ret++] = (2.0f * c) / (-b + sqrtDis);
}
} else if (dis == 0f) {
t = (-b) / (2f * a);
} else if (dis == 0.0f) {
t = (-b) / (2.0f * a);
zeroes[ret++] = t;
}
} else {
if (b != 0f) {
if (b != 0.0f) {
t = (-c) / b;
zeroes[ret++] = t;
}
@ -85,7 +85,7 @@ final class Helpers implements MarlinConst {
float[] pts, final int off,
final float A, final float B)
{
if (d == 0f) {
if (d == 0.0f) {
int num = quadraticRoots(a, b, c, pts, off);
return filterOutNotInAB(pts, off, num, A, B) - off;
}
@ -109,8 +109,8 @@ final class Helpers implements MarlinConst {
// q = Q/2
// instead and use those values for simplicity of the code.
double sq_A = a * a;
double p = (1.0/3.0) * ((-1.0/3.0) * sq_A + b);
double q = (1.0/2.0) * ((2.0/27.0) * a * sq_A - (1.0/3.0) * a * b + c);
double p = (1.0d/3.0d) * ((-1.0d/3.0d) * sq_A + b);
double q = (1.0d/2.0d) * ((2.0d/27.0d) * a * sq_A - (1.0d/3.0d) * a * b + c);
// use Cardano's formula
@ -118,30 +118,30 @@ final class Helpers implements MarlinConst {
double D = q * q + cb_p;
int num;
if (D < 0.0) {
if (D < 0.0d) {
// see: http://en.wikipedia.org/wiki/Cubic_function#Trigonometric_.28and_hyperbolic.29_method
final double phi = (1.0/3.0) * acos(-q / sqrt(-cb_p));
final double t = 2.0 * sqrt(-p);
final double phi = (1.0d/3.0d) * acos(-q / sqrt(-cb_p));
final double t = 2.0d * sqrt(-p);
pts[ off+0 ] = (float)( t * cos(phi));
pts[ off+1 ] = (float)(-t * cos(phi + (PI / 3.0)));
pts[ off+2 ] = (float)(-t * cos(phi - (PI / 3.0)));
pts[ off+0 ] = (float) ( t * cos(phi));
pts[ off+1 ] = (float) (-t * cos(phi + (PI / 3.0d)));
pts[ off+2 ] = (float) (-t * cos(phi - (PI / 3.0d)));
num = 3;
} else {
final double sqrt_D = sqrt(D);
final double u = cbrt(sqrt_D - q);
final double v = - cbrt(sqrt_D + q);
pts[ off ] = (float)(u + v);
pts[ off ] = (float) (u + v);
num = 1;
if (within(D, 0.0, 1e-8)) {
pts[off+1] = -(pts[off] / 2f);
if (within(D, 0.0d, 1e-8d)) {
pts[off+1] = -(pts[off] / 2.0f);
num = 2;
}
}
final float sub = (1f/3f) * a;
final float sub = (1.0f/3.0f) * a;
for (int i = 0; i < num; ++i) {
pts[ off+i ] -= sub;
@ -178,7 +178,7 @@ final class Helpers implements MarlinConst {
static float polyLineLength(float[] poly, final int off, final int nCoords) {
assert nCoords % 2 == 0 && poly.length >= off + nCoords : "";
float acc = 0;
float acc = 0.0f;
for (int i = off + 2; i < off + nCoords; i += 2) {
acc += linelen(poly[i], poly[i+1], poly[i-2], poly[i-1]);
}
@ -188,7 +188,7 @@ final class Helpers implements MarlinConst {
static float linelen(float x1, float y1, float x2, float y2) {
final float dx = x2 - x1;
final float dy = y2 - y1;
return (float)Math.sqrt(dx*dx + dy*dy);
return (float) Math.sqrt(dx*dx + dy*dy);
}
static void subdivide(float[] src, int srcoff, float[] left, int leftoff,
@ -218,8 +218,8 @@ final class Helpers implements MarlinConst {
}
// 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.
// both single and double precision variants 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>
@ -268,18 +268,18 @@ final class Helpers implements MarlinConst {
right[rightoff + 6] = x2;
right[rightoff + 7] = y2;
}
x1 = (x1 + ctrlx1) / 2f;
y1 = (y1 + ctrly1) / 2f;
x2 = (x2 + ctrlx2) / 2f;
y2 = (y2 + ctrly2) / 2f;
float centerx = (ctrlx1 + ctrlx2) / 2f;
float centery = (ctrly1 + ctrly2) / 2f;
ctrlx1 = (x1 + centerx) / 2f;
ctrly1 = (y1 + centery) / 2f;
ctrlx2 = (x2 + centerx) / 2f;
ctrly2 = (y2 + centery) / 2f;
centerx = (ctrlx1 + ctrlx2) / 2f;
centery = (ctrly1 + ctrly2) / 2f;
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;
@ -367,12 +367,12 @@ final class Helpers implements MarlinConst {
right[rightoff + 4] = x2;
right[rightoff + 5] = y2;
}
x1 = (x1 + ctrlx) / 2f;
y1 = (y1 + ctrly) / 2f;
x2 = (x2 + ctrlx) / 2f;
y2 = (y2 + ctrly) / 2f;
ctrlx = (x1 + x2) / 2f;
ctrly = (y1 + y2) / 2f;
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;

View File

@ -0,0 +1,36 @@
/*
* Copyright (c) 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
* 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.marlin;
interface IRendererContext extends MarlinConst {
public RendererStats stats();
public OffHeapArray newOffHeapArray(final long initialSize);
public IntArrayCache.Reference newCleanIntArrayRef(final int initialSize);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 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
@ -22,6 +22,7 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.marlin;
import static sun.java2d.marlin.ArrayCacheConst.ARRAY_SIZES;
@ -37,13 +38,14 @@ import sun.java2d.marlin.ArrayCacheConst.BucketStats;
import sun.java2d.marlin.ArrayCacheConst.CacheStats;
/*
* Note that the [BYTE/INT/FLOAT]ArrayCache files are nearly identical except
* Note that the [BYTE/INT/FLOAT/DOUBLE]ArrayCache files are nearly identical except
* for a few type and name differences. Typically, the [BYTE]ArrayCache.java file
* is edited manually and then [INT]ArrayCache.java and [FLOAT]ArrayCache.java
* is edited manually and then [INT/FLOAT/DOUBLE]ArrayCache.java
* files are generated with the following command lines:
*/
// % sed -e 's/(b\yte)[ ]*//g' -e 's/b\yte/int/g' -e 's/B\yte/Int/g' < B\yteArrayCache.java > IntArrayCache.java
// % sed -e 's/(b\yte)[ ]*/(float) /g' -e 's/b\yte/float/g' -e 's/B\yte/Float/g' < B\yteArrayCache.java > FloatArrayCache.java
// % sed -e 's/(b\yte)[ ]*0/0.0f/g' -e 's/(b\yte)[ ]*/(float) /g' -e 's/b\yte/float/g' -e 's/B\yte/Float/g' < B\yteArrayCache.java > FloatArrayCache.java
// % sed -e 's/(b\yte)[ ]*0/0.0d/g' -e 's/(b\yte)[ ]*/(double) /g' -e 's/b\yte/double/g' -e 's/B\yte/Double/g' < B\yteArrayCache.java > DoubleArrayCache.java
final class IntArrayCache implements MarlinConst {
@ -231,8 +233,8 @@ final class IntArrayCache implements MarlinConst {
if (clean) {
return new int[length];
}
// use JDK9 Unsafe.allocateUninitializedArray(class, length):
return (int[]) OffHeapArray.UNSAFE.allocateUninitializedArray(int.class, length);
// use JDK9 Unsafe.allocateUninitializedArray(class, length):
return (int[]) OffHeapArray.UNSAFE.allocateUninitializedArray(int.class, length);
}
static void fill(final int[] array, final int fromIndex,

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved.
* 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
@ -45,7 +45,7 @@ public final class MarlinCache implements MarlinConst {
// 2048 (pixelSize) alpha values (width) x 32 rows (tile) = 64K bytes
// x1 instead of 4 bytes (RLE) ie 1/4 capacity or average good RLE compression
static final long INITIAL_CHUNK_ARRAY = TILE_SIZE * INITIAL_PIXEL_DIM; // 64K
static final long INITIAL_CHUNK_ARRAY = TILE_H * INITIAL_PIXEL_DIM; // 64K
// The alpha map used by this object (taken out of our map cache) to convert
// pixel coverage counts gotten from MarlinCache (which are in the range
@ -72,17 +72,17 @@ public final class MarlinCache implements MarlinConst {
// 1D dirty arrays
// row index in rowAAChunk[]
final long[] rowAAChunkIndex = new long[TILE_SIZE];
final long[] rowAAChunkIndex = new long[TILE_H];
// first pixel (inclusive) for each row
final int[] rowAAx0 = new int[TILE_SIZE];
final int[] rowAAx0 = new int[TILE_H];
// last pixel (exclusive) for each row
final int[] rowAAx1 = new int[TILE_SIZE];
final int[] rowAAx1 = new int[TILE_H];
// encoding mode (0=raw, 1=RLE encoding) for each row
final int[] rowAAEnc = new int[TILE_SIZE];
final int[] rowAAEnc = new int[TILE_H];
// coded length (RLE encoding) for each row
final long[] rowAALen = new long[TILE_SIZE];
final long[] rowAALen = new long[TILE_H];
// last position in RLE decoding for each row (getAlpha):
final long[] rowAAPos = new long[TILE_SIZE];
final long[] rowAAPos = new long[TILE_H];
// dirty off-heap array containing pixel coverages for (32) rows (packed)
// if encoding=raw, it contains alpha coverage values (val) as integer
@ -97,8 +97,8 @@ public final class MarlinCache implements MarlinConst {
// x=j*TILE_SIZE+bboxX0.
int[] touchedTile;
// per-thread renderer context
final RendererContext rdrCtx;
// per-thread renderer stats
final RendererStats rdrStats;
// touchedTile ref (clean)
private final IntArrayCache.Reference touchedTile_ref;
@ -107,8 +107,8 @@ public final class MarlinCache implements MarlinConst {
boolean useRLE = false;
MarlinCache(final RendererContext rdrCtx) {
this.rdrCtx = rdrCtx;
MarlinCache(final IRendererContext rdrCtx) {
this.rdrStats = rdrCtx.stats();
rowAAChunk = rdrCtx.newOffHeapArray(INITIAL_CHUNK_ARRAY); // 64K
@ -120,7 +120,7 @@ public final class MarlinCache implements MarlinConst {
tileMax = Integer.MIN_VALUE;
}
void init(int minx, int miny, int maxx, int maxy, int edgeSumDeltaY)
void init(int minx, int miny, int maxx, int maxy)
{
// assert maxy >= miny && maxx >= minx;
bboxX0 = minx;
@ -142,47 +142,16 @@ public final class MarlinCache implements MarlinConst {
if (width <= RLE_MIN_WIDTH || width >= RLE_MAX_WIDTH) {
useRLE = false;
} else {
// perimeter approach: how fit the total length into given height:
// if stroking: meanCrossings /= 2 => divide edgeSumDeltaY by 2
final int heightSubPixel
= (((maxy - miny) << SUBPIXEL_LG_POSITIONS_Y) << rdrCtx.stroking);
// check meanDist > block size:
// check width / (meanCrossings - 1) >= RLE_THRESHOLD
// fast case: (meanCrossingPerPixel <= 2) means 1 span only
useRLE = (edgeSumDeltaY <= (heightSubPixel << 1))
// note: already checked (meanCrossingPerPixel <= 2)
// rewritten to avoid division:
|| (width * heightSubPixel) >
((edgeSumDeltaY - heightSubPixel) << BLOCK_SIZE_LG);
if (DO_TRACE && !useRLE) {
final float meanCrossings
= ((float) edgeSumDeltaY) / heightSubPixel;
final float meanDist = width / (meanCrossings - 1);
System.out.println("High complexity: "
+ " for bbox[width = " + width
+ " height = " + (maxy - miny)
+ "] edgeSumDeltaY = " + edgeSumDeltaY
+ " heightSubPixel = " + heightSubPixel
+ " meanCrossings = "+ meanCrossings
+ " meanDist = " + meanDist
+ " width = " + (width * heightSubPixel)
+ " <= criteria: " + ((edgeSumDeltaY - heightSubPixel) << BLOCK_SIZE_LG)
);
}
useRLE = true;
}
}
// the ceiling of (maxy - miny + 1) / TILE_SIZE;
final int nxTiles = (width + TILE_SIZE) >> TILE_SIZE_LG;
final int nxTiles = (width + TILE_W) >> TILE_W_LG;
if (nxTiles > INITIAL_ARRAY) {
if (DO_STATS) {
rdrCtx.stats.stat_array_marlincache_touchedTile.add(nxTiles);
rdrStats.stat_array_marlincache_touchedTile.add(nxTiles);
}
touchedTile = touchedTile_ref.getArray(nxTiles);
}
@ -197,7 +166,7 @@ public final class MarlinCache implements MarlinConst {
resetTileLine(0);
if (DO_STATS) {
rdrCtx.stats.totalOffHeap += rowAAChunk.length;
rdrStats.totalOffHeap += rowAAChunk.length;
}
// Return arrays:
@ -220,14 +189,14 @@ public final class MarlinCache implements MarlinConst {
// reset current pos
if (DO_STATS) {
rdrCtx.stats.stat_cache_rowAAChunk.add(rowAAChunkPos);
rdrStats.stat_cache_rowAAChunk.add(rowAAChunkPos);
}
rowAAChunkPos = 0L;
// Reset touchedTile:
if (tileMin != Integer.MAX_VALUE) {
if (DO_STATS) {
rdrCtx.stats.stat_cache_tiles.add(tileMax - tileMin);
rdrStats.stat_cache_tiles.add(tileMax - tileMin);
}
// clean only dirty touchedTile:
if (tileMax == 1) {
@ -269,10 +238,6 @@ public final class MarlinCache implements MarlinConst {
void copyAARowNoRLE(final int[] alphaRow, final int y,
final int px0, final int px1)
{
if (DO_MONITORS) {
rdrCtx.stats.mon_rdr_copyAARow.start();
}
// skip useless pixels above boundary
final int px_bbox1 = FloatMath.min(px1, bboxX1);
@ -308,12 +273,12 @@ public final class MarlinCache implements MarlinConst {
expandRowAAChunk(needSize);
}
if (DO_STATS) {
rdrCtx.stats.stat_cache_rowAA.add(px_bbox1 - px0);
rdrStats.stat_cache_rowAA.add(px_bbox1 - px0);
}
// rowAA contains only alpha values for range[x0; x1[
final int[] _touchedTile = touchedTile;
final int _TILE_SIZE_LG = TILE_SIZE_LG;
final int _TILE_SIZE_LG = TILE_W_LG;
final int from = px0 - bboxX0; // first pixel inclusive
final int to = px_bbox1 - bboxX0; // last pixel exclusive
@ -342,9 +307,9 @@ public final class MarlinCache implements MarlinConst {
// store alpha sum (as byte):
if (val == 0) {
_unsafe.putByte(addr_off, (byte)0); // [0..255]
_unsafe.putByte(addr_off, (byte)0); // [0-255]
} else {
_unsafe.putByte(addr_off, _unsafe.getByte(addr_alpha + val)); // [0..255]
_unsafe.putByte(addr_off, _unsafe.getByte(addr_alpha + val)); // [0-255]
// update touchedTile
_touchedTile[x >> _TILE_SIZE_LG] += val;
@ -368,25 +333,17 @@ public final class MarlinCache implements MarlinConst {
}
// Clear alpha row for reuse:
IntArrayCache.fill(alphaRow, from, px1 - bboxX0, 0);
if (DO_MONITORS) {
rdrCtx.stats.mon_rdr_copyAARow.stop();
}
IntArrayCache.fill(alphaRow, from, px1 + 1 - bboxX0, 0);
}
void copyAARowRLE_WithBlockFlags(final int[] blkFlags, final int[] alphaRow,
final int y, final int px0, final int px1)
{
if (DO_MONITORS) {
rdrCtx.stats.mon_rdr_copyAARow.start();
}
// Copy rowAA data into the piscesCache if one is present
final int _bboxX0 = bboxX0;
// process tile line [0 - 32]
final int row = y - bboxY0;
final int row = y - bboxY0;
final int from = px0 - _bboxX0; // first pixel inclusive
// skip useless pixels above boundary
@ -418,12 +375,14 @@ public final class MarlinCache implements MarlinConst {
long addr_off = _rowAAChunk.address + initialPos;
final int[] _touchedTile = touchedTile;
final int _TILE_SIZE_LG = TILE_SIZE_LG;
final int _TILE_SIZE_LG = TILE_W_LG;
final int _BLK_SIZE_LG = BLOCK_SIZE_LG;
// traverse flagged blocks:
final int blkW = (from >> _BLK_SIZE_LG);
final int blkE = (to >> _BLK_SIZE_LG) + 1;
// ensure last block flag = 0 to process final block:
blkFlags[blkE] = 0;
// Perform run-length encoding and store results in the piscesCache
int val = 0;
@ -481,7 +440,7 @@ public final class MarlinCache implements MarlinConst {
} else {
_unsafe.putInt(addr_off,
((_bboxX0 + cx) << 8)
| (((int) _unsafe.getByte(addr_alpha + val)) & 0xFF) // [0..255]
| (((int) _unsafe.getByte(addr_alpha + val)) & 0xFF) // [0-255]
);
if (runLen == 1) {
@ -493,7 +452,7 @@ public final class MarlinCache implements MarlinConst {
addr_off += SIZE_INT;
if (DO_STATS) {
rdrCtx.stats.hist_tile_generator_encoding_runLen
rdrStats.hist_tile_generator_encoding_runLen
.add(runLen);
}
cx0 = cx;
@ -544,7 +503,7 @@ public final class MarlinCache implements MarlinConst {
} else {
_unsafe.putInt(addr_off,
((_bboxX0 + to) << 8)
| (((int) _unsafe.getByte(addr_alpha + val)) & 0xFF) // [0..255]
| (((int) _unsafe.getByte(addr_alpha + val)) & 0xFF) // [0-255]
);
if (runLen == 1) {
@ -556,7 +515,7 @@ public final class MarlinCache implements MarlinConst {
addr_off += SIZE_INT;
if (DO_STATS) {
rdrCtx.stats.hist_tile_generator_encoding_runLen.add(runLen);
rdrStats.hist_tile_generator_encoding_runLen.add(runLen);
}
long len = (addr_off - _rowAAChunk.address);
@ -568,8 +527,8 @@ public final class MarlinCache implements MarlinConst {
rowAAChunkPos = len;
if (DO_STATS) {
rdrCtx.stats.stat_cache_rowAA.add(rowAALen[row]);
rdrCtx.stats.hist_tile_generator_encoding_ratio.add(
rdrStats.stat_cache_rowAA.add(rowAALen[row]);
rdrStats.hist_tile_generator_encoding_ratio.add(
(100 * skip) / (blkE - blkW)
);
}
@ -586,17 +545,10 @@ public final class MarlinCache implements MarlinConst {
}
// Clear alpha row for reuse:
if (px1 > bboxX1) {
alphaRow[to ] = 0;
alphaRow[to + 1] = 0;
}
alphaRow[to] = 0;
if (DO_CHECKS) {
IntArrayCache.check(blkFlags, blkW, blkE, 0);
IntArrayCache.check(alphaRow, from, px1 - bboxX0, 0);
}
if (DO_MONITORS) {
rdrCtx.stats.mon_rdr_copyAARow.stop();
IntArrayCache.check(alphaRow, from, px1 + 1 - bboxX0, 0);
}
}
@ -613,7 +565,7 @@ public final class MarlinCache implements MarlinConst {
private void expandRowAAChunk(final long needSize) {
if (DO_STATS) {
rdrCtx.stats.stat_array_marlincache_rowAAChunk.add(needSize);
rdrStats.stat_array_marlincache_rowAAChunk.add(needSize);
}
// note: throw IOOB if neededSize > 2Gb:
@ -629,7 +581,7 @@ public final class MarlinCache implements MarlinConst {
{
// the x and y of the current row, minus bboxX0, bboxY0
// process tile line [0 - 32]
final int _TILE_SIZE_LG = TILE_SIZE_LG;
final int _TILE_SIZE_LG = TILE_W_LG;
// update touchedTile
int tx = (x0 >> _TILE_SIZE_LG);
@ -666,7 +618,7 @@ public final class MarlinCache implements MarlinConst {
}
int alphaSumInTile(final int x) {
return touchedTile[(x - bboxX0) >> TILE_SIZE_LG];
return touchedTile[(x - bboxX0) >> TILE_W_LG];
}
@Override

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 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
@ -95,10 +95,10 @@ interface MarlinConst {
// 4096 edges for initial capacity
static final int INITIAL_EDGES_COUNT = MarlinProperties.getInitialEdges();
// initial edges = 3/4 * edges count (4096)
// initial edges = edges count (4096)
// 6 ints per edges = 24 bytes
// edges capacity = 24 x initial edges = 18 * edges count (4096) = 72K
static final int INITIAL_EDGES_CAPACITY = INITIAL_EDGES_COUNT * 18;
// edges capacity = 24 x initial edges = 24 * edges count (4096) = 96K
static final int INITIAL_EDGES_CAPACITY = INITIAL_EDGES_COUNT * 24;
// zero value as byte
static final byte BYTE_0 = (byte) 0;
@ -114,14 +114,17 @@ interface MarlinConst {
public static final int SUBPIXEL_POSITIONS_Y = 1 << (SUBPIXEL_LG_POSITIONS_Y);
public static final float NORM_SUBPIXELS
= (float)Math.sqrt(( SUBPIXEL_POSITIONS_X * SUBPIXEL_POSITIONS_X
+ SUBPIXEL_POSITIONS_Y * SUBPIXEL_POSITIONS_Y)/2.0);
= (float) Math.sqrt(( SUBPIXEL_POSITIONS_X * SUBPIXEL_POSITIONS_X
+ SUBPIXEL_POSITIONS_Y * SUBPIXEL_POSITIONS_Y) / 2.0d);
public static final int MAX_AA_ALPHA
= SUBPIXEL_POSITIONS_X * SUBPIXEL_POSITIONS_Y;
public static final int TILE_SIZE_LG = MarlinProperties.getTileSize_Log2();
public static final int TILE_SIZE = 1 << TILE_SIZE_LG; // 32 by default
public static final int TILE_H_LG = MarlinProperties.getTileSize_Log2();
public static final int TILE_H = 1 << TILE_H_LG; // 32 by default
public static final int TILE_W_LG = MarlinProperties.getTileWidth_Log2();
public static final int TILE_W = 1 << TILE_W_LG; // 32 by default
public static final int BLOCK_SIZE_LG = MarlinProperties.getBlockSize_Log2();
public static final int BLOCK_SIZE = 1 << BLOCK_SIZE_LG;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
* 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
@ -68,21 +68,21 @@ public final class MarlinProperties {
/**
* Return the log(2) corresponding to subpixel on x-axis (
*
* @return 1 (2 subpixels) < initial pixel size < 4 (256 subpixels)
* @return 0 (1 subpixels) < initial pixel size < 8 (256 subpixels)
* (3 by default ie 8 subpixels)
*/
public static int getSubPixel_Log2_X() {
return getInteger("sun.java2d.renderer.subPixel_log2_X", 3, 1, 8);
return getInteger("sun.java2d.renderer.subPixel_log2_X", 3, 0, 8);
}
/**
* Return the log(2) corresponding to subpixel on y-axis (
*
* @return 1 (2 subpixels) < initial pixel size < 8 (256 subpixels)
* @return 0 (1 subpixels) < initial pixel size < 8 (256 subpixels)
* (3 by default ie 8 subpixels)
*/
public static int getSubPixel_Log2_Y() {
return getInteger("sun.java2d.renderer.subPixel_log2_Y", 3, 1, 8);
return getInteger("sun.java2d.renderer.subPixel_log2_Y", 3, 0, 8);
}
/**
@ -92,7 +92,18 @@ public final class MarlinProperties {
* (5 by default ie 32x32 pixels)
*/
public static int getTileSize_Log2() {
return getInteger("sun.java2d.renderer.tileSize_log2", 5, 3, 8);
return getInteger("sun.java2d.renderer.tileSize_log2", 5, 3, 10);
}
/**
* Return the log(2) corresponding to the tile width in pixels
*
* @return 3 (8 pixels) < tile with < 8 (256 pixels)
* (by default is given by the square tile size)
*/
public static int getTileWidth_Log2() {
final int tileSize = getTileSize_Log2();
return getInteger("sun.java2d.renderer.tileWidth_log2", tileSize, 3, 10);
}
/**
@ -166,6 +177,20 @@ public final class MarlinProperties {
return getBoolean("sun.java2d.renderer.logUnsafeMalloc", "false");
}
// quality settings
public static float getCubicDecD2() {
return getFloat("sun.java2d.renderer.cubic_dec_d2", 1.0f, 0.01f, 4.0f);
}
public static float getCubicIncD1() {
return getFloat("sun.java2d.renderer.cubic_inc_d1", 0.4f, 0.01f, 2.0f);
}
public static float getQuadDecD2() {
return getFloat("sun.java2d.renderer.quad_dec_d2", 0.5f, 0.01f, 4.0f);
}
// system property utilities
static boolean getBoolean(final String key, final String def) {
return Boolean.valueOf(AccessController.doPrivileged(
@ -197,7 +222,36 @@ public final class MarlinProperties {
}
static int align(final int val, final int norm) {
final int ceil = FloatMath.ceil_int( ((float)val) / norm);
final int ceil = FloatMath.ceil_int( ((float) val) / norm);
return ceil * norm;
}
public static double getDouble(final String key, final double def,
final double min, final double max)
{
double value = def;
final String property = AccessController.doPrivileged(
new GetPropertyAction(key));
if (property != null) {
try {
value = Double.parseDouble(property);
} catch (NumberFormatException nfe) {
logInfo("Invalid value for " + key + " = " + property + " !");
}
}
// check for invalid values
if (value < min || value > max) {
logInfo("Invalid value for " + key + " = " + value
+ "; expect value in range[" + min + ", " + max + "] !");
value = def;
}
return value;
}
public static float getFloat(final String key, final float def,
final float min, final float max)
{
return (float)getDouble(key, def, min, max);
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright (c) 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
* 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.marlin;
public interface MarlinRenderer extends MarlinConst {
}

View File

@ -44,8 +44,8 @@ import sun.security.action.GetPropertyAction;
/**
* Marlin RendererEngine implementation (derived from Pisces)
*/
public class MarlinRenderingEngine extends RenderingEngine
implements MarlinConst
public final class MarlinRenderingEngine extends RenderingEngine
implements MarlinConst
{
private static enum NormMode {
ON_WITH_AA {
@ -80,7 +80,7 @@ public class MarlinRenderingEngine extends RenderingEngine
PathIterator src);
}
private static final float MIN_PEN_SIZE = 1f / NORM_SUBPIXELS;
private static final float MIN_PEN_SIZE = 1.0f / NORM_SUBPIXELS;
static final float UPPER_BND = Float.MAX_VALUE / 2.0f;
static final float LOWER_BND = -UPPER_BND;
@ -259,7 +259,7 @@ public class MarlinRenderingEngine extends RenderingEngine
*/
double EA = A*A + B*B; // x^2 coefficient
double EB = 2.0*(A*C + B*D); // xy coefficient
double EB = 2.0d * (A*C + B*D); // xy coefficient
double EC = C*C + D*D; // y^2 coefficient
/*
@ -287,7 +287,7 @@ public class MarlinRenderingEngine extends RenderingEngine
double hypot = Math.sqrt(EB*EB + (EA-EC)*(EA-EC));
// sqrt omitted, compare to squared limits below.
double widthsquared = ((EA + EC + hypot)/2.0);
double widthsquared = ((EA + EC + hypot) / 2.0d);
widthScale = (float)Math.sqrt(widthsquared);
}
@ -332,7 +332,7 @@ public class MarlinRenderingEngine extends RenderingEngine
final double d = at.getScaleY();
final double det = a * d - c * b;
if (Math.abs(det) <= (2f * Float.MIN_VALUE)) {
if (Math.abs(det) <= (2.0f * 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
@ -344,7 +344,7 @@ public class MarlinRenderingEngine extends RenderingEngine
// of writing of this comment (September 16, 2010)). Actually,
// I am not sure if the moveTo is necessary to avoid the SIGSEGV
// but the pathDone is definitely needed.
pc2d.moveTo(0f, 0f);
pc2d.moveTo(0.0f, 0.0f);
pc2d.pathDone();
return;
}
@ -361,17 +361,7 @@ public class MarlinRenderingEngine extends RenderingEngine
if (dashes != null) {
recycleDashes = true;
dashLen = dashes.length;
final float[] newDashes;
if (dashLen <= INITIAL_ARRAY) {
newDashes = rdrCtx.dasher.dashes_ref.initial;
} else {
if (DO_STATS) {
rdrCtx.stats.stat_array_dasher_dasher.add(dashLen);
}
newDashes = rdrCtx.dasher.dashes_ref.getArray(dashLen);
}
System.arraycopy(dashes, 0, newDashes, 0, dashLen);
dashes = newDashes;
dashes = rdrCtx.dasher.copyDashArray(dashes);
for (int i = 0; i < dashLen; i++) {
dashes[i] *= scale;
}
@ -445,7 +435,7 @@ public class MarlinRenderingEngine extends RenderingEngine
}
private static boolean nearZero(final double num) {
return Math.abs(num) < 2.0 * Math.ulp(num);
return Math.abs(num) < 2.0d * Math.ulp(num);
}
abstract static class NormalizingPathIterator implements PathIterator {
@ -524,8 +514,8 @@ public class MarlinRenderingEngine extends RenderingEngine
case PathIterator.SEG_LINETO:
break;
case PathIterator.SEG_QUADTO:
coords[0] += (curx_adjust + x_adjust) / 2f;
coords[1] += (cury_adjust + y_adjust) / 2f;
coords[0] += (curx_adjust + x_adjust) / 2.0f;
coords[1] += (cury_adjust + y_adjust) / 2.0f;
break;
case PathIterator.SEG_CUBICTO:
coords[0] += curx_adjust;
@ -824,10 +814,8 @@ public class MarlinRenderingEngine extends RenderingEngine
}
} finally {
if (r != null) {
// dispose renderer:
// dispose renderer and recycle the RendererContext instance:
r.dispose();
// recycle the RendererContext instance
MarlinRenderingEngine.returnRendererContext(rdrCtx);
}
}
@ -845,25 +833,25 @@ public class MarlinRenderingEngine extends RenderingEngine
{
// REMIND: Deal with large coordinates!
double ldx1, ldy1, ldx2, ldy2;
boolean innerpgram = (lw1 > 0.0 && lw2 > 0.0);
boolean innerpgram = (lw1 > 0.0d && lw2 > 0.0d);
if (innerpgram) {
ldx1 = dx1 * lw1;
ldy1 = dy1 * lw1;
ldx2 = dx2 * lw2;
ldy2 = dy2 * lw2;
x -= (ldx1 + ldx2) / 2.0;
y -= (ldy1 + ldy2) / 2.0;
x -= (ldx1 + ldx2) / 2.0d;
y -= (ldy1 + ldy2) / 2.0d;
dx1 += ldx1;
dy1 += ldy1;
dx2 += ldx2;
dy2 += ldy2;
if (lw1 > 1.0 && lw2 > 1.0) {
if (lw1 > 1.0d && lw2 > 1.0d) {
// Inner parallelogram was entirely consumed by stroke...
innerpgram = false;
}
} else {
ldx1 = ldy1 = ldx2 = ldy2 = 0.0;
ldx1 = ldy1 = ldx2 = ldy2 = 0.0d;
}
MarlinTileGenerator ptg = null;
@ -884,10 +872,10 @@ public class MarlinRenderingEngine extends RenderingEngine
if (innerpgram) {
x += ldx1 + ldx2;
y += ldy1 + ldy2;
dx1 -= 2.0 * ldx1;
dy1 -= 2.0 * ldy1;
dx2 -= 2.0 * ldx2;
dy2 -= 2.0 * ldy2;
dx1 -= 2.0d * ldx1;
dy1 -= 2.0d * ldy1;
dx2 -= 2.0d * ldx2;
dy2 -= 2.0d * ldy2;
r.moveTo((float) x, (float) y);
r.lineTo((float) (x+dx1), (float) (y+dy1));
r.lineTo((float) (x+dx1+dx2), (float) (y+dy1+dy2));
@ -905,10 +893,8 @@ public class MarlinRenderingEngine extends RenderingEngine
}
} finally {
if (r != null) {
// dispose renderer:
// dispose renderer and recycle the RendererContext instance:
r.dispose();
// recycle the RendererContext instance
MarlinRenderingEngine.returnRendererContext(rdrCtx);
}
}
@ -1035,12 +1021,11 @@ public class MarlinRenderingEngine extends RenderingEngine
+ MarlinConst.SUBPIXEL_LG_POSITIONS_X);
logInfo("sun.java2d.renderer.subPixel_log2_Y = "
+ MarlinConst.SUBPIXEL_LG_POSITIONS_Y);
logInfo("sun.java2d.renderer.tileSize_log2 = "
+ MarlinConst.TILE_SIZE_LG);
logInfo("sun.java2d.renderer.blockSize_log2 = "
+ MarlinConst.BLOCK_SIZE_LG);
+ MarlinConst.TILE_H_LG);
logInfo("sun.java2d.renderer.tileWidth_log2 = "
+ MarlinConst.TILE_W_LG);
logInfo("sun.java2d.renderer.blockSize_log2 = "
+ MarlinConst.BLOCK_SIZE_LG);
@ -1078,8 +1063,14 @@ public class MarlinRenderingEngine extends RenderingEngine
+ MarlinConst.LOG_UNSAFE_MALLOC);
// quality settings
logInfo("sun.java2d.renderer.cubic_dec_d2 = "
+ MarlinProperties.getCubicDecD2());
logInfo("sun.java2d.renderer.cubic_inc_d1 = "
+ MarlinProperties.getCubicIncD1());
logInfo("sun.java2d.renderer.quad_dec_d2 = "
+ MarlinProperties.getQuadDecD2());
logInfo("Renderer settings:");
logInfo("CUB_COUNT_LG = " + Renderer.CUB_COUNT_LG);
logInfo("CUB_DEC_BND = " + Renderer.CUB_DEC_BND);
logInfo("CUB_INC_BND = " + Renderer.CUB_INC_BND);
logInfo("QUAD_DEC_BND = " + Renderer.QUAD_DEC_BND);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved.
* 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
@ -25,25 +25,51 @@
package sun.java2d.marlin;
import java.util.Arrays;
import sun.java2d.pipe.AATileGenerator;
import jdk.internal.misc.Unsafe;
final class MarlinTileGenerator implements AATileGenerator, MarlinConst {
private static final int MAX_TILE_ALPHA_SUM = TILE_SIZE * TILE_SIZE
* MAX_AA_ALPHA;
private static final int MAX_TILE_ALPHA_SUM = TILE_W * TILE_H * MAX_AA_ALPHA;
private final Renderer rdr;
private static final int TH_AA_ALPHA_FILL_EMPTY = ((MAX_AA_ALPHA + 1) / 3); // 33%
private static final int TH_AA_ALPHA_FILL_FULL = ((MAX_AA_ALPHA + 1) * 2 / 3); // 66%
private static final int FILL_TILE_W = TILE_W >> 1; // half tile width
static {
if (MAX_TILE_ALPHA_SUM <= 0) {
throw new IllegalStateException("Invalid MAX_TILE_ALPHA_SUM: " + MAX_TILE_ALPHA_SUM);
}
if (DO_TRACE) {
System.out.println("MAX_AA_ALPHA : " + MAX_AA_ALPHA);
System.out.println("TH_AA_ALPHA_FILL_EMPTY : " + TH_AA_ALPHA_FILL_EMPTY);
System.out.println("TH_AA_ALPHA_FILL_FULL : " + TH_AA_ALPHA_FILL_FULL);
System.out.println("FILL_TILE_W : " + FILL_TILE_W);
}
}
private final Renderer rdrF;
private final DRenderer rdrD;
private final MarlinCache cache;
private int x, y;
// per-thread renderer context
final RendererContext rdrCtx;
// per-thread renderer stats
final RendererStats rdrStats;
MarlinTileGenerator(Renderer r) {
this.rdr = r;
this.cache = r.cache;
this.rdrCtx = r.rdrCtx;
MarlinTileGenerator(final RendererStats stats, final MarlinRenderer r,
final MarlinCache cache)
{
this.rdrStats = stats;
if (r instanceof Renderer) {
this.rdrF = (Renderer)r;
this.rdrD = null;
} else {
this.rdrF = null;
this.rdrD = (DRenderer)r;
}
this.cache = cache;
}
MarlinTileGenerator init() {
@ -61,14 +87,17 @@ final class MarlinTileGenerator implements AATileGenerator, MarlinConst {
public void dispose() {
if (DO_MONITORS) {
// called from AAShapePipe.renderTiles() (render tiles end):
rdrCtx.stats.mon_pipe_renderTiles.stop();
rdrStats.mon_pipe_renderTiles.stop();
}
// dispose cache:
cache.dispose();
// dispose renderer:
rdr.dispose();
// recycle the RendererContext instance
MarlinRenderingEngine.returnRendererContext(rdrCtx);
// dispose renderer and recycle the RendererContext instance:
// bimorphic call optimization:
if (rdrF != null) {
rdrF.dispose();
} else if (rdrD != null) {
rdrD.dispose();
}
}
void getBbox(int[] bbox) {
@ -86,9 +115,9 @@ final class MarlinTileGenerator implements AATileGenerator, MarlinConst {
public int getTileWidth() {
if (DO_MONITORS) {
// called from AAShapePipe.renderTiles() (render tiles start):
rdrCtx.stats.mon_pipe_renderTiles.start();
rdrStats.mon_pipe_renderTiles.start();
}
return TILE_SIZE;
return TILE_W;
}
/**
@ -97,7 +126,7 @@ final class MarlinTileGenerator implements AATileGenerator, MarlinConst {
*/
@Override
public int getTileHeight() {
return TILE_SIZE;
return TILE_H;
}
/**
@ -131,7 +160,7 @@ final class MarlinTileGenerator implements AATileGenerator, MarlinConst {
final int alpha = (al == 0x00 ? 0x00
: (al == MAX_TILE_ALPHA_SUM ? 0xff : 0x80));
if (DO_STATS) {
rdrCtx.stats.hist_tile_generator_alpha.add(alpha);
rdrStats.hist_tile_generator_alpha.add(alpha);
}
return alpha;
}
@ -143,14 +172,19 @@ final class MarlinTileGenerator implements AATileGenerator, MarlinConst {
*/
@Override
public void nextTile() {
if ((x += TILE_SIZE) >= cache.bboxX1) {
if ((x += TILE_W) >= cache.bboxX1) {
x = cache.bboxX0;
y += TILE_SIZE;
y += TILE_H;
if (y < cache.bboxY1) {
// compute for the tile line
// [ y; max(y + TILE_SIZE, bboxY1) ]
this.rdr.endRendering(y);
// bimorphic call optimization:
if (rdrF != null) {
rdrF.endRendering(y);
} else if (rdrD != null) {
rdrD.endRendering(y);
}
}
}
}
@ -180,7 +214,7 @@ final class MarlinTileGenerator implements AATileGenerator, MarlinConst {
final int rowstride)
{
if (DO_MONITORS) {
rdrCtx.stats.mon_ptg_getAlpha.start();
rdrStats.mon_ptg_getAlpha.start();
}
// local vars for performance:
@ -190,11 +224,11 @@ final class MarlinTileGenerator implements AATileGenerator, MarlinConst {
final int[] rowAAx1 = _cache.rowAAx1;
final int x0 = this.x;
final int x1 = FloatMath.min(x0 + TILE_SIZE, _cache.bboxX1);
final int x1 = FloatMath.min(x0 + TILE_W, _cache.bboxX1);
// note: process tile line [0 - 32[
final int y0 = 0;
final int y1 = FloatMath.min(this.y + TILE_SIZE, _cache.bboxY1) - this.y;
final int y1 = FloatMath.min(this.y + TILE_H, _cache.bboxY1) - this.y;
if (DO_LOG_BOUNDS) {
MarlinUtils.logInfo("getAlpha = [" + x0 + " ... " + x1
@ -237,14 +271,14 @@ final class MarlinTileGenerator implements AATileGenerator, MarlinConst {
}
}
// now: cx >= x0 but cx < aax0 (x1 < aax0)
// now: cx >= x0 and cx >= aax0
// Copy AA data (sum alpha data):
addr = addr_rowAA + rowAAChunkIndex[cy] + (cx - aax0);
for (end = (aax1 <= x1) ? aax1 : x1; cx < end; cx++) {
// cx inside tile[x0; x1[ :
tile[idx++] = _unsafe.getByte(addr); // [0..255]
tile[idx++] = _unsafe.getByte(addr); // [0-255]
addr += SIZE;
}
}
@ -269,7 +303,7 @@ final class MarlinTileGenerator implements AATileGenerator, MarlinConst {
nextTile();
if (DO_MONITORS) {
rdrCtx.stats.mon_ptg_getAlpha.stop();
rdrStats.mon_ptg_getAlpha.stop();
}
}
@ -282,7 +316,7 @@ final class MarlinTileGenerator implements AATileGenerator, MarlinConst {
final int rowstride)
{
if (DO_MONITORS) {
rdrCtx.stats.mon_ptg_getAlpha.start();
rdrStats.mon_ptg_getAlpha.start();
}
// Decode run-length encoded alpha mask data
@ -300,24 +334,48 @@ final class MarlinTileGenerator implements AATileGenerator, MarlinConst {
final long[] rowAAPos = _cache.rowAAPos;
final int x0 = this.x;
final int x1 = FloatMath.min(x0 + TILE_SIZE, _cache.bboxX1);
final int x1 = FloatMath.min(x0 + TILE_W, _cache.bboxX1);
final int w = x1 - x0;
// note: process tile line [0 - 32[
final int y0 = 0;
final int y1 = FloatMath.min(this.y + TILE_SIZE, _cache.bboxY1) - this.y;
final int y1 = FloatMath.min(this.y + TILE_H, _cache.bboxY1) - this.y;
if (DO_LOG_BOUNDS) {
MarlinUtils.logInfo("getAlpha = [" + x0 + " ... " + x1
+ "[ [" + y0 + " ... " + y1 + "[");
}
// avoid too small area: fill is not faster !
final int clearTile;
final byte refVal;
final int area;
if ((w >= FILL_TILE_W) && (area = w * y1) > 64) { // 64 / 4 ie 16 words min (faster)
final int alphaSum = cache.alphaSumInTile(x0);
if (alphaSum < area * TH_AA_ALPHA_FILL_EMPTY) {
clearTile = 1;
refVal = 0;
} else if (alphaSum > area * TH_AA_ALPHA_FILL_FULL) {
clearTile = 2;
refVal = (byte)0xff;
} else {
clearTile = 0;
refVal = 0;
}
} else {
clearTile = 0;
refVal = 0;
}
final Unsafe _unsafe = OffHeapArray.UNSAFE;
final long SIZE_BYTE = 1L;
final long SIZE_INT = 4L;
final long addr_rowAA = _cache.rowAAChunk.address;
long addr, addr_row, last_addr, addr_end;
final int skipRowPixels = (rowstride - (x1 - x0));
final int skipRowPixels = (rowstride - w);
int cx, cy, cx1;
int rx0, rx1, runLen, end;
@ -325,137 +383,414 @@ final class MarlinTileGenerator implements AATileGenerator, MarlinConst {
byte val;
int idx = offset;
for (cy = y0; cy < y1; cy++) {
// empty line (default)
cx = x0;
switch (clearTile) {
case 1: // 0x00
// Clear full tile rows:
Arrays.fill(tile, offset, offset + (y1 * rowstride), refVal);
if (rowAAEnc[cy] == 0) {
// Raw encoding:
for (cy = y0; cy < y1; cy++) {
// empty line (default)
cx = x0;
final int aax1 = rowAAx1[cy]; // exclusive
if (rowAAEnc[cy] == 0) {
// Raw encoding:
// quick check if there is AA data
// corresponding to this tile [x0; x1[
if (aax1 > x0) {
final int aax0 = rowAAx0[cy]; // inclusive
final int aax1 = rowAAx1[cy]; // exclusive
if (aax0 < x1) {
// note: cx is the cursor pointer in the tile array
// (left to right)
cx = aax0;
// quick check if there is AA data
// corresponding to this tile [x0; x1[
if (aax1 > x0) {
final int aax0 = rowAAx0[cy]; // inclusive
// ensure cx >= x0
if (cx <= x0) {
cx = x0;
} else {
// fill line start until first AA pixel rowAA exclusive:
for (end = x0; end < cx; end++) {
tile[idx++] = 0;
if (aax0 < x1) {
// note: cx is the cursor pointer in the tile array
// (left to right)
cx = aax0;
// ensure cx >= x0
if (cx <= x0) {
cx = x0;
} else {
// skip line start until first AA pixel rowAA exclusive:
idx += (cx - x0); // > 0
}
// now: cx >= x0 and cx >= aax0
// Copy AA data (sum alpha data):
addr = addr_rowAA + rowAAChunkIndex[cy] + (cx - aax0);
for (end = (aax1 <= x1) ? aax1 : x1; cx < end; cx++) {
tile[idx++] = _unsafe.getByte(addr); // [0-255]
addr += SIZE_BYTE;
}
}
}
} else {
// RLE encoding:
// quick check if there is AA data
// corresponding to this tile [x0; x1[
if (rowAAx1[cy] > x0) { // last pixel exclusive
cx = rowAAx0[cy]; // inclusive
if (cx > x1) {
cx = x1;
}
// skip line start until first AA pixel rowAA exclusive:
if (cx > x0) {
idx += (cx - x0); // > 0
}
// get row address:
addr_row = addr_rowAA + rowAAChunkIndex[cy];
// get row end address:
addr_end = addr_row + rowAALen[cy]; // coded length
// reuse previous iteration position:
addr = addr_row + rowAAPos[cy];
last_addr = 0L;
while ((cx < x1) && (addr < addr_end)) {
// keep current position:
last_addr = addr;
// packed value:
packed = _unsafe.getInt(addr);
// last exclusive pixel x-coordinate:
cx1 = (packed >> 8);
// as bytes:
addr += SIZE_INT;
rx0 = cx;
if (rx0 < x0) {
rx0 = x0;
}
rx1 = cx = cx1;
if (rx1 > x1) {
rx1 = x1;
cx = x1; // fix last x
}
// adjust runLen:
runLen = rx1 - rx0;
// ensure rx1 > rx0:
if (runLen > 0) {
packed &= 0xFF; // [0-255]
if (packed == 0)
{
idx += runLen;
continue;
}
val = (byte) packed; // [0-255]
do {
tile[idx++] = val;
} while (--runLen > 0);
}
}
// now: cx >= x0 but cx < aax0 (x1 < aax0)
// Copy AA data (sum alpha data):
addr = addr_rowAA + rowAAChunkIndex[cy] + (cx - aax0);
for (end = (aax1 <= x1) ? aax1 : x1; cx < end; cx++) {
tile[idx++] = _unsafe.getByte(addr); // [0..255]
addr += SIZE_BYTE;
// Update last position in RLE entries:
if (last_addr != 0L) {
// Fix x0:
rowAAx0[cy] = cx; // inclusive
// Fix position:
rowAAPos[cy] = (last_addr - addr_row);
}
}
}
} else {
// RLE encoding:
// quick check if there is AA data
// corresponding to this tile [x0; x1[
if (rowAAx1[cy] > x0) { // last pixel exclusive
// skip line end
if (cx < x1) {
idx += (x1 - cx); // > 0
}
cx = rowAAx0[cy]; // inclusive
if (cx > x1) {
cx = x1;
if (DO_TRACE) {
for (int i = idx - (x1 - x0); i < idx; i++) {
System.out.print(hex(tile[i], 2));
}
System.out.println();
}
// fill line start until first AA pixel rowAA exclusive:
for (int i = x0; i < cx; i++) {
tile[idx++] = 0;
}
idx += skipRowPixels;
}
break;
// get row address:
addr_row = addr_rowAA + rowAAChunkIndex[cy];
// get row end address:
addr_end = addr_row + rowAALen[cy]; // coded length
case 0:
default:
for (cy = y0; cy < y1; cy++) {
// empty line (default)
cx = x0;
// reuse previous iteration position:
addr = addr_row + rowAAPos[cy];
if (rowAAEnc[cy] == 0) {
// Raw encoding:
last_addr = 0L;
final int aax1 = rowAAx1[cy]; // exclusive
while ((cx < x1) && (addr < addr_end)) {
// keep current position:
last_addr = addr;
// quick check if there is AA data
// corresponding to this tile [x0; x1[
if (aax1 > x0) {
final int aax0 = rowAAx0[cy]; // inclusive
// packed value:
packed = _unsafe.getInt(addr);
if (aax0 < x1) {
// note: cx is the cursor pointer in the tile array
// (left to right)
cx = aax0;
// last exclusive pixel x-coordinate:
cx1 = (packed >> 8);
// as bytes:
addr += SIZE_INT;
// ensure cx >= x0
if (cx <= x0) {
cx = x0;
} else {
for (end = x0; end < cx; end++) {
tile[idx++] = 0;
}
}
rx0 = cx;
if (rx0 < x0) {
rx0 = x0;
}
rx1 = cx = cx1;
if (rx1 > x1) {
rx1 = x1;
cx = x1; // fix last x
}
// adjust runLen:
runLen = rx1 - rx0;
// now: cx >= x0 and cx >= aax0
// ensure rx1 > rx0:
if (runLen > 0) {
val = (byte)(packed & 0xFF); // [0..255]
// Copy AA data (sum alpha data):
addr = addr_rowAA + rowAAChunkIndex[cy] + (cx - aax0);
do {
tile[idx++] = val;
} while (--runLen > 0);
for (end = (aax1 <= x1) ? aax1 : x1; cx < end; cx++) {
tile[idx++] = _unsafe.getByte(addr); // [0-255]
addr += SIZE_BYTE;
}
}
}
} else {
// RLE encoding:
// Update last position in RLE entries:
if (last_addr != 0L) {
// Fix x0:
rowAAx0[cy] = cx; // inclusive
// Fix position:
rowAAPos[cy] = (last_addr - addr_row);
// quick check if there is AA data
// corresponding to this tile [x0; x1[
if (rowAAx1[cy] > x0) { // last pixel exclusive
cx = rowAAx0[cy]; // inclusive
if (cx > x1) {
cx = x1;
}
// fill line start until first AA pixel rowAA exclusive:
for (end = x0; end < cx; end++) {
tile[idx++] = 0;
}
// get row address:
addr_row = addr_rowAA + rowAAChunkIndex[cy];
// get row end address:
addr_end = addr_row + rowAALen[cy]; // coded length
// reuse previous iteration position:
addr = addr_row + rowAAPos[cy];
last_addr = 0L;
while ((cx < x1) && (addr < addr_end)) {
// keep current position:
last_addr = addr;
// packed value:
packed = _unsafe.getInt(addr);
// last exclusive pixel x-coordinate:
cx1 = (packed >> 8);
// as bytes:
addr += SIZE_INT;
rx0 = cx;
if (rx0 < x0) {
rx0 = x0;
}
rx1 = cx = cx1;
if (rx1 > x1) {
rx1 = x1;
cx = x1; // fix last x
}
// adjust runLen:
runLen = rx1 - rx0;
// ensure rx1 > rx0:
if (runLen > 0) {
packed &= 0xFF; // [0-255]
val = (byte) packed; // [0-255]
do {
tile[idx++] = val;
} while (--runLen > 0);
}
}
// Update last position in RLE entries:
if (last_addr != 0L) {
// Fix x0:
rowAAx0[cy] = cx; // inclusive
// Fix position:
rowAAPos[cy] = (last_addr - addr_row);
}
}
}
}
// fill line end
while (cx < x1) {
tile[idx++] = 0;
cx++;
}
if (DO_TRACE) {
for (int i = idx - (x1 - x0); i < idx; i++) {
System.out.print(hex(tile[i], 2));
// fill line end
while (cx < x1) {
tile[idx++] = 0;
cx++;
}
System.out.println();
}
idx += skipRowPixels;
if (DO_TRACE) {
for (int i = idx - (x1 - x0); i < idx; i++) {
System.out.print(hex(tile[i], 2));
}
System.out.println();
}
idx += skipRowPixels;
}
break;
case 2: // 0xFF
// Fill full tile rows:
Arrays.fill(tile, offset, offset + (y1 * rowstride), refVal);
for (cy = y0; cy < y1; cy++) {
// empty line (default)
cx = x0;
if (rowAAEnc[cy] == 0) {
// Raw encoding:
final int aax1 = rowAAx1[cy]; // exclusive
// quick check if there is AA data
// corresponding to this tile [x0; x1[
if (aax1 > x0) {
final int aax0 = rowAAx0[cy]; // inclusive
if (aax0 < x1) {
// note: cx is the cursor pointer in the tile array
// (left to right)
cx = aax0;
// ensure cx >= x0
if (cx <= x0) {
cx = x0;
} else {
// fill line start until first AA pixel rowAA exclusive:
for (end = x0; end < cx; end++) {
tile[idx++] = 0;
}
}
// now: cx >= x0 and cx >= aax0
// Copy AA data (sum alpha data):
addr = addr_rowAA + rowAAChunkIndex[cy] + (cx - aax0);
for (end = (aax1 <= x1) ? aax1 : x1; cx < end; cx++) {
tile[idx++] = _unsafe.getByte(addr); // [0-255]
addr += SIZE_BYTE;
}
}
}
} else {
// RLE encoding:
// quick check if there is AA data
// corresponding to this tile [x0; x1[
if (rowAAx1[cy] > x0) { // last pixel exclusive
cx = rowAAx0[cy]; // inclusive
if (cx > x1) {
cx = x1;
}
// fill line start until first AA pixel rowAA exclusive:
for (end = x0; end < cx; end++) {
tile[idx++] = 0;
}
// get row address:
addr_row = addr_rowAA + rowAAChunkIndex[cy];
// get row end address:
addr_end = addr_row + rowAALen[cy]; // coded length
// reuse previous iteration position:
addr = addr_row + rowAAPos[cy];
last_addr = 0L;
while ((cx < x1) && (addr < addr_end)) {
// keep current position:
last_addr = addr;
// packed value:
packed = _unsafe.getInt(addr);
// last exclusive pixel x-coordinate:
cx1 = (packed >> 8);
// as bytes:
addr += SIZE_INT;
rx0 = cx;
if (rx0 < x0) {
rx0 = x0;
}
rx1 = cx = cx1;
if (rx1 > x1) {
rx1 = x1;
cx = x1; // fix last x
}
// adjust runLen:
runLen = rx1 - rx0;
// ensure rx1 > rx0:
if (runLen > 0) {
packed &= 0xFF; // [0-255]
if (packed == 0xFF)
{
idx += runLen;
continue;
}
val = (byte) packed; // [0-255]
do {
tile[idx++] = val;
} while (--runLen > 0);
}
}
// Update last position in RLE entries:
if (last_addr != 0L) {
// Fix x0:
rowAAx0[cy] = cx; // inclusive
// Fix position:
rowAAPos[cy] = (last_addr - addr_row);
}
}
}
// fill line end
while (cx < x1) {
tile[idx++] = 0;
cx++;
}
if (DO_TRACE) {
for (int i = idx - (x1 - x0); i < idx; i++) {
System.out.print(hex(tile[i], 2));
}
System.out.println();
}
idx += skipRowPixels;
}
}
nextTile();
if (DO_MONITORS) {
rdrCtx.stats.mon_ptg_getAlpha.stop();
rdrStats.mon_ptg_getAlpha.stop();
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved.
* 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
@ -89,6 +89,7 @@ final class OffHeapArray {
+ this.length
+ " at addr = " + this.address);
}
this.address = 0L;
}
void fill(final byte val) {

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved.
* 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
@ -25,41 +25,38 @@
package sun.java2d.marlin;
import java.util.Arrays;
import sun.awt.geom.PathConsumer2D;
import static sun.java2d.marlin.OffHeapArray.SIZE_INT;
import jdk.internal.misc.Unsafe;
final class Renderer implements PathConsumer2D, MarlinConst {
final class Renderer implements PathConsumer2D, MarlinRenderer {
static final boolean DISABLE_RENDER = false;
static final boolean ENABLE_BLOCK_FLAGS = MarlinProperties.isUseTileFlags();
static final boolean ENABLE_BLOCK_FLAGS_HEURISTICS = MarlinProperties.isUseTileFlagsWithHeuristics();
private static final int ALL_BUT_LSB = 0xfffffffe;
private static final int ERR_STEP_MAX = 0x7fffffff; // = 2^31 - 1
private static final int ALL_BUT_LSB = 0xFFFFFFFE;
private static final int ERR_STEP_MAX = 0x7FFFFFFF; // = 2^31 - 1
private static final double POWER_2_TO_32 = 0x1.0p32;
private static final double POWER_2_TO_32 = 0x1.0p32d;
// use float to make tosubpix methods faster (no int to float conversion)
public static final float F_SUBPIXEL_POSITIONS_X
= (float) SUBPIXEL_POSITIONS_X;
public static final float F_SUBPIXEL_POSITIONS_Y
= (float) SUBPIXEL_POSITIONS_Y;
public static final int SUBPIXEL_MASK_X = SUBPIXEL_POSITIONS_X - 1;
public static final int SUBPIXEL_MASK_Y = SUBPIXEL_POSITIONS_Y - 1;
static final float SUBPIXEL_SCALE_X = (float) SUBPIXEL_POSITIONS_X;
static final float SUBPIXEL_SCALE_Y = (float) SUBPIXEL_POSITIONS_Y;
static final int SUBPIXEL_MASK_X = SUBPIXEL_POSITIONS_X - 1;
static final int SUBPIXEL_MASK_Y = SUBPIXEL_POSITIONS_Y - 1;
// number of subpixels corresponding to a tile line
private static final int SUBPIXEL_TILE
= TILE_SIZE << SUBPIXEL_LG_POSITIONS_Y;
= TILE_H << SUBPIXEL_LG_POSITIONS_Y;
// 2048 (pixelSize) pixels (height) x 8 subpixels = 64K
static final int INITIAL_BUCKET_ARRAY
= INITIAL_PIXEL_DIM * SUBPIXEL_POSITIONS_Y;
// crossing capacity = edges count / 8 ~ 512
static final int INITIAL_CROSSING_COUNT = INITIAL_EDGES_COUNT >> 3;
// crossing capacity = edges count / 4 ~ 1024
static final int INITIAL_CROSSING_COUNT = INITIAL_EDGES_COUNT >> 2;
public static final int WIND_EVEN_ODD = 0;
public static final int WIND_NON_ZERO = 1;
@ -80,20 +77,20 @@ final class Renderer implements PathConsumer2D, MarlinConst {
// curve break into lines
// cubic error in subpixels to decrement step
private static final float CUB_DEC_ERR_SUBPIX
= 2.5f * (NORM_SUBPIXELS / 8f); // 2.5 subpixel for typical 8x8 subpixels
= MarlinProperties.getCubicDecD2() * (NORM_SUBPIXELS / 8.0f); // 1 pixel
// cubic error in subpixels to increment step
private static final float CUB_INC_ERR_SUBPIX
= 1f * (NORM_SUBPIXELS / 8f); // 1 subpixel for typical 8x8 subpixels
= MarlinProperties.getCubicIncD1() * (NORM_SUBPIXELS / 8.0f); // 0.4 pixel
// cubic bind length to decrement step = 8 * error in subpixels
// pisces: 20 / 8
// openjfx pisces: 8 / 3.2
// multiply by 8 = error scale factor:
// TestNonAARasterization (JDK-8170879): cubics
// bad paths (59294/100000 == 59,29%, 94335 bad pixels (avg = 1,59), 3966 warnings (avg = 0,07)
// cubic bind length to decrement step
public static final float CUB_DEC_BND
= 8f * CUB_DEC_ERR_SUBPIX; // 20f means 2.5 subpixel error
// cubic bind length to increment step = 8 * error in subpixels
= 8.0f * CUB_DEC_ERR_SUBPIX;
// cubic bind length to increment step
public static final float CUB_INC_BND
= 8f * CUB_INC_ERR_SUBPIX; // 8f means 1 subpixel error
= 8.0f * CUB_INC_ERR_SUBPIX;
// cubic countlg
public static final int CUB_COUNT_LG = 2;
@ -104,21 +101,23 @@ final class Renderer implements PathConsumer2D, MarlinConst {
// cubic count^3 = 8^countlg
private static final int CUB_COUNT_3 = 1 << (3 * CUB_COUNT_LG);
// cubic dt = 1 / count
private static final float CUB_INV_COUNT = 1f / CUB_COUNT;
private static final float CUB_INV_COUNT = 1.0f / CUB_COUNT;
// cubic dt^2 = 1 / count^2 = 1 / 4^countlg
private static final float CUB_INV_COUNT_2 = 1f / CUB_COUNT_2;
private static final float CUB_INV_COUNT_2 = 1.0f / CUB_COUNT_2;
// cubic dt^3 = 1 / count^3 = 1 / 8^countlg
private static final float CUB_INV_COUNT_3 = 1f / CUB_COUNT_3;
private static final float CUB_INV_COUNT_3 = 1.0f / CUB_COUNT_3;
// quad break into lines
// quadratic error in subpixels
private static final float QUAD_DEC_ERR_SUBPIX
= 1f * (NORM_SUBPIXELS / 8f); // 1 subpixel for typical 8x8 subpixels
= MarlinProperties.getQuadDecD2() * (NORM_SUBPIXELS / 8.0f); // 0.5 pixel
// quadratic bind length to decrement step = 8 * error in subpixels
// pisces and openjfx pisces: 32
// TestNonAARasterization (JDK-8170879): quads
// bad paths (62916/100000 == 62,92%, 103818 bad pixels (avg = 1,65), 6514 warnings (avg = 0,10)
// quadratic bind length to decrement step
public static final float QUAD_DEC_BND
= 8f * QUAD_DEC_ERR_SUBPIX; // 8f means 1 subpixel error
= 8.0f * QUAD_DEC_ERR_SUBPIX;
//////////////////////////////////////////////////////////////////////////////
// SCAN LINE
@ -157,7 +156,7 @@ final class Renderer implements PathConsumer2D, MarlinConst {
private float edgeMinX = Float.POSITIVE_INFINITY;
private float edgeMaxX = Float.NEGATIVE_INFINITY;
// edges [floats|ints] stored in off-heap memory
// edges [ints] stored in off-heap memory
private final OffHeapArray edges;
private int[] edgeBuckets;
@ -165,8 +164,6 @@ final class Renderer implements PathConsumer2D, MarlinConst {
// used range for edgeBuckets / edgeBucketCounts
private int buckets_minY;
private int buckets_maxY;
// sum of each edge delta Y (subpixels)
private int edgeSumDeltaY;
// edgeBuckets ref (clean)
private final IntArrayCache.Reference edgeBuckets_ref;
@ -183,13 +180,13 @@ final class Renderer implements PathConsumer2D, MarlinConst {
int count = 1; // dt = 1 / count
// maximum(ddX|Y) = norm(dbx, dby) * dt^2 (= 1)
float maxDD = FloatMath.max(Math.abs(c.dbx), Math.abs(c.dby));
float maxDD = Math.abs(c.dbx) + Math.abs(c.dby);
final float _DEC_BND = QUAD_DEC_BND;
while (maxDD >= _DEC_BND) {
// divide step by half:
maxDD /= 4f; // error divided by 2^2 = 4
maxDD /= 4.0f; // error divided by 2^2 = 4
count <<= 1;
if (DO_STATS) {
@ -199,7 +196,7 @@ final class Renderer implements PathConsumer2D, MarlinConst {
int nL = 0; // line count
if (count > 1) {
final float icount = 1f / count; // dt
final float icount = 1.0f / count; // dt
final float icount2 = icount * icount; // dt^2
final float ddx = c.dbx * icount2;
@ -246,8 +243,8 @@ final class Renderer implements PathConsumer2D, MarlinConst {
// the dx and dy refer to forward differencing variables, not the last
// coefficients of the "points" polynomial
float dddx, dddy, ddx, ddy, dx, dy;
dddx = 2f * c.dax * icount3;
dddy = 2f * c.day * icount3;
dddx = 2.0f * c.dax * icount3;
dddy = 2.0f * c.day * icount3;
ddx = dddx + c.dbx * icount2;
ddy = dddy + c.dby * icount2;
dx = c.ax * icount3 + c.bx * icount2 + c.cx * icount;
@ -262,13 +259,13 @@ final class Renderer implements PathConsumer2D, MarlinConst {
while (count > 0) {
// divide step by half:
while (Math.abs(ddx) >= _DEC_BND || Math.abs(ddy) >= _DEC_BND) {
dddx /= 8f;
dddy /= 8f;
ddx = ddx/4f - dddx;
ddy = ddy/4f - dddy;
dx = (dx - ddx) / 2f;
dy = (dy - ddy) / 2f;
while (Math.abs(ddx) + Math.abs(ddy) >= _DEC_BND) {
dddx /= 8.0f;
dddy /= 8.0f;
ddx = ddx / 4.0f - dddx;
ddy = ddy / 4.0f - dddy;
dx = (dx - ddx) / 2.0f;
dy = (dy - ddy) / 2.0f;
count <<= 1;
if (DO_STATS) {
@ -277,19 +274,16 @@ final class Renderer implements PathConsumer2D, MarlinConst {
}
// double step:
// TODO: why use first derivative dX|Y instead of second ddX|Y ?
// both scale changes should use speed or acceleration to have the same metric.
// can only do this on even "count" values, because we must divide count by 2
while (count % 2 == 0
&& Math.abs(dx) <= _INC_BND && Math.abs(dy) <= _INC_BND)
&& Math.abs(dx) + Math.abs(dy) <= _INC_BND)
{
dx = 2f * dx + ddx;
dy = 2f * dy + ddy;
ddx = 4f * (ddx + dddx);
ddy = 4f * (ddy + dddy);
dddx *= 8f;
dddy *= 8f;
dx = 2.0f * dx + ddx;
dy = 2.0f * dy + ddy;
ddx = 4.0f * (ddx + dddx);
ddy = 4.0f * (ddy + dddy);
dddx *= 8.0f;
dddy *= 8.0f;
count >>= 1;
if (DO_STATS) {
@ -337,7 +331,7 @@ final class Renderer implements PathConsumer2D, MarlinConst {
x1 = tmp;
}
// convert subpixel coordinates (float) into pixel positions (int)
// convert subpixel coordinates [float] into pixel positions [int]
// The index of the pixel that holds the next HPC is at ceil(trueY - 0.5)
// Since y1 and y2 are biased by -0.5 in tosubpixy(), this is simply
@ -361,7 +355,7 @@ final class Renderer implements PathConsumer2D, MarlinConst {
return;
}
// edge min/max X/Y are in subpixel space (inclusive) within bounds:
// edge min/max X/Y are in subpixel space (half-open interval):
// note: Use integer crossings to ensure consistent range within
// edgeBuckets / edgeBucketCounts arrays in case of NaN values (int = 0)
if (firstCrossing < edgeMinY) {
@ -376,7 +370,7 @@ final class Renderer implements PathConsumer2D, MarlinConst {
final double y1d = y1;
final double slope = (x1d - x2) / (y1d - y2);
if (slope >= 0.0) { // <==> x1 < x2
if (slope >= 0.0d) { // <==> x1 < x2
if (x1 < edgeMinX) {
edgeMinX = x1;
}
@ -439,13 +433,13 @@ final class Renderer implements PathConsumer2D, MarlinConst {
// long x1_fixed = x1_intercept * 2^32; (fixed point 32.32 format)
// curx = next VPC = fixed_floor(x1_fixed - 2^31 + 2^32 - 1)
// = fixed_floor(x1_fixed + 2^31 - 1)
// = fixed_floor(x1_fixed + 0x7fffffff)
// and error = fixed_fract(x1_fixed + 0x7fffffff)
// = fixed_floor(x1_fixed + 0x7FFFFFFF)
// and error = fixed_fract(x1_fixed + 0x7FFFFFFF)
final double x1_intercept = x1d + (firstCrossing - y1d) * slope;
// inlined scalb(x1_intercept, 32):
final long x1_fixed_biased = ((long) (POWER_2_TO_32 * x1_intercept))
+ 0x7fffffffL;
+ 0x7FFFFFFFL;
// curx:
// last bit corresponds to the orientation
_unsafe.putInt(addr, (((int) (x1_fixed_biased >> 31L)) & ALL_BUT_LSB) | or);
@ -474,7 +468,7 @@ final class Renderer implements PathConsumer2D, MarlinConst {
// pointer from bucket
_unsafe.putInt(addr, _edgeBuckets[bucketIdx]);
addr += SIZE_INT;
// y max (inclusive)
// y max (exclusive)
_unsafe.putInt(addr, lastCrossing);
// Update buckets:
@ -484,9 +478,6 @@ final class Renderer implements PathConsumer2D, MarlinConst {
// last bit means edge end
_edgeBucketCounts[lastCrossing - _boundsMinY] |= 0x1;
// update sum of delta Y (subpixels):
edgeSumDeltaY += (lastCrossing - firstCrossing);
// update free pointer (ie length in bytes)
_edges.used += _SIZEOF_EDGE_BYTES;
@ -568,8 +559,8 @@ final class Renderer implements PathConsumer2D, MarlinConst {
Renderer init(final int pix_boundsX, final int pix_boundsY,
final int pix_boundsWidth, final int pix_boundsHeight,
final int windingRule) {
final int windingRule)
{
this.windingRule = windingRule;
// bounds as half-open intervals: minX <= x < maxX and minY <= y < maxY
@ -611,8 +602,6 @@ final class Renderer implements PathConsumer2D, MarlinConst {
activeEdgeMaxUsed = 0;
edges.used = 0;
edgeSumDeltaY = 0;
return this; // fluent API
}
@ -669,15 +658,17 @@ final class Renderer implements PathConsumer2D, MarlinConst {
if (DO_MONITORS) {
rdrCtx.stats.mon_rdr_endRendering.stop();
}
// recycle the RendererContext instance
MarlinRenderingEngine.returnRendererContext(rdrCtx);
}
private static float tosubpixx(final float pix_x) {
return F_SUBPIXEL_POSITIONS_X * pix_x;
return SUBPIXEL_SCALE_X * pix_x;
}
private static float tosubpixy(final float pix_y) {
// shift y by -0.5 for fast ceil(y - 0.5):
return F_SUBPIXEL_POSITIONS_Y * pix_y - 0.5f;
return SUBPIXEL_SCALE_Y * pix_y - 0.5f;
}
@Override
@ -702,8 +693,8 @@ final class Renderer implements PathConsumer2D, MarlinConst {
@Override
public void curveTo(float x1, float y1,
float x2, float y2,
float x3, float y3)
float x2, float y2,
float x3, float y3)
{
final float xe = tosubpixx(x3);
final float ye = tosubpixy(y3);
@ -969,8 +960,8 @@ final class Renderer implements PathConsumer2D, MarlinConst {
// get the pointer to the edge
ecur = _edgePtrs[i];
/* convert subpixel coordinates (float) into pixel
positions (int) for coming scanline */
/* convert subpixel coordinates into pixel
positions for coming scanline */
/* note: it is faster to always update edges even
if it is removed from AEL for coming or last scanline */
@ -1069,8 +1060,8 @@ final class Renderer implements PathConsumer2D, MarlinConst {
// get the pointer to the edge
ecur = _edgePtrs[i];
/* convert subpixel coordinates (float) into pixel
positions (int) for coming scanline */
/* convert subpixel coordinates into pixel
positions for coming scanline */
/* note: it is faster to always update edges even
if it is removed from AEL for coming or last scanline */
@ -1176,7 +1167,14 @@ final class Renderer implements PathConsumer2D, MarlinConst {
// TODO: perform line clipping on left-right sides
// to avoid such bound checks:
x0 = (prev > bboxx0) ? prev : bboxx0;
x1 = (curx < bboxx1) ? curx : bboxx1;
if (curx < bboxx1) {
x1 = curx;
} else {
x1 = bboxx1;
// skip right side (fast exit loop):
i = numCrossings;
}
if (x0 < x1) {
x0 -= bboxx0; // turn x0, x1 from coords to indices
@ -1193,7 +1191,8 @@ final class Renderer implements PathConsumer2D, MarlinConst {
if (useBlkFlags) {
// flag used blocks:
_blkFlags[pix_x >> _BLK_SIZE_LG] = 1;
// note: block processing handles extra pixel:
_blkFlags[pix_x >> _BLK_SIZE_LG] = 1;
}
} else {
tmp = (x0 & _SUBPIXEL_MASK_X);
@ -1212,6 +1211,7 @@ final class Renderer implements PathConsumer2D, MarlinConst {
if (useBlkFlags) {
// flag used blocks:
// note: block processing handles extra pixel:
_blkFlags[pix_x >> _BLK_SIZE_LG] = 1;
_blkFlags[pix_xmax >> _BLK_SIZE_LG] = 1;
}
@ -1237,7 +1237,14 @@ final class Renderer implements PathConsumer2D, MarlinConst {
// TODO: perform line clipping on left-right sides
// to avoid such bound checks:
x0 = (prev > bboxx0) ? prev : bboxx0;
x1 = (curx < bboxx1) ? curx : bboxx1;
if (curx < bboxx1) {
x1 = curx;
} else {
x1 = bboxx1;
// skip right side (fast exit loop):
i = numCrossings;
}
if (x0 < x1) {
x0 -= bboxx0; // turn x0, x1 from coords to indices
@ -1254,7 +1261,8 @@ final class Renderer implements PathConsumer2D, MarlinConst {
if (useBlkFlags) {
// flag used blocks:
_blkFlags[pix_x >> _BLK_SIZE_LG] = 1;
// note: block processing handles extra pixel:
_blkFlags[pix_x >> _BLK_SIZE_LG] = 1;
}
} else {
tmp = (x0 & _SUBPIXEL_MASK_X);
@ -1273,6 +1281,7 @@ final class Renderer implements PathConsumer2D, MarlinConst {
if (useBlkFlags) {
// flag used blocks:
// note: block processing handles extra pixel:
_blkFlags[pix_x >> _BLK_SIZE_LG] = 1;
_blkFlags[pix_xmax >> _BLK_SIZE_LG] = 1;
}
@ -1306,9 +1315,12 @@ final class Renderer implements PathConsumer2D, MarlinConst {
if (maxX >= minX) {
// note: alpha array will be zeroed by copyAARow()
// +2 because alpha [pix_minX; pix_maxX+1]
// +1 because alpha [pix_minX; pix_maxX[
// fix range [x0; x1[
copyAARow(_alpha, lastY, minX, maxX + 2, useBlkFlags);
// note: if x1=bboxx1, then alpha is written up to bboxx1+1
// inclusive: alpha[bboxx1] ignored, alpha[bboxx1+1] == 0
// (normally so never cleared below)
copyAARow(_alpha, lastY, minX, maxX + 1, useBlkFlags);
// speculative for next pixel row (scanline coherence):
if (_enableBlkFlagsHeuristics) {
@ -1350,9 +1362,12 @@ final class Renderer implements PathConsumer2D, MarlinConst {
if (maxX >= minX) {
// note: alpha array will be zeroed by copyAARow()
// +2 because alpha [pix_minX; pix_maxX+1]
// +1 because alpha [pix_minX; pix_maxX[
// fix range [x0; x1[
copyAARow(_alpha, y, minX, maxX + 2, useBlkFlags);
// note: if x1=bboxx1, then alpha is written up to bboxx1+1
// inclusive: alpha[bboxx1] ignored then cleared and
// alpha[bboxx1+1] == 0 (normally so never cleared after)
copyAARow(_alpha, y, minX, maxX + 1, useBlkFlags);
} else if (y != lastY) {
_cache.clearAARow(y);
}
@ -1375,36 +1390,26 @@ final class Renderer implements PathConsumer2D, MarlinConst {
return false; // undefined edges bounds
}
final int _boundsMinY = boundsMinY;
final int _boundsMaxY = boundsMaxY;
// bounds as inclusive intervals
// bounds as half-open intervals
final int spminX = FloatMath.max(FloatMath.ceil_int(edgeMinX - 0.5f), boundsMinX);
final int spmaxX = FloatMath.min(FloatMath.ceil_int(edgeMaxX - 0.5f), boundsMaxX - 1);
final int spmaxX = FloatMath.min(FloatMath.ceil_int(edgeMaxX - 0.5f), boundsMaxX);
// edge Min/Max Y are already rounded to subpixels within bounds:
final int spminY = edgeMinY;
final int spmaxY;
int maxY = edgeMaxY;
final int spmaxY = edgeMaxY;
if (maxY <= _boundsMaxY - 1) {
spmaxY = maxY;
} else {
spmaxY = _boundsMaxY - 1;
maxY = _boundsMaxY;
}
buckets_minY = spminY - _boundsMinY;
buckets_maxY = maxY - _boundsMinY;
buckets_minY = spminY - boundsMinY;
buckets_maxY = spmaxY - boundsMinY;
if (DO_LOG_BOUNDS) {
MarlinUtils.logInfo("edgesXY = [" + edgeMinX + " ... " + edgeMaxX
+ "][" + edgeMinY + " ... " + edgeMaxY + "]");
+ "[ [" + edgeMinY + " ... " + edgeMaxY + "[");
MarlinUtils.logInfo("spXY = [" + spminX + " ... " + spmaxX
+ "][" + spminY + " ... " + spmaxY + "]");
+ "[ [" + spminY + " ... " + spmaxY + "[");
}
// test clipping for shapes out of bounds
if ((spminX > spmaxX) || (spminY > spmaxY)) {
if ((spminX >= spmaxX) || (spminY >= spmaxY)) {
return false;
}
@ -1419,7 +1424,7 @@ final class Renderer implements PathConsumer2D, MarlinConst {
final int pmaxY = (spmaxY + SUBPIXEL_MASK_Y) >> SUBPIXEL_LG_POSITIONS_Y;
// store BBox to answer ptg.getBBox():
this.cache.init(pminX, pminY, pmaxX, pmaxY, edgeSumDeltaY);
this.cache.init(pminX, pminY, pmaxX, pmaxY);
// Heuristics for using block flags:
if (ENABLE_BLOCK_FLAGS) {
@ -1429,9 +1434,9 @@ final class Renderer implements PathConsumer2D, MarlinConst {
if (enableBlkFlags) {
// ensure blockFlags array is large enough:
// note: +2 to ensure enough space left at end
final int nxTiles = ((pmaxX - pminX) >> TILE_SIZE_LG) + 2;
if (nxTiles > INITIAL_ARRAY) {
blkFlags = blkFlags_ref.getArray(nxTiles);
final int blkLen = ((pmaxX - pminX) >> BLOCK_SIZE_LG) + 2;
if (blkLen > INITIAL_ARRAY) {
blkFlags = blkFlags_ref.getArray(blkLen);
}
}
}
@ -1446,7 +1451,7 @@ final class Renderer implements PathConsumer2D, MarlinConst {
// inclusive:
bbox_spminY = spminY;
// exclusive:
bbox_spmaxY = FloatMath.min(spmaxY + 1, pmaxY << SUBPIXEL_LG_POSITIONS_Y);
bbox_spmaxY = spmaxY;
if (DO_LOG_BOUNDS) {
MarlinUtils.logInfo("pXY = [" + pminX + " ... " + pmaxX
@ -1504,6 +1509,9 @@ final class Renderer implements PathConsumer2D, MarlinConst {
final int pix_y, final int pix_from, final int pix_to,
final boolean useBlockFlags)
{
if (DO_MONITORS) {
rdrCtx.stats.mon_rdr_copyAARow.start();
}
if (useBlockFlags) {
if (DO_STATS) {
rdrCtx.stats.hist_tile_generator_encoding.add(1);
@ -1515,5 +1523,8 @@ final class Renderer implements PathConsumer2D, MarlinConst {
}
cache.copyAARowNoRLE(alphaRow, pix_y, pix_from, pix_to);
}
if (DO_MONITORS) {
rdrCtx.stats.mon_rdr_copyAARow.stop();
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 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
@ -35,7 +35,7 @@ import sun.java2d.marlin.MarlinRenderingEngine.NormalizingPathIterator;
/**
* This class is a renderer context dedicated to a single thread
*/
final class RendererContext extends ReentrantContext implements MarlinConst {
final class RendererContext extends ReentrantContext implements IRendererContext {
// RendererContext creation counter
private static final AtomicInteger CTX_COUNT = new AtomicInteger(1);
@ -121,7 +121,7 @@ final class RendererContext extends ReentrantContext implements MarlinConst {
// Renderer:
cache = new MarlinCache(this);
renderer = new Renderer(this); // needs MarlinCache from rdrCtx.cache
ptg = new MarlinTileGenerator(renderer);
ptg = new MarlinTileGenerator(stats, renderer, cache);
stroker = new Stroker(this);
dasher = new Dasher(this);
@ -174,14 +174,21 @@ final class RendererContext extends ReentrantContext implements MarlinConst {
return p2d;
}
OffHeapArray newOffHeapArray(final long initialSize) {
@Override
public RendererStats stats() {
return stats;
}
@Override
public OffHeapArray newOffHeapArray(final long initialSize) {
if (DO_STATS) {
stats.totalOffHeapInitial += initialSize;
}
return new OffHeapArray(cleanerObj, initialSize);
}
IntArrayCache.Reference newCleanIntArrayRef(final int initialSize) {
@Override
public IntArrayCache.Reference newCleanIntArrayRef(final int initialSize) {
return cleanIntCache.createRef(initialSize);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2016, Oracle and/or its affiliates. All rights reserved.
* 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
@ -26,12 +26,8 @@
package sun.java2d.marlin;
import java.util.Arrays;
import static java.lang.Math.ulp;
import static java.lang.Math.sqrt;
import sun.awt.geom.PathConsumer2D;
import sun.java2d.marlin.Curve.BreakPtrIterator;
// 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
@ -75,7 +71,7 @@ final class Stroker implements PathConsumer2D, MarlinConst {
// 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/65536f;
private static final float ROUND_JOIN_THRESHOLD = 1000.0f/65536.0f;
private static final float C = 0.5522847498307933f;
@ -112,9 +108,8 @@ final class Stroker implements PathConsumer2D, MarlinConst {
private final PolyStack reverse;
// This is where the curve to be processed is put. We give it
// enough room to store 2 curves: one for the current subdivision, the
// other for the rest of the curve.
private final float[] middle = new float[2 * 8];
// 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];
@ -158,8 +153,8 @@ final class Stroker implements PathConsumer2D, MarlinConst {
{
this.out = pc2d;
this.lineWidth2 = lineWidth / 2f;
this.invHalfLineWidth2Sq = 1f / (2f * lineWidth2 * lineWidth2);
this.lineWidth2 = lineWidth / 2.0f;
this.invHalfLineWidth2Sq = 1.0f / (2.0f * lineWidth2 * lineWidth2);
this.capStyle = capStyle;
this.joinStyle = joinStyle;
@ -182,14 +177,14 @@ final class Stroker implements PathConsumer2D, MarlinConst {
if (DO_CLEAN_DIRTY) {
// Force zero-fill dirty arrays:
Arrays.fill(offset0, 0f);
Arrays.fill(offset1, 0f);
Arrays.fill(offset2, 0f);
Arrays.fill(miter, 0f);
Arrays.fill(middle, 0f);
Arrays.fill(lp, 0f);
Arrays.fill(rp, 0f);
Arrays.fill(subdivTs, 0f);
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);
}
}
@ -197,11 +192,11 @@ final class Stroker implements PathConsumer2D, MarlinConst {
final float w, final float[] m)
{
float len = lx*lx + ly*ly;
if (len == 0f) {
m[0] = 0f;
m[1] = 0f;
if (len == 0.0f) {
m[0] = 0.0f;
m[1] = 0.0f;
} else {
len = (float) sqrt(len);
len = (float) Math.sqrt(len);
m[0] = (ly * w) / len;
m[1] = -(lx * w) / len;
}
@ -226,7 +221,7 @@ final class Stroker implements PathConsumer2D, MarlinConst {
boolean rev,
float threshold)
{
if ((omx == 0f && omy == 0f) || (mx == 0f && my == 0f)) {
if ((omx == 0.0f && omy == 0.0f) || (mx == 0.0f && my == 0.0f)) {
return;
}
@ -258,7 +253,7 @@ final class Stroker implements PathConsumer2D, MarlinConst {
// If it is >=0, we know that abs(ext) is <= 90 degrees, so we only
// need 1 curve to approximate the circle section that joins omx,omy
// and mx,my.
final int numCurves = (cosext >= 0f) ? 1 : 2;
final int numCurves = (cosext >= 0.0f) ? 1 : 2;
switch (numCurves) {
case 1:
@ -280,7 +275,7 @@ final class Stroker implements PathConsumer2D, MarlinConst {
// this normal's length is at least 0.5 and at most sqrt(2)/2 (because
// we know the angle of the arc is > 90 degrees).
float nx = my - omy, ny = omx - mx;
float nlen = (float) sqrt(nx*nx + ny*ny);
float nlen = (float) Math.sqrt(nx*nx + ny*ny);
float scale = lineWidth2/nlen;
float mmx = nx * scale, mmy = ny * scale;
@ -318,8 +313,8 @@ final class Stroker implements PathConsumer2D, MarlinConst {
// define the bezier curve we're computing.
// It is computed using the constraints that P1-P0 and P3-P2 are parallel
// to the arc tangents at the endpoints, and that |P1-P0|=|P3-P2|.
float cv = (float) ((4.0 / 3.0) * sqrt(0.5 - cosext2) /
(1.0 + sqrt(cosext2 + 0.5)));
float cv = (float) ((4.0d / 3.0d) * Math.sqrt(0.5d - cosext2) /
(1.0d + Math.sqrt(cosext2 + 0.5d)));
// if clockwise, we need to negate cv.
if (rev) { // rev is equivalent to isCW(omx, omy, mx, my)
cv = -cv;
@ -348,20 +343,28 @@ final class Stroker implements PathConsumer2D, MarlinConst {
cx - mx, cy - my);
}
// Put the intersection point of the lines (x0, y0) -> (x1, y1)
// and (x0p, y0p) -> (x1p, y1p) in m[off] and m[off+1].
// If the lines are parallel, it will put a non finite number in m.
private static void computeIntersection(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)
// 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 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;
// if this is 0, the lines are parallel. If they go in the
// same direction, there is no intersection so m[off] and
// m[off+1] will contain infinity, so no miter will be drawn.
// If they go in the same direction that means that the start of the
// current segment and the end of the previous segment have the same
// tangent, in which case this method won't even be involved in
// 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;
float t = x10p*(y0-y0p) - y10p*(x0-x0p);
t /= den;
@ -369,6 +372,40 @@ final class Stroker implements PathConsumer2D, MarlinConst {
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;
// if this is 0, the lines are parallel. If they go in the
// same direction, there is no intersection so m[off] and
// m[off+1] will contain infinity, so no miter will be drawn.
// If they go in the same direction that means that the start of the
// current segment and the end of the previous segment have the same
// tangent, in which case this method won't even be involved in
// 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,
@ -376,8 +413,8 @@ final class Stroker implements PathConsumer2D, MarlinConst {
boolean rev)
{
if ((mx == omx && my == omy) ||
(pdx == 0f && pdy == 0f) ||
(dx == 0f && dy == 0f))
(pdx == 0.0f && pdy == 0.0f) ||
(dx == 0.0f && dy == 0.0f))
{
return;
}
@ -389,9 +426,9 @@ final class Stroker implements PathConsumer2D, MarlinConst {
my = -my;
}
computeIntersection((x0 - pdx) + omx, (y0 - pdy) + omy, x0 + omx, y0 + omy,
(dx + x0) + mx, (dy + y0) + my, x0 + mx, y0 + my,
miter, 0);
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];
@ -414,8 +451,8 @@ final class Stroker implements PathConsumer2D, MarlinConst {
}
this.sx0 = this.cx0 = x0;
this.sy0 = this.cy0 = y0;
this.cdx = this.sdx = 1f;
this.cdy = this.sdy = 0f;
this.cdx = this.sdx = 1.0f;
this.cdy = this.sdy = 0.0f;
this.prev = MOVE_TO;
}
@ -423,8 +460,8 @@ final class Stroker implements PathConsumer2D, MarlinConst {
public void lineTo(float x1, float y1) {
float dx = x1 - cx0;
float dy = y1 - cy0;
if (dx == 0f && dy == 0f) {
dx = 1f;
if (dx == 0.0f && dy == 0.0f) {
dx = 1.0f;
}
computeOffset(dx, dy, lineWidth2, offset0);
final float mx = offset0[0];
@ -454,10 +491,10 @@ final class Stroker implements PathConsumer2D, MarlinConst {
return;
}
emitMoveTo(cx0, cy0 - lineWidth2);
this.cmx = this.smx = 0f;
this.cmx = this.smx = 0.0f;
this.cmy = this.smy = -lineWidth2;
this.cdx = this.sdx = 1f;
this.cdy = this.sdy = 0f;
this.cdx = this.sdx = 1.0f;
this.cdy = this.sdy = 0.0f;
finish();
return;
}
@ -640,7 +677,7 @@ final class Stroker implements PathConsumer2D, MarlinConst {
{
// 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 winden
// 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
@ -657,8 +694,8 @@ final class Stroker implements PathConsumer2D, MarlinConst {
// 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, 6f * ulp(y2));
final boolean p3eqp4 = within(x3,y3,x4,y4, 6f * ulp(y4));
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;
@ -674,7 +711,7 @@ final class Stroker implements PathConsumer2D, MarlinConst {
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, 4f * ulp(dotsq))) {
if (Helpers.within(dotsq, l1sq * l4sq, 4.0f * Math.ulp(dotsq))) {
getLineOffsets(x1, y1, x4, y4, leftOff, rightOff);
return 4;
}
@ -726,8 +763,8 @@ final class Stroker implements PathConsumer2D, MarlinConst {
// getting the inverse of the matrix above. Then we use [c1,c2] to compute
// p2p and p3p.
float x = (x1 + 3f * (x2 + x3) + x4) / 8f;
float y = (y1 + 3f * (y2 + y3) + y4) / 8f;
float x = (x1 + 3.0f * (x2 + x3) + x4) / 8.0f;
float y = (y1 + 3.0f * (y2 + y3) + y4) / 8.0f;
// (dxm,dym) is some tangent of B at t=0.5. This means it's equal to
// c*B'(0.5) for some constant c.
float dxm = x3 + x4 - x1 - x2, dym = y3 + y4 - y1 - y2;
@ -745,10 +782,10 @@ final class Stroker implements PathConsumer2D, MarlinConst {
float x4p = x4 + offset2[0]; // end
float y4p = y4 + offset2[1]; // point
float invdet43 = 4f / (3f * (dx1 * dy4 - dy1 * dx4));
float invdet43 = 4.0f / (3.0f * (dx1 * dy4 - dy1 * dx4));
float two_pi_m_p1_m_p4x = 2f * xi - x1p - x4p;
float two_pi_m_p1_m_p4y = 2f * yi - y1p - y4p;
float two_pi_m_p1_m_p4x = 2.0f * xi - x1p - x4p;
float two_pi_m_p1_m_p4y = 2.0f * yi - y1p - y4p;
float c1 = invdet43 * (dy4 * two_pi_m_p1_m_p4x - dx4 * two_pi_m_p1_m_p4y);
float c2 = invdet43 * (dx1 * two_pi_m_p1_m_p4y - dy1 * two_pi_m_p1_m_p4x);
@ -764,11 +801,11 @@ final class Stroker implements PathConsumer2D, MarlinConst {
leftOff[6] = x4p; leftOff[7] = y4p;
x1p = x1 - offset0[0]; y1p = y1 - offset0[1];
xi = xi - 2f * offset1[0]; yi = yi - 2f * offset1[1];
xi = xi - 2.0f * offset1[0]; yi = yi - 2.0f * offset1[1];
x4p = x4 - offset2[0]; y4p = y4 - offset2[1];
two_pi_m_p1_m_p4x = 2f * xi - x1p - x4p;
two_pi_m_p1_m_p4y = 2f * yi - y1p - y4p;
two_pi_m_p1_m_p4x = 2.0f * xi - x1p - x4p;
two_pi_m_p1_m_p4y = 2.0f * yi - y1p - y4p;
c1 = invdet43 * (dy4 * two_pi_m_p1_m_p4x - dx4 * two_pi_m_p1_m_p4y);
c2 = invdet43 * (dx1 * two_pi_m_p1_m_p4y - dy1 * two_pi_m_p1_m_p4x);
@ -784,6 +821,8 @@ final class Stroker implements PathConsumer2D, MarlinConst {
return 8;
}
// 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)
@ -797,170 +836,54 @@ final class Stroker implements PathConsumer2D, MarlinConst {
final float dx1 = x2 - x1;
final float dy1 = y2 - y1;
// this computes the offsets at t = 0, 1
// 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.
// 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;
}
// this computes the offsets at t=0, 0.5, 1, using the property that
// for any bezier curve the vectors p2-p1 and p4-p3 are parallel to
// the (dx/dt, dy/dt) vectors at the endpoints.
computeOffset(dx1, dy1, lineWidth2, offset0);
computeOffset(dx3, dy3, lineWidth2, offset1);
leftOff[0] = x1 + offset0[0]; leftOff[1] = y1 + offset0[1];
leftOff[4] = x3 + offset1[0]; leftOff[5] = y3 + offset1[1];
rightOff[0] = x1 - offset0[0]; rightOff[1] = y1 - offset0[1];
rightOff[4] = x3 - offset1[0]; rightOff[5] = y3 - offset1[1];
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;
float x1p = leftOff[0]; // start
float y1p = leftOff[1]; // point
float x3p = leftOff[4]; // end
float y3p = leftOff[5]; // point
// Corner cases:
// 1. If the two control vectors are parallel, we'll end up with NaN's
// in leftOff (and rightOff in the body of the if below), so we'll
// do getLineOffsets, which is right.
// 2. If the first or second two points are equal, then (dx1,dy1)==(0,0)
// or (dx3,dy3)==(0,0), so (x1p, y1p)==(x1p+dx1, y1p+dy1)
// or (x3p, y3p)==(x3p-dx3, y3p-dy3), which means that
// computeIntersection will put NaN's in leftOff and right off, and
// we will do getLineOffsets, which is right.
computeIntersection(x1p, y1p, x1p+dx1, y1p+dy1, x3p, y3p, x3p-dx3, y3p-dy3, leftOff, 2);
float cx = leftOff[2];
float cy = leftOff[3];
if (!(isFinite(cx) && isFinite(cy))) {
// maybe the right path is not degenerate.
x1p = rightOff[0];
y1p = rightOff[1];
x3p = rightOff[4];
y3p = rightOff[5];
computeIntersection(x1p, y1p, x1p+dx1, y1p+dy1, x3p, y3p, x3p-dx3, y3p-dy3, rightOff, 2);
cx = rightOff[2];
cy = rightOff[3];
if (!(isFinite(cx) && isFinite(cy))) {
// both are degenerate. This curve is a line.
getLineOffsets(x1, y1, x3, y3, leftOff, rightOff);
return 4;
}
// {left,right}Off[0,1,4,5] are already set to the correct values.
leftOff[2] = 2f * x2 - cx;
leftOff[3] = 2f * y2 - cy;
return 6;
}
// rightOff[2,3] = (x2,y2) - ((left_x2, left_y2) - (x2, y2))
// == 2*(x2, y2) - (left_x2, left_y2)
rightOff[2] = 2f * x2 - cx;
rightOff[3] = 2f * y2 - cy;
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;
}
private static boolean isFinite(float x) {
return (Float.NEGATIVE_INFINITY < x && x < Float.POSITIVE_INFINITY);
}
// If this class is compiled with ecj, then Hotspot crashes when OSR
// compiling this function. See bugs 7004570 and 6675699
// TODO: until those are fixed, we should work around that by
// manually inlining this into curveTo and quadTo.
/******************************* WORKAROUND **********************************
private void somethingTo(final int type) {
// need these so we can update the state at the end of this method
final float xf = middle[type-2], yf = middle[type-1];
float dxs = middle[2] - middle[0];
float dys = middle[3] - middle[1];
float dxf = middle[type - 2] - middle[type - 4];
float dyf = middle[type - 1] - middle[type - 3];
switch(type) {
case 6:
if ((dxs == 0f && dys == 0f) ||
(dxf == 0f && dyf == 0f)) {
dxs = dxf = middle[4] - middle[0];
dys = dyf = middle[5] - middle[1];
}
break;
case 8:
boolean p1eqp2 = (dxs == 0f && dys == 0f);
boolean p3eqp4 = (dxf == 0f && dyf == 0f);
if (p1eqp2) {
dxs = middle[4] - middle[0];
dys = middle[5] - middle[1];
if (dxs == 0f && dys == 0f) {
dxs = middle[6] - middle[0];
dys = middle[7] - middle[1];
}
}
if (p3eqp4) {
dxf = middle[6] - middle[2];
dyf = middle[7] - middle[3];
if (dxf == 0f && dyf == 0f) {
dxf = middle[6] - middle[0];
dyf = middle[7] - middle[1];
}
}
}
if (dxs == 0f && dys == 0f) {
// this happens iff the "curve" is just a point
lineTo(middle[0], middle[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) sqrt(dxs*dxs + dys*dys);
dxs /= len;
dys /= len;
}
if (Math.abs(dxf) < 0.1f && Math.abs(dyf) < 0.1f) {
float len = (float) sqrt(dxf*dxf + dyf*dyf);
dxf /= len;
dyf /= len;
}
computeOffset(dxs, dys, lineWidth2, offset0);
final float mx = offset0[0];
final float my = offset0[1];
drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, mx, my);
int nSplits = findSubdivPoints(curve, middle, subdivTs, type, lineWidth2);
int kind = 0;
BreakPtrIterator it = curve.breakPtsAtTs(middle, type, subdivTs, nSplits);
while(it.hasNext()) {
int curCurveOff = it.next();
switch (type) {
case 8:
kind = computeOffsetCubic(middle, curCurveOff, lp, rp);
break;
case 6:
kind = computeOffsetQuad(middle, curCurveOff, lp, rp);
break;
}
emitLineTo(lp[0], lp[1]);
switch(kind) {
case 8:
emitCurveTo(lp[2], lp[3], lp[4], lp[5], lp[6], lp[7]);
emitCurveToRev(rp[0], rp[1], rp[2], rp[3], rp[4], rp[5]);
break;
case 6:
emitQuadTo(lp[2], lp[3], lp[4], lp[5]);
emitQuadToRev(rp[0], rp[1], rp[2], rp[3]);
break;
case 4:
emitLineTo(lp[2], lp[3]);
emitLineTo(rp[0], rp[1], true);
break;
}
emitLineTo(rp[kind - 2], rp[kind - 1], true);
}
this.cmx = (lp[kind - 2] - rp[kind - 2]) / 2;
this.cmy = (lp[kind - 1] - rp[kind - 1]) / 2;
this.cdx = dxf;
this.cdy = dyf;
this.cx0 = xf;
this.cy0 = yf;
this.prev = DRAWING_OP_TO;
}
****************************** END WORKAROUND *******************************/
// 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.
@ -971,11 +894,11 @@ final class Stroker implements PathConsumer2D, MarlinConst {
final float y12 = pts[3] - pts[1];
// if the curve is already parallel to either axis we gain nothing
// from rotating it.
if (y12 != 0f && x12 != 0f) {
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) sqrt(x12 * x12 + y12 * y12);
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];
@ -1031,9 +954,6 @@ final class Stroker implements PathConsumer2D, MarlinConst {
mid[4] = x2; mid[5] = y2;
mid[6] = x3; mid[7] = y3;
// inlined version of somethingTo(8);
// See the TODO on somethingTo
// need these so we can update the state at the end of this method
final float xf = mid[6], yf = mid[7];
float dxs = mid[2] - mid[0];
@ -1041,12 +961,12 @@ final class Stroker implements PathConsumer2D, MarlinConst {
float dxf = mid[6] - mid[4];
float dyf = mid[7] - mid[5];
boolean p1eqp2 = (dxs == 0f && dys == 0f);
boolean p3eqp4 = (dxf == 0f && dyf == 0f);
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 == 0f && dys == 0f) {
if (dxs == 0.0f && dys == 0.0f) {
dxs = mid[6] - mid[0];
dys = mid[7] - mid[1];
}
@ -1054,12 +974,12 @@ final class Stroker implements PathConsumer2D, MarlinConst {
if (p3eqp4) {
dxf = mid[6] - mid[2];
dyf = mid[7] - mid[3];
if (dxf == 0f && dyf == 0f) {
if (dxf == 0.0f && dyf == 0.0f) {
dxf = mid[6] - mid[0];
dyf = mid[7] - mid[1];
}
}
if (dxs == 0f && dys == 0f) {
if (dxs == 0.0f && dys == 0.0f) {
// this happens if the "curve" is just a point
lineTo(mid[0], mid[1]);
return;
@ -1068,12 +988,12 @@ final class Stroker implements PathConsumer2D, MarlinConst {
// 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) sqrt(dxs*dxs + dys*dys);
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) sqrt(dxf*dxf + dyf*dyf);
float len = (float) Math.sqrt(dxf*dxf + dyf*dyf);
dxf /= len;
dyf /= len;
}
@ -1081,17 +1001,23 @@ final class Stroker implements PathConsumer2D, MarlinConst {
computeOffset(dxs, dys, lineWidth2, offset0);
drawJoin(cdx, cdy, cx0, cy0, dxs, dys, cmx, cmy, offset0[0], offset0[1]);
int nSplits = findSubdivPoints(curve, mid, subdivTs, 8, lineWidth2);
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;
BreakPtrIterator it = curve.breakPtsAtTs(mid, 8, subdivTs, nSplits);
while(it.hasNext()) {
int curCurveOff = it.next();
for (int i = 0, off = 0; i <= nSplits; i++, off += 6) {
kind = computeOffsetCubic(mid, off, l, r);
kind = computeOffsetCubic(mid, curCurveOff, l, r);
emitLineTo(l[0], l[1]);
switch(kind) {
@ -1108,8 +1034,8 @@ final class Stroker implements PathConsumer2D, MarlinConst {
emitLineToRev(r[kind - 2], r[kind - 1]);
}
this.cmx = (l[kind - 2] - r[kind - 2]) / 2f;
this.cmy = (l[kind - 1] - r[kind - 1]) / 2f;
this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0f;
this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0f;
this.cdx = dxf;
this.cdy = dyf;
this.cx0 = xf;
@ -1124,20 +1050,17 @@ final class Stroker implements PathConsumer2D, MarlinConst {
mid[2] = x1; mid[3] = y1;
mid[4] = x2; mid[5] = y2;
// inlined version of somethingTo(8);
// See the TODO on somethingTo
// need these so we can update the state at the end of this method
final float xf = mid[4], yf = mid[5];
float dxs = mid[2] - mid[0];
float dys = mid[3] - mid[1];
float dxf = mid[4] - mid[2];
float dyf = mid[5] - mid[3];
if ((dxs == 0f && dys == 0f) || (dxf == 0f && dyf == 0f)) {
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 == 0f && dys == 0f) {
if (dxs == 0.0f && dys == 0.0f) {
// this happens if the "curve" is just a point
lineTo(mid[0], mid[1]);
return;
@ -1145,12 +1068,12 @@ final class Stroker implements PathConsumer2D, MarlinConst {
// 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) sqrt(dxs*dxs + dys*dys);
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) sqrt(dxf*dxf + dyf*dyf);
float len = (float) Math.sqrt(dxf*dxf + dyf*dyf);
dxf /= len;
dyf /= len;
}
@ -1160,15 +1083,21 @@ final class Stroker implements PathConsumer2D, MarlinConst {
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;
BreakPtrIterator it = curve.breakPtsAtTs(mid, 6, subdivTs, nSplits);
while(it.hasNext()) {
int curCurveOff = it.next();
for (int i = 0, off = 0; i <= nSplits; i++, off += 4) {
kind = computeOffsetQuad(mid, off, l, r);
kind = computeOffsetQuad(mid, curCurveOff, l, r);
emitLineTo(l[0], l[1]);
switch(kind) {
@ -1185,8 +1114,8 @@ final class Stroker implements PathConsumer2D, MarlinConst {
emitLineToRev(r[kind - 2], r[kind - 1]);
}
this.cmx = (l[kind - 2] - r[kind - 2]) / 2f;
this.cmy = (l[kind - 1] - r[kind - 1]) / 2f;
this.cmx = (l[kind - 2] - r[kind - 2]) / 2.0f;
this.cmy = (l[kind - 1] - r[kind - 1]) / 2.0f;
this.cdx = dxf;
this.cdy = dyf;
this.cx0 = xf;
@ -1205,11 +1134,11 @@ final class Stroker implements PathConsumer2D, MarlinConst {
private static final byte TYPE_QUADTO = (byte) 1;
private static final byte TYPE_CUBICTO = (byte) 2;
// curves capacity = edges count (4096) = half edges x 2 (coords)
private static final int INITIAL_CURVES_COUNT = INITIAL_EDGES_COUNT;
// curves capacity = edges count (8192) = edges x 2 (coords)
private static final int INITIAL_CURVES_COUNT = INITIAL_EDGES_COUNT << 1;
// types capacity = half edges count (2048)
private static final int INITIAL_TYPES_COUNT = INITIAL_EDGES_COUNT >> 1;
// types capacity = edges count (4096)
private static final int INITIAL_TYPES_COUNT = INITIAL_EDGES_COUNT;
float[] curves;
int end;
@ -1235,10 +1164,10 @@ final class Stroker implements PathConsumer2D, MarlinConst {
PolyStack(final RendererContext rdrCtx) {
this.rdrCtx = rdrCtx;
curves_ref = rdrCtx.newDirtyFloatArrayRef(INITIAL_CURVES_COUNT); // 16K
curves_ref = rdrCtx.newDirtyFloatArrayRef(INITIAL_CURVES_COUNT); // 32K
curves = curves_ref.initial;
curveTypes_ref = rdrCtx.newDirtyByteArrayRef(INITIAL_TYPES_COUNT); // 2K
curveTypes_ref = rdrCtx.newDirtyByteArrayRef(INITIAL_TYPES_COUNT); // 4K
curveTypes = curveTypes_ref.initial;
numCurves = 0;
end = 0;
@ -1369,7 +1298,7 @@ final class Stroker implements PathConsumer2D, MarlinConst {
public String toString() {
String ret = "";
int nc = numCurves;
int e = end;
int last = end;
int len;
while (nc != 0) {
switch(curveTypes[--nc]) {
@ -1388,8 +1317,8 @@ final class Stroker implements PathConsumer2D, MarlinConst {
default:
len = 0;
}
e -= len;
ret += Arrays.toString(Arrays.copyOfRange(curves, e, e+len))
last -= len;
ret += Arrays.toString(Arrays.copyOfRange(curves, last, last+len))
+ "\n";
}
return ret;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2015, Oracle and/or its affiliates. All rights reserved.
* 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
@ -58,8 +58,8 @@ final class TransformingPathConsumer2D {
float myx = (float) at.getShearY();
float myy = (float) at.getScaleY();
if (mxy == 0f && myx == 0f) {
if (mxx == 1f && myy == 1f) {
if (mxy == 0.0f && myx == 0.0f) {
if (mxx == 1.0f && myy == 1.0f) {
return out;
} else {
return dt_DeltaScaleFilter.init(out, mxx, myy);
@ -84,8 +84,8 @@ final class TransformingPathConsumer2D {
float myx = (float) at.getShearY();
float myy = (float) at.getScaleY();
if (mxy == 0f && myx == 0f) {
if (mxx == 1f && myy == 1f) {
if (mxy == 0.0f && myx == 0.0f) {
if (mxx == 1.0f && myy == 1.0f) {
return out;
} else {
return iv_DeltaScaleFilter.init(out, 1.0f/mxx, 1.0f/myy);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2015, 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
@ -27,7 +27,7 @@ package sun.java2d.marlin;
public final class Version {
private static final String VERSION = "marlin-0.7.4-Unsafe-OpenJDK";
private static final String VERSION = "marlin-0.7.5-Unsafe-OpenJDK";
public static String getVersion() {
return VERSION;

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
* 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
@ -132,7 +132,7 @@ public abstract class RenderingEngine {
}
}
if (reImpl == null) {
final String marlinREClass = "sun.java2d.marlin.MarlinRenderingEngine";
final String marlinREClass = "sun.java2d.marlin.DMarlinRenderingEngine";
try {
Class<?> cls = Class.forName(marlinREClass);
reImpl = (RenderingEngine) cls.getConstructor().newInstance();