8143848: changeset was pushed with wrong attributions

Reviewed-by: prr
This commit is contained in:
Jim Graham 2015-11-23 14:56:43 -08:00
parent be42ed8890
commit 8571ce9809
30 changed files with 64 additions and 10618 deletions

View File

@ -1,216 +0,0 @@
/*
* Copyright (c) 2015, 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;
import static sun.java2d.marlin.MarlinUtils.logInfo;
public final class ArrayCache implements MarlinConst {
static final int BUCKETS = 4;
static final int MIN_ARRAY_SIZE = 4096;
static final int MAX_ARRAY_SIZE;
static final int MASK_CLR_1 = ~1;
// threshold to grow arrays only by (3/2) instead of 2
static final int THRESHOLD_ARRAY_SIZE;
static final int[] ARRAY_SIZES = new int[BUCKETS];
// dirty byte array sizes
static final int MIN_DIRTY_BYTE_ARRAY_SIZE = 32 * 2048; // 32px x 2048px
static final int MAX_DIRTY_BYTE_ARRAY_SIZE;
static final int[] DIRTY_BYTE_ARRAY_SIZES = new int[BUCKETS];
// large array thresholds:
static final long THRESHOLD_LARGE_ARRAY_SIZE;
static final long THRESHOLD_HUGE_ARRAY_SIZE;
// stats
private static int resizeInt = 0;
private static int resizeDirtyInt = 0;
private static int resizeDirtyFloat = 0;
private static int resizeDirtyByte = 0;
private static int oversize = 0;
static {
// initialize buckets for int/float arrays
int arraySize = MIN_ARRAY_SIZE;
for (int i = 0; i < BUCKETS; i++, arraySize <<= 2) {
ARRAY_SIZES[i] = arraySize;
if (doTrace) {
logInfo("arraySize[" + i + "]: " + arraySize);
}
}
MAX_ARRAY_SIZE = arraySize >> 2;
/* initialize buckets for dirty byte arrays
(large AA chunk = 32 x 2048 pixels) */
arraySize = MIN_DIRTY_BYTE_ARRAY_SIZE;
for (int i = 0; i < BUCKETS; i++, arraySize <<= 1) {
DIRTY_BYTE_ARRAY_SIZES[i] = arraySize;
if (doTrace) {
logInfo("dirty arraySize[" + i + "]: " + arraySize);
}
}
MAX_DIRTY_BYTE_ARRAY_SIZE = arraySize >> 1;
// threshold to grow arrays only by (3/2) instead of 2
THRESHOLD_ARRAY_SIZE = Math.max(2 * 1024 * 1024, MAX_ARRAY_SIZE); // 2M
THRESHOLD_LARGE_ARRAY_SIZE = 8L * THRESHOLD_ARRAY_SIZE; // 16M
THRESHOLD_HUGE_ARRAY_SIZE = 8L * THRESHOLD_LARGE_ARRAY_SIZE; // 128M
if (doStats || doMonitors) {
logInfo("ArrayCache.BUCKETS = " + BUCKETS);
logInfo("ArrayCache.MIN_ARRAY_SIZE = " + MIN_ARRAY_SIZE);
logInfo("ArrayCache.MAX_ARRAY_SIZE = " + MAX_ARRAY_SIZE);
logInfo("ArrayCache.ARRAY_SIZES = "
+ Arrays.toString(ARRAY_SIZES));
logInfo("ArrayCache.MIN_DIRTY_BYTE_ARRAY_SIZE = "
+ MIN_DIRTY_BYTE_ARRAY_SIZE);
logInfo("ArrayCache.MAX_DIRTY_BYTE_ARRAY_SIZE = "
+ MAX_DIRTY_BYTE_ARRAY_SIZE);
logInfo("ArrayCache.ARRAY_SIZES = "
+ Arrays.toString(DIRTY_BYTE_ARRAY_SIZES));
logInfo("ArrayCache.THRESHOLD_ARRAY_SIZE = "
+ THRESHOLD_ARRAY_SIZE);
logInfo("ArrayCache.THRESHOLD_LARGE_ARRAY_SIZE = "
+ THRESHOLD_LARGE_ARRAY_SIZE);
logInfo("ArrayCache.THRESHOLD_HUGE_ARRAY_SIZE = "
+ THRESHOLD_HUGE_ARRAY_SIZE);
}
}
private ArrayCache() {
// Utility class
}
static synchronized void incResizeInt() {
resizeInt++;
}
static synchronized void incResizeDirtyInt() {
resizeDirtyInt++;
}
static synchronized void incResizeDirtyFloat() {
resizeDirtyFloat++;
}
static synchronized void incResizeDirtyByte() {
resizeDirtyByte++;
}
static synchronized void incOversize() {
oversize++;
}
static void dumpStats() {
if (resizeInt != 0 || resizeDirtyInt != 0 || resizeDirtyFloat != 0
|| resizeDirtyByte != 0 || oversize != 0) {
logInfo("ArrayCache: int resize: " + resizeInt
+ " - dirty int resize: " + resizeDirtyInt
+ " - dirty float resize: " + resizeDirtyFloat
+ " - dirty byte resize: " + resizeDirtyByte
+ " - oversize: " + oversize);
}
}
// small methods used a lot (to be inlined / optimized by hotspot)
static int getBucket(final int length) {
for (int i = 0; i < ARRAY_SIZES.length; i++) {
if (length <= ARRAY_SIZES[i]) {
return i;
}
}
return -1;
}
static int getBucketDirtyBytes(final int length) {
for (int i = 0; i < DIRTY_BYTE_ARRAY_SIZES.length; i++) {
if (length <= DIRTY_BYTE_ARRAY_SIZES[i]) {
return i;
}
}
return -1;
}
/**
* Return the new array size (~ x2)
* @param curSize current used size
* @param needSize needed size
* @return new array size
*/
public static int getNewSize(final int curSize, final int needSize) {
final int initial = (curSize & MASK_CLR_1);
int size;
if (initial > THRESHOLD_ARRAY_SIZE) {
size = initial + (initial >> 1); // x(3/2)
} else {
size = (initial) << 1; // x2
}
// ensure the new size is >= needed size:
if (size < needSize) {
// align to 4096:
size = ((needSize >> 12) + 1) << 12;
}
return size;
}
/**
* Return the new array size (~ x2)
* @param curSize current used size
* @param needSize needed size
* @return new array size
*/
public static long getNewLargeSize(final long curSize, final long needSize) {
long size;
if (curSize > THRESHOLD_HUGE_ARRAY_SIZE) {
size = curSize + (curSize >> 2L); // x(5/4)
} else if (curSize > THRESHOLD_LARGE_ARRAY_SIZE) {
size = curSize + (curSize >> 1L); // x(3/2)
} else {
size = curSize << 1L; // x2
}
// ensure the new size is >= needed size:
if (size < needSize) {
// align to 4096:
size = ((needSize >> 12) + 1) << 12;
}
if (size >= Integer.MAX_VALUE) {
if (curSize >= Integer.MAX_VALUE) {
// hard overflow failure - we can't even accommodate
// new items without overflowing
throw new ArrayIndexOutOfBoundsException(
"array exceeds maximum capacity !");
}
// resize to maximum capacity:
size = Integer.MAX_VALUE;
}
return size;
}
}

View File

@ -1,151 +0,0 @@
/*
* Copyright (c) 2015, 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.ArrayDeque;
import java.util.Arrays;
import static sun.java2d.marlin.MarlinUtils.logException;
import static sun.java2d.marlin.MarlinUtils.logInfo;
final class ByteArrayCache implements MarlinConst {
private final int arraySize;
private final ArrayDeque<byte[]> byteArrays;
// stats
private int getOp = 0;
private int createOp = 0;
private int returnOp = 0;
void dumpStats() {
if (getOp > 0) {
logInfo("ByteArrayCache[" + arraySize + "]: get: " + getOp
+ " created: " + createOp + " - returned: " + returnOp
+ " :: cache size: " + byteArrays.size());
}
}
ByteArrayCache(final int arraySize) {
this.arraySize = arraySize;
// small but enough: almost 1 cache line
this.byteArrays = new ArrayDeque<byte[]>(6);
}
byte[] getArray() {
if (doStats) {
getOp++;
}
// use cache:
final byte[] array = byteArrays.pollLast();
if (array != null) {
return array;
}
if (doStats) {
createOp++;
}
return new byte[arraySize];
}
void putDirtyArray(final byte[] array, final int length) {
if (length != arraySize) {
if (doChecks) {
System.out.println("ArrayCache: bad length = " + length);
}
return;
}
if (doStats) {
returnOp++;
}
// NO clean-up of array data = DIRTY ARRAY
if (doCleanDirty) {
// Force zero-fill dirty arrays:
Arrays.fill(array, 0, array.length, BYTE_0);
}
// fill cache:
byteArrays.addLast(array);
}
void putArray(final byte[] array, final int length,
final int fromIndex, final int toIndex)
{
if (length != arraySize) {
if (doChecks) {
System.out.println("ArrayCache: bad length = " + length);
}
return;
}
if (doStats) {
returnOp++;
}
// clean-up array of dirty part[fromIndex; toIndex[
fill(array, fromIndex, toIndex, BYTE_0);
// fill cache:
byteArrays.addLast(array);
}
static void fill(final byte[] array, final int fromIndex,
final int toIndex, final byte value)
{
// clear array data:
/*
* Arrays.fill is faster than System.arraycopy(empty array)
* or Unsafe.setMemory(byte 0)
*/
if (toIndex != 0) {
Arrays.fill(array, fromIndex, toIndex, value);
}
if (doChecks) {
check(array, 0, array.length, value);
}
}
static void check(final byte[] array, final int fromIndex,
final int toIndex, final byte value)
{
if (doChecks) {
// check zero on full array:
for (int i = fromIndex; i < toIndex; i++) {
if (array[i] != value) {
logException("Invalid array value at " + i + "\n"
+ Arrays.toString(array), new Throwable());
// ensure array is correctly filled:
Arrays.fill(array, value);
return;
}
}
}
}
}

View File

@ -1,155 +0,0 @@
/*
* Copyright (c) 2015, 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 sun.awt.geom.PathConsumer2D;
final class CollinearSimplifier implements PathConsumer2D {
enum SimplifierState {
Empty, PreviousPoint, PreviousLine
};
// slope precision threshold
static final float EPS = 1e-4f; // aaime proposed 1e-3f
PathConsumer2D delegate;
SimplifierState state;
float px1, py1, px2, py2;
float pslope;
CollinearSimplifier() {
}
public CollinearSimplifier init(PathConsumer2D 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(float x1, float y1, float x2, float y2) {
emitStashedLine();
delegate.quadTo(x1, y1, x2, y2);
// final end point:
state = SimplifierState.PreviousPoint;
px1 = x2;
py1 = y2;
}
@Override
public void curveTo(float x1, float y1, float x2, float y2,
float x3, float y3) {
emitStashedLine();
delegate.curveTo(x1, y1, x2, y2, x3, y3);
// final end point:
state = SimplifierState.PreviousPoint;
px1 = x3;
py1 = y3;
}
@Override
public void moveTo(float x, float y) {
emitStashedLine();
delegate.moveTo(x, y);
state = SimplifierState.PreviousPoint;
px1 = x;
py1 = y;
}
@Override
public void lineTo(final float x, final float 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 float 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 float getSlope(float x1, float y1, float x2, float y2) {
float dy = y2 - y1;
if (dy == 0f) {
return (x2 > x1) ? Float.POSITIVE_INFINITY
: Float.NEGATIVE_INFINITY;
}
return (x2 - x1) / dy;
}
}

View File

@ -1,306 +0,0 @@
/*
* Copyright (c) 2007, 2015, 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.Iterator;
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() {
}
void set(float[] points, int type) {
switch(type) {
case 8:
set(points[0], points[1],
points[2], points[3],
points[4], points[5],
points[6], points[7]);
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(float x1, float y1,
float x2, float y2,
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);
dx = x1;
dy = y1;
dax = 3f * ax; day = 3f * ay;
dbx = 2f * bx; dby = 2f * 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);
dx = x1;
dy = y1;
dax = 0f; day = 0f;
dbx = 2f * bx; dby = 2f * by;
}
float xat(float t) {
return t * (t * (t * ax + bx) + cx) + dx;
}
float yat(float t) {
return t * (t * (t * ay + by) + cy) + dy;
}
float dxat(float t) {
return t * (t * dax + dbx) + cx;
}
float dyat(float t) {
return t * (t * day + dby) + cy;
}
int dxRoots(float[] roots, int off) {
return Helpers.quadraticRoots(dax, dbx, cx, roots, off);
}
int dyRoots(float[] roots, int off) {
return Helpers.quadraticRoots(day, dby, cy, roots, off);
}
int infPoints(float[] pts, int off) {
// inflection point at t if -f'(t)x*f''(t)y + f'(t)y*f''(t)x == 0
// Fortunately, this turns out to be quadratic, so there are at
// most 2 inflection points.
final float a = dax * dby - dbx * day;
final float b = 2f * (cy * dax - day * cx);
final float c = cy * dbx - cx * dby;
return Helpers.quadraticRoots(a, b, c, pts, off);
}
// finds points where the first and second derivative are
// perpendicular. This happens when g(t) = f'(t)*f''(t) == 0 (where
// * is a dot product). Unfortunately, we have to solve a cubic.
private int perpendiculardfddf(float[] pts, int off) {
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 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 d = dbx*cx + dby*cy;
return Helpers.cubicRootsInAB(a, b, c, d, pts, off, 0f, 1f);
}
// Tries to find the roots of the function ROC(t)-w in [0, 1). It uses
// a variant of the false position algorithm to find the roots. False
// position requires that 2 initial values x0,x1 be given, and that the
// function must have opposite signs at those values. To find such
// values, we need the local extrema of the ROC function, for which we
// need the roots of its derivative; however, it's harder to find the
// roots of the derivative in this case than it is to find the roots
// of the original function. So, we find all points where this curve's
// first and second derivative are perpendicular, and we pretend these
// are our local extrema. There are at most 3 of these, so we will check
// at most 4 sub-intervals of (0,1). ROC has asymptotes at inflection
// points, so roc-w can have at least 6 roots. This shouldn't be a
// problem for what we're trying to do (draw a nice looking curve).
int rootsOfROCMinusW(float[] roots, int off, final float w, final float err) {
// no OOB exception, because by now off<=6, and roots.length >= 10
assert off <= 6 && roots.length >= 10;
int ret = off;
int numPerpdfddf = perpendiculardfddf(roots, off);
float t0 = 0, ft0 = ROCsq(t0) - w*w;
roots[off + numPerpdfddf] = 1f; // always check interval end points
numPerpdfddf++;
for (int i = off; i < off + numPerpdfddf; i++) {
float t1 = roots[i], ft1 = ROCsq(t1) - w*w;
if (ft0 == 0f) {
roots[ret++] = t0;
} else if (ft1 * ft0 < 0f) { // have opposite signs
// (ROC(t)^2 == w^2) == (ROC(t) == w) is true because
// ROC(t) >= 0 for all t.
roots[ret++] = falsePositionROCsqMinusX(t0, t1, w*w, err);
}
t0 = t1;
ft0 = ft1;
}
return ret - off;
}
private static float eliminateInf(float x) {
return (x == Float.POSITIVE_INFINITY ? Float.MAX_VALUE :
(x == Float.NEGATIVE_INFINITY ? Float.MIN_VALUE : x));
}
// A slight modification of the false position algorithm on wikipedia.
// This only works for the ROCsq-x functions. It might be nice to have
// the function as an argument, but that would be awkward in java6.
// 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 Helpers.java
private float falsePositionROCsqMinusX(float x0, float x1,
final float x, final float err)
{
final int iterLimit = 100;
int side = 0;
float t = x1, ft = eliminateInf(ROCsq(t) - x);
float s = x0, fs = eliminateInf(ROCsq(s) - x);
float r = s, fr;
for (int i = 0; i < iterLimit && Math.abs(t - s) > err * Math.abs(t + s); i++) {
r = (fs * t - ft * s) / (fs - ft);
fr = ROCsq(r) - x;
if (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(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);
}
// returns the radius of curvature squared at t of this curve
// see http://en.wikipedia.org/wiki/Radius_of_curvature_(applications)
private float ROCsq(final float t) {
// 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 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

@ -1,702 +0,0 @@
/*
* Copyright (c) 2007, 2015, 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;
import sun.awt.geom.PathConsumer2D;
/**
* The <code>Dasher</code> class takes a series of linear commands
* (<code>moveTo</code>, <code>lineTo</code>, <code>close</code> and
* <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 Dasher implements sun.awt.geom.PathConsumer2D, MarlinConst {
static final int recLimit = 4;
static final float ERR = 0.01f;
static final float minTincrement = 1f / (1 << recLimit);
private PathConsumer2D out;
private float[] dash;
private int dashLen;
private float startPhase;
private boolean startDashOn;
private int startIdx;
private boolean starting;
private boolean needsMoveTo;
private int idx;
private boolean dashOn;
private float phase;
private float sx, sy;
private float x0, y0;
// temporary storage for the current curve
private final float[] curCurvepts;
// per-thread renderer context
final RendererContext rdrCtx;
// dashes array (dirty)
final float[] dashes_initial = new float[INITIAL_ARRAY];
// flag to recycle dash array copy
boolean recycleDashes;
// per-thread initial arrays (large enough to satisfy most usages
// +1 to avoid recycling in Helpers.widenArray()
private final float[] firstSegmentsBuffer_initial = new float[INITIAL_ARRAY + 1];
/**
* Constructs a <code>Dasher</code>.
* @param rdrCtx per-thread renderer context
*/
Dasher(final RendererContext rdrCtx) {
this.rdrCtx = rdrCtx;
firstSegmentsBuffer = firstSegmentsBuffer_initial;
// we need curCurvepts to be able to contain 2 curves because when
// dashing curves, we need to subdivide it
curCurvepts = new float[8 * 2];
}
/**
* Initialize the <code>Dasher</code>.
*
* @param out an output <code>PathConsumer2D</code>.
* @param dash an array of <code>float</code>s containing the dash pattern
* @param dashLen length of the given dash array
* @param phase a <code>float</code> containing the dash phase
* @param recycleDashes true to indicate to recycle the given dash array
* @return this instance
*/
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;
dashOn = true;
float d;
while (phase >= (d = dash[idx])) {
phase -= d;
idx = (idx + 1) % dashLen;
dashOn = !dashOn;
}
this.dash = dash;
this.dashLen = dashLen;
this.startPhase = this.phase = phase;
this.startDashOn = dashOn;
this.startIdx = idx;
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 (doCleanDirty) {
// Force zero-fill dirty arrays:
Arrays.fill(curCurvepts, 0f);
Arrays.fill(firstSegmentsBuffer, 0f);
}
// Return arrays:
if (recycleDashes && dash != dashes_initial) {
rdrCtx.putDirtyFloatArray(dash);
dash = null;
}
if (firstSegmentsBuffer != firstSegmentsBuffer_initial) {
rdrCtx.putDirtyFloatArray(firstSegmentsBuffer);
firstSegmentsBuffer = firstSegmentsBuffer_initial;
}
}
@Override
public void moveTo(float x0, float 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(float[] buf, int off, int type) {
switch (type) {
case 8:
out.curveTo(buf[off+0], buf[off+1],
buf[off+2], buf[off+3],
buf[off+4], buf[off+5]);
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 float[] 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 float[] firstSegmentsBuffer; // dynamic array
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 segIdx = firstSegidx;
float[] buf = firstSegmentsBuffer;
if (segIdx + len > buf.length) {
if (doStats) {
RendererContext.stats.stat_array_dasher_firstSegmentsBuffer
.add(segIdx + len);
}
firstSegmentsBuffer = buf
= rdrCtx.widenDirtyFloatArray(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(float x1, float y1) {
float dx = x1 - x0;
float dy = y1 - y0;
float len = dx*dx + dy*dy;
if (len == 0f) {
return;
}
len = (float) Math.sqrt(len);
// The scaling factors needed to get the dx and dy of the
// transformed dash segments.
final float cx = dx / len;
final float cy = dy / len;
final float[] _curCurvepts = curCurvepts;
final float[] _dash = dash;
float leftInThisDashSegment;
float 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 float values using epsilon:
if (len == leftInThisDashSegment) {
phase = 0f;
idx = (idx + 1) % dashLen;
dashOn = !dashOn;
}
return;
}
dashdx = _dash[idx] * cx;
dashdy = _dash[idx] * cy;
if (phase == 0f) {
_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 = 0f;
}
}
// shared instance in Dasher
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;
float lastSplitT = 0f;
float t;
float leftInThisDashSegment = dash[idx] - phase;
while ((t = li.next(leftInThisDashSegment)) < 1f) {
if (t != 0f) {
Helpers.subdivideAt((t - lastSplitT) / (1f - 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 = 0f;
leftInThisDashSegment = dash[idx];
}
goTo(curCurvepts, curCurveoff+2, type);
phase += li.lastSegLen();
if (phase >= dash[idx]) {
phase = 0f;
idx = (idx + 1) % dashLen;
dashOn = !dashOn;
}
// reset LengthIterator:
li.reset();
}
private static boolean pointCurve(float[] curve, int type) {
for (int i = 2; i < type; i++) {
if (curve[i] != curve[i-2]) {
return false;
}
}
return true;
}
// Objects of this class are used to iterate through curves. They return
// t values where the left side of the curve has a specified length.
// It does this by subdividing the input curve until a certain error
// condition has been met. A recursive subdivision procedure would
// return as many as 1<<limit curves, but this is an iterator and we
// don't need all the curves all at once, so what we carry out a
// lazy inorder traversal of the recursion tree (meaning we only move
// through the tree when we need the next subdivided curve). This saves
// us a lot of memory because at any one time we only need to store
// limit+1 curves - one for each level of the tree + 1.
// NOTE: the way we do things here is not enough to traverse a general
// tree; however, the trees we are interested in have the property that
// every non leaf node has exactly 2 children
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 float[][] 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 float nextT;
private float lenAtNextT;
private float lastT;
private float lenAtLastT;
private float lenAtLastSplit;
private float lastSegLen;
// the current level in the recursion tree. 0 is the root. limit
// is the deepest possible leaf.
private int recLevel;
private boolean done;
// 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.
private final float[] curLeafCtrlPolyLengths = new float[3];
LengthIterator() {
this.recCurveStack = new float[recLimit + 1][8];
this.sides = new Side[recLimit];
// if any methods are called without first initializing this object
// on a curve, we want it to fail ASAP.
this.nextT = Float.MAX_VALUE;
this.lenAtNextT = Float.MAX_VALUE;
this.lenAtLastSplit = Float.MIN_VALUE;
this.recLevel = Integer.MIN_VALUE;
this.lastSegLen = Float.MAX_VALUE;
this.done = true;
}
/**
* Reset this LengthIterator.
*/
void reset() {
// keep data dirty
// as it appears not useful to reset data:
if (doCleanDirty) {
final int recLimit = recCurveStack.length - 1;
for (int i = recLimit; i >= 0; i--) {
Arrays.fill(recCurveStack[i], 0f);
}
Arrays.fill(sides, Side.LEFT);
Arrays.fill(curLeafCtrlPolyLengths, 0f);
Arrays.fill(nextRoots, 0f);
Arrays.fill(flatLeafCoefCache, 0f);
flatLeafCoefCache[2] = -1f;
}
}
void initializeIterationOnCurve(float[] 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 = 0f;
this.lenAtLastT = 0f;
this.nextT = 0f;
this.lenAtNextT = 0f;
goLeft(); // initializes nextT and lenAtNextT properly
this.lenAtLastSplit = 0f;
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 = 0f;
}
// 0 == false, 1 == true, -1 == invalid cached value.
private int cachedHaveLowAcceleration = -1;
private boolean haveLowAcceleration(float err) {
if (cachedHaveLowAcceleration == -1) {
final float len1 = curLeafCtrlPolyLengths[0];
final float 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 (!Helpers.within(len1, len2, err*len2)) {
cachedHaveLowAcceleration = 0;
return false;
}
if (curveType == 8) {
final float 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 float errLen3 = err * len3;
if (!(Helpers.within(len2, len3, errLen3) &&
Helpers.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 float[] nextRoots = new float[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 float[] flatLeafCoefCache = new float[]{0f, 0f, -1f, 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
// is >= than the length of the uniterated curve, it returns 1.
float next(final float len) {
final float targetLength = lenAtLastSplit + len;
while (lenAtNextT < targetLength) {
if (done) {
lastSegLen = lenAtNextT - lenAtLastSplit;
return 1f;
}
goToNextLeaf();
}
lenAtLastSplit = targetLength;
final float leaflen = lenAtNextT - lenAtLastT;
float 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.05f)) {
// 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 float[] _flatLeafCoefCache = flatLeafCoefCache;
if (_flatLeafCoefCache[2] < 0) {
float x = 0f + 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[3] = -z;
} else if (curveType == 6) {
_flatLeafCoefCache[0] = 0f;
_flatLeafCoefCache[1] = y - 2f * x;
_flatLeafCoefCache[2] = 2f * x;
_flatLeafCoefCache[3] = -y;
}
}
float a = _flatLeafCoefCache[0];
float b = _flatLeafCoefCache[1];
float c = _flatLeafCoefCache[2];
float 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 = Helpers.cubicRootsInAB(a, b, c, d, nextRoots, 0, 0, 1);
if (n == 1 && !Float.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 >= 1f) {
t = 1f;
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;
}
float 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() {
float len = onLeaf();
if (len >= 0f) {
lastT = nextT;
lenAtLastT = lenAtNextT;
nextT += (1 << (recLimit - recLevel)) * minTincrement;
lenAtNextT += len;
// invalidate caches
flatLeafCoefCache[2] = -1f;
cachedHaveLowAcceleration = -1;
} else {
Helpers.subdivide(recCurveStack[recLevel], 0,
recCurveStack[recLevel+1], 0,
recCurveStack[recLevel], 0, curveType);
sides[recLevel] = Side.LEFT;
recLevel++;
goLeft();
}
}
// this is a bit of a hack. It returns -1 if we're not on a leaf, and
// the length of the leaf if we are on a leaf.
private float onLeaf() {
float[] curve = recCurveStack[recLevel];
float polyLen = 0f;
float x0 = curve[0], y0 = curve[1];
for (int i = 2; i < curveType; i += 2) {
final float x1 = curve[i], y1 = curve[i+1];
final float len = Helpers.linelen(x0, y0, x1, y1);
polyLen += len;
curLeafCtrlPolyLengths[i/2 - 1] = len;
x0 = x1;
y0 = y1;
}
final float lineLen = Helpers.linelen(curve[0], curve[1],
curve[curveType-2],
curve[curveType-1]);
if ((polyLen - lineLen) < ERR || recLevel == recLimit) {
return (polyLen + lineLen) / 2f;
}
return -1f;
}
}
@Override
public void curveTo(float x1, float y1,
float x2, float y2,
float x3, float y3)
{
final float[] _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(float x1, float y1, float x2, float y2) {
final float[] _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("Dasher does not use a native consumer");
}
}

View File

@ -1,152 +0,0 @@
/*
* Copyright (c) 2015, 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.ArrayDeque;
import java.util.Arrays;
import static sun.java2d.marlin.MarlinUtils.logException;
import static sun.java2d.marlin.MarlinUtils.logInfo;
final class FloatArrayCache implements MarlinConst {
private final int arraySize;
private final ArrayDeque<float[]> floatArrays;
// stats
private int getOp = 0;
private int createOp = 0;
private int returnOp = 0;
void dumpStats() {
if (getOp > 0) {
logInfo("FloatArrayCache[" + arraySize + "]: get: " + getOp
+ " created: " + createOp + " - returned: " + returnOp
+ " :: cache size: " + floatArrays.size());
}
}
FloatArrayCache(final int arraySize) {
this.arraySize = arraySize;
// small but enough: almost 1 cache line
this.floatArrays = new ArrayDeque<float[]>(6);
}
float[] getArray() {
if (doStats) {
getOp++;
}
// use cache
final float[] array = floatArrays.pollLast();
if (array != null) {
return array;
}
if (doStats) {
createOp++;
}
return new float[arraySize];
}
void putDirtyArray(final float[] array, final int length) {
if (length != arraySize) {
if (doChecks) {
System.out.println("ArrayCache: bad length = " + length);
}
return;
}
if (doStats) {
returnOp++;
}
// NO clean-up of array data = DIRTY ARRAY
if (doCleanDirty) {
// Force zero-fill dirty arrays:
Arrays.fill(array, 0, array.length, 0f);
}
// fill cache:
floatArrays.addLast(array);
}
void putArray(final float[] array, final int length,
final int fromIndex, final int toIndex)
{
if (length != arraySize) {
if (doChecks) {
System.out.println("ArrayCache: bad length = " + length);
}
return;
}
if (doStats) {
returnOp++;
}
// clean-up array of dirty part[fromIndex; toIndex[
fill(array, fromIndex, toIndex, 0f);
// fill cache:
floatArrays.addLast(array);
}
static void fill(final float[] array, final int fromIndex,
final int toIndex, final float value)
{
// clear array data:
/*
* Arrays.fill is faster than System.arraycopy(empty array)
* or Unsafe.setMemory(byte 0)
*/
if (toIndex != 0) {
Arrays.fill(array, fromIndex, toIndex, value);
}
if (doChecks) {
check(array, 0, array.length, value);
}
}
static void check(final float[] array, final int fromIndex,
final int toIndex, final float value)
{
if (doChecks) {
// check zero on full array:
for (int i = fromIndex; i < toIndex; i++) {
if (array[i] != value) {
logException("Invalid array value at " + i + "\n"
+ Arrays.toString(array), new Throwable());
// ensure array is correctly filled:
Arrays.fill(array, value);
return;
}
}
}
}
}

View File

@ -1,223 +0,0 @@
/*
* Copyright (c) 2015, 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 sun.misc.DoubleConsts;
import sun.misc.FloatConsts;
/**
* Faster Math ceil / floor routines derived from StrictMath
*/
public final class FloatMath implements MarlinConst {
// overflow / NaN handling enabled:
static final boolean CHECK_OVERFLOW = true;
static final boolean CHECK_NAN = true;
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;
}
static int min(final int a, final int b) {
return (a <= b) ? a : b;
}
/**
* Returns the smallest (closest to negative infinity) {@code float} value
* that is greater than or equal to the argument and is equal to a
* mathematical integer. Special cases:
* <ul><li>If the argument value is already equal to a mathematical integer,
* then the result is the same as the argument. <li>If the argument is NaN
* or an infinity or positive zero or negative zero, then the result is the
* same as the argument. <li>If the argument value is less than zero but
* greater than -1.0, then the result is negative zero.</ul> Note that the
* value of {@code StrictMath.ceil(x)} is exactly the value of
* {@code -StrictMath.floor(-x)}.
*
* @param a a value.
* @return the smallest (closest to negative infinity) floating-point value
* that is greater than or equal to the argument and is equal to a
* mathematical integer.
*/
public static float ceil_f(final float a) {
// Derived from StrictMath.ceil(double):
// Inline call to Math.getExponent(a) to
// 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;
if (exponent < 0) {
/*
* Absolute value of argument is less than 1.
* floorOrceil(-0.0) => -0.0
* floorOrceil(+0.0) => +0.0
*/
return ((a == 0) ? a :
( (a < 0f) ? -0f : 1f) );
}
if (CHECK_OVERFLOW && (exponent >= 23)) { // 52 for double
/*
* Infinity, NaN, or a value so large it must be integral.
*/
return a;
}
// Else the argument is either an integral value already XOR it
// has to be rounded to one.
assert exponent >= 0 && exponent <= 22; // 51 for double
final int intpart = doppel
& (~(FloatConsts.SIGNIF_BIT_MASK >> exponent));
if (intpart == doppel) {
return a; // integral value (including 0)
}
// 0 handled above as an integer
// sign: 1 for negative, 0 for positive numbers
// add : 0 for negative and 1 for positive numbers
return Float.intBitsToFloat(intpart) + ((~intpart) >>> 31);
}
/**
* Returns the largest (closest to positive infinity) {@code float} value
* that is less than or equal to the argument and is equal to a mathematical
* integer. Special cases:
* <ul><li>If the argument value is already equal to a mathematical integer,
* then the result is the same as the argument. <li>If the argument is NaN
* or an infinity or positive zero or negative zero, then the result is the
* same as the argument.</ul>
*
* @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 float floor_f(final float a) {
// Derived from StrictMath.floor(double):
// Inline call to Math.getExponent(a) to
// 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;
if (exponent < 0) {
/*
* Absolute value of argument is less than 1.
* floorOrceil(-0.0) => -0.0
* floorOrceil(+0.0) => +0.0
*/
return ((a == 0) ? a :
( (a < 0f) ? -1f : 0f) );
}
if (CHECK_OVERFLOW && (exponent >= 23)) { // 52 for double
/*
* Infinity, NaN, or a value so large it must be integral.
*/
return a;
}
// Else the argument is either an integral value already XOR it
// has to be rounded to one.
assert exponent >= 0 && exponent <= 22; // 51 for double
final int intpart = doppel
& (~(FloatConsts.SIGNIF_BIT_MASK >> exponent));
if (intpart == doppel) {
return a; // integral value (including 0)
}
// 0 handled above as an integer
// sign: 1 for negative, 0 for positive numbers
// add : -1 for negative and 0 for positive numbers
return Float.intBitsToFloat(intpart) + (intpart >> 31);
}
/**
* Faster alternative to ceil(float) 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 float a) {
final int intpart = (int) a;
if (a <= intpart
|| (CHECK_OVERFLOW && intpart == Integer.MAX_VALUE)
|| CHECK_NAN && Float.isNaN(a)) {
return intpart;
}
return intpart + 1;
}
/**
* Faster alternative to floor(float) 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 float a) {
final int intpart = (int) a;
if (a >= intpart
|| (CHECK_OVERFLOW && intpart == Integer.MIN_VALUE)
|| CHECK_NAN && Float.isNaN(a)) {
return intpart;
}
return intpart - 1;
}
/**
* Returns a floating-point power of two in the normal range.
*/
static double powerOfTwoD(int n) {
assert (n >= DoubleConsts.MIN_EXPONENT && n <= DoubleConsts.MAX_EXPONENT);
return Double.longBitsToDouble((((long) n + (long) DoubleConsts.EXP_BIAS)
<< (DoubleConsts.SIGNIFICAND_WIDTH - 1))
& DoubleConsts.EXP_BIT_MASK);
}
}

View File

@ -1,441 +0,0 @@
/*
* Copyright (c) 2007, 2015, 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 Helpers implements MarlinConst {
private Helpers() {
throw new Error("This is a non instantiable class");
}
static boolean within(final float x, final float y, final float err) {
final float d = y - x;
return (d <= err && d >= -err);
}
static boolean within(final double x, final double y, final double err) {
final double d = y - x;
return (d <= err && d >= -err);
}
static int quadraticRoots(final float a, final float b,
final float c, float[] zeroes, final int off)
{
int ret = off;
float t;
if (a != 0f) {
final float dis = b*b - 4*a*c;
if (dis > 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);
} else {
zeroes[ret++] = (-b + sqrtDis) / (2f * a);
zeroes[ret++] = (2f * c) / (-b + sqrtDis);
}
} else if (dis == 0f) {
t = (-b) / (2f * a);
zeroes[ret++] = t;
}
} else {
if (b != 0f) {
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(float d, float a, float b, float c,
float[] pts, final int off,
final float A, final float B)
{
if (d == 0f) {
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.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);
// use Cardano's formula
double cb_p = p * p * p;
double D = q * q + cb_p;
int num;
if (D < 0.0) {
// 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);
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)));
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);
num = 1;
if (within(D, 0.0, 1e-8)) {
pts[off+1] = -(pts[off] / 2f);
num = 2;
}
}
final float sub = (1f/3f) * a;
for (int i = 0; i < num; ++i) {
pts[ off+i ] -= sub;
}
return filterOutNotInAB(pts, off, num, A, B) - off;
}
static float evalCubic(final float a, final float b,
final float c, final float d,
final float t)
{
return t * (t * (t * a + b) + c) + d;
}
static float evalQuad(final float a, final float b,
final float c, final float t)
{
return t * (t * a + b) + c;
}
// returns the index 1 past the last valid element remaining after filtering
static int filterOutNotInAB(float[] nums, final int off, final int len,
final float a, final float b)
{
int ret = off;
for (int i = off, end = off + len; i < end; i++) {
if (nums[i] >= a && nums[i] < b) {
nums[ret++] = nums[i];
}
}
return ret;
}
static float polyLineLength(float[] poly, final int off, final int nCoords) {
assert nCoords % 2 == 0 && poly.length >= off + nCoords : "";
float acc = 0;
for (int i = off + 2; i < off + nCoords; i += 2) {
acc += linelen(poly[i], poly[i+1], poly[i-2], poly[i-1]);
}
return acc;
}
static float linelen(float x1, float y1, float x2, float y2) {
final float dx = x2 - x1;
final float dy = y2 - y1;
return (float)Math.sqrt(dx*dx + dy*dy);
}
static void subdivide(float[] src, int srcoff, float[] left, int leftoff,
float[] right, int rightoff, int type)
{
switch(type) {
case 6:
Helpers.subdivideQuad(src, srcoff, left, leftoff, right, rightoff);
return;
case 8:
Helpers.subdivideCubic(src, srcoff, left, leftoff, right, rightoff);
return;
default:
throw new InternalError("Unsupported curve type");
}
}
static void isort(float[] a, int off, int len) {
for (int i = off + 1, end = off + len; i < end; i++) {
float ai = a[i];
int j = i - 1;
for (; j >= off && a[j] > ai; j--) {
a[j+1] = a[j];
}
a[j+1] = ai;
}
}
// Most of these are copied from classes in java.awt.geom because we need
// float versions of these functions, and Line2D, CubicCurve2D,
// QuadCurve2D don't provide them.
/**
* Subdivides the cubic curve specified by the coordinates
* stored in the <code>src</code> array at indices <code>srcoff</code>
* through (<code>srcoff</code>&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(float src[], int srcoff,
float left[], int leftoff,
float right[], int rightoff)
{
float x1 = src[srcoff + 0];
float y1 = src[srcoff + 1];
float ctrlx1 = src[srcoff + 2];
float ctrly1 = src[srcoff + 3];
float ctrlx2 = src[srcoff + 4];
float ctrly2 = src[srcoff + 5];
float x2 = src[srcoff + 6];
float y2 = src[srcoff + 7];
if (left != null) {
left[leftoff + 0] = x1;
left[leftoff + 1] = y1;
}
if (right != null) {
right[rightoff + 6] = x2;
right[rightoff + 7] = y2;
}
x1 = (x1 + ctrlx1) / 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;
if (left != null) {
left[leftoff + 2] = x1;
left[leftoff + 3] = y1;
left[leftoff + 4] = ctrlx1;
left[leftoff + 5] = ctrly1;
left[leftoff + 6] = centerx;
left[leftoff + 7] = centery;
}
if (right != null) {
right[rightoff + 0] = centerx;
right[rightoff + 1] = centery;
right[rightoff + 2] = ctrlx2;
right[rightoff + 3] = ctrly2;
right[rightoff + 4] = x2;
right[rightoff + 5] = y2;
}
}
static void subdivideCubicAt(float t, float src[], int srcoff,
float left[], int leftoff,
float right[], int rightoff)
{
float x1 = src[srcoff + 0];
float y1 = src[srcoff + 1];
float ctrlx1 = src[srcoff + 2];
float ctrly1 = src[srcoff + 3];
float ctrlx2 = src[srcoff + 4];
float ctrly2 = src[srcoff + 5];
float x2 = src[srcoff + 6];
float y2 = src[srcoff + 7];
if (left != null) {
left[leftoff + 0] = x1;
left[leftoff + 1] = y1;
}
if (right != null) {
right[rightoff + 6] = x2;
right[rightoff + 7] = y2;
}
x1 = x1 + t * (ctrlx1 - x1);
y1 = y1 + t * (ctrly1 - y1);
x2 = ctrlx2 + t * (x2 - ctrlx2);
y2 = ctrly2 + t * (y2 - ctrly2);
float centerx = ctrlx1 + t * (ctrlx2 - ctrlx1);
float centery = ctrly1 + t * (ctrly2 - ctrly1);
ctrlx1 = x1 + t * (centerx - x1);
ctrly1 = y1 + t * (centery - y1);
ctrlx2 = centerx + t * (x2 - centerx);
ctrly2 = centery + t * (y2 - centery);
centerx = ctrlx1 + t * (ctrlx2 - ctrlx1);
centery = ctrly1 + t * (ctrly2 - ctrly1);
if (left != null) {
left[leftoff + 2] = x1;
left[leftoff + 3] = y1;
left[leftoff + 4] = ctrlx1;
left[leftoff + 5] = ctrly1;
left[leftoff + 6] = centerx;
left[leftoff + 7] = centery;
}
if (right != null) {
right[rightoff + 0] = centerx;
right[rightoff + 1] = centery;
right[rightoff + 2] = ctrlx2;
right[rightoff + 3] = ctrly2;
right[rightoff + 4] = x2;
right[rightoff + 5] = y2;
}
}
static void subdivideQuad(float src[], int srcoff,
float left[], int leftoff,
float right[], int rightoff)
{
float x1 = src[srcoff + 0];
float y1 = src[srcoff + 1];
float ctrlx = src[srcoff + 2];
float ctrly = src[srcoff + 3];
float x2 = src[srcoff + 4];
float y2 = src[srcoff + 5];
if (left != null) {
left[leftoff + 0] = x1;
left[leftoff + 1] = y1;
}
if (right != null) {
right[rightoff + 4] = x2;
right[rightoff + 5] = y2;
}
x1 = (x1 + ctrlx) / 2f;
y1 = (y1 + ctrly) / 2f;
x2 = (x2 + ctrlx) / 2f;
y2 = (y2 + ctrly) / 2f;
ctrlx = (x1 + x2) / 2f;
ctrly = (y1 + y2) / 2f;
if (left != null) {
left[leftoff + 2] = x1;
left[leftoff + 3] = y1;
left[leftoff + 4] = ctrlx;
left[leftoff + 5] = ctrly;
}
if (right != null) {
right[rightoff + 0] = ctrlx;
right[rightoff + 1] = ctrly;
right[rightoff + 2] = x2;
right[rightoff + 3] = y2;
}
}
static void subdivideQuadAt(float t, float src[], int srcoff,
float left[], int leftoff,
float right[], int rightoff)
{
float x1 = src[srcoff + 0];
float y1 = src[srcoff + 1];
float ctrlx = src[srcoff + 2];
float ctrly = src[srcoff + 3];
float x2 = src[srcoff + 4];
float y2 = src[srcoff + 5];
if (left != null) {
left[leftoff + 0] = x1;
left[leftoff + 1] = y1;
}
if (right != null) {
right[rightoff + 4] = x2;
right[rightoff + 5] = y2;
}
x1 = x1 + t * (ctrlx - x1);
y1 = y1 + t * (ctrly - y1);
x2 = ctrlx + t * (x2 - ctrlx);
y2 = ctrly + t * (y2 - ctrly);
ctrlx = x1 + t * (x2 - x1);
ctrly = y1 + t * (y2 - y1);
if (left != null) {
left[leftoff + 2] = x1;
left[leftoff + 3] = y1;
left[leftoff + 4] = ctrlx;
left[leftoff + 5] = ctrly;
}
if (right != null) {
right[rightoff + 0] = ctrlx;
right[rightoff + 1] = ctrly;
right[rightoff + 2] = x2;
right[rightoff + 3] = y2;
}
}
static void subdivideAt(float t, float src[], int srcoff,
float left[], int leftoff,
float right[], int rightoff, int size)
{
switch(size) {
case 8:
subdivideCubicAt(t, src, srcoff, left, leftoff, right, rightoff);
return;
case 6:
subdivideQuadAt(t, src, srcoff, left, leftoff, right, rightoff);
return;
}
}
}

View File

@ -1,151 +0,0 @@
/*
* Copyright (c) 2015, 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.ArrayDeque;
import java.util.Arrays;
import static sun.java2d.marlin.MarlinUtils.logException;
import static sun.java2d.marlin.MarlinUtils.logInfo;
final class IntArrayCache implements MarlinConst {
private final int arraySize;
private final ArrayDeque<int[]> intArrays;
// stats
private int getOp = 0;
private int createOp = 0;
private int returnOp = 0;
void dumpStats() {
if (getOp > 0) {
logInfo("IntArrayCache[" + arraySize + "]: get: " + getOp
+ " created: " + createOp + " - returned: " + returnOp
+ " :: cache size: " + intArrays.size());
}
}
IntArrayCache(final int arraySize) {
this.arraySize = arraySize;
// small but enough: almost 1 cache line
this.intArrays = new ArrayDeque<int[]>(6);
}
int[] getArray() {
if (doStats) {
getOp++;
}
// use cache:
final int[] array = intArrays.pollLast();
if (array != null) {
return array;
}
if (doStats) {
createOp++;
}
return new int[arraySize];
}
void putDirtyArray(final int[] array, final int length) {
if (length != arraySize) {
if (doChecks) {
System.out.println("ArrayCache: bad length = " + length);
}
return;
}
if (doStats) {
returnOp++;
}
// NO clean-up of array data = DIRTY ARRAY
if (doCleanDirty) {
// Force zero-fill dirty arrays:
Arrays.fill(array, 0, array.length, 0);
}
// fill cache:
intArrays.addLast(array);
}
void putArray(final int[] array, final int length,
final int fromIndex, final int toIndex)
{
if (length != arraySize) {
if (doChecks) {
System.out.println("ArrayCache: bad length = " + length);
}
return;
}
if (doStats) {
returnOp++;
}
// clean-up array of dirty part[fromIndex; toIndex[
fill(array, fromIndex, toIndex, 0);
// fill cache:
intArrays.addLast(array);
}
static void fill(final int[] array, final int fromIndex,
final int toIndex, final int value)
{
// clear array data:
/*
* Arrays.fill is faster than System.arraycopy(empty array)
* or Unsafe.setMemory(byte 0)
*/
if (toIndex != 0) {
Arrays.fill(array, fromIndex, toIndex, value);
}
if (doChecks) {
check(array, 0, array.length, value);
}
}
static void check(final int[] array, final int fromIndex,
final int toIndex, final int value)
{
if (doChecks) {
// check zero on full array:
for (int i = fromIndex; i < toIndex; i++) {
if (array[i] != value) {
logException("Invalid array value at " + i + "\n"
+ Arrays.toString(array), new Throwable());
// ensure array is correctly filled:
Arrays.fill(array, value);
return;
}
}
}
}
}

View File

@ -1,676 +0,0 @@
/*
* Copyright (c) 2007, 2015, 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 jdk.internal.misc.Unsafe;
/**
* An object used to cache pre-rendered complex paths.
*
* @see Renderer
*/
public final class MarlinCache implements MarlinConst {
static final boolean FORCE_RLE = MarlinProperties.isForceRLE();
static final boolean FORCE_NO_RLE = MarlinProperties.isForceNoRLE();
// minimum width to try using RLE encoding:
static final int RLE_MIN_WIDTH
= Math.max(BLOCK_SIZE, MarlinProperties.getRLEMinWidth());
// maximum width for RLE encoding:
// values are stored as int [x|alpha] where alpha is 8 bits
static final int RLE_MAX_WIDTH = 1 << (24 - 1);
// 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
// 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
// [0, maxalpha]) into alpha values, which are in [0,256).
static final byte[] ALPHA_MAP;
static final OffHeapArray ALPHA_MAP_UNSAFE;
static {
final byte[] _ALPHA_MAP = buildAlphaMap(MAX_AA_ALPHA);
ALPHA_MAP_UNSAFE = new OffHeapArray(_ALPHA_MAP, _ALPHA_MAP.length); // 1K
ALPHA_MAP =_ALPHA_MAP;
final Unsafe _unsafe = OffHeapArray.unsafe;
final long addr = ALPHA_MAP_UNSAFE.address;
for (int i = 0; i < _ALPHA_MAP.length; i++) {
_unsafe.putByte(addr + i, _ALPHA_MAP[i]);
}
}
int bboxX0, bboxY0, bboxX1, bboxY1;
// 1D dirty arrays
// row index in rowAAChunk[]
final long[] rowAAChunkIndex = new long[TILE_SIZE];
// first pixel (inclusive) for each row
final int[] rowAAx0 = new int[TILE_SIZE];
// last pixel (exclusive) for each row
final int[] rowAAx1 = new int[TILE_SIZE];
// encoding mode (0=raw, 1=RLE encoding) for each row
final int[] rowAAEnc = new int[TILE_SIZE];
// coded length (RLE encoding) for each row
final long[] rowAALen = new long[TILE_SIZE];
// last position in RLE decoding for each row (getAlpha):
final long[] rowAAPos = new long[TILE_SIZE];
// dirty off-heap array containing pixel coverages for (32) rows (packed)
// if encoding=raw, it contains alpha coverage values (val) as integer
// if encoding=RLE, it contains tuples (val, last x-coordinate exclusive)
// use rowAAx0/rowAAx1 to get row indices within this chunk
final OffHeapArray rowAAChunk;
// current position in rowAAChunk array
long rowAAChunkPos;
// touchedTile[i] is the sum of all the alphas in the tile with
// x=j*TILE_SIZE+bboxX0.
int[] touchedTile;
// per-thread renderer context
final RendererContext rdrCtx;
// large cached touchedTile (dirty)
final int[] touchedTile_initial = new int[INITIAL_ARRAY]; // 1 tile line
int tileMin, tileMax;
boolean useRLE = false;
MarlinCache(final RendererContext rdrCtx) {
this.rdrCtx = rdrCtx;
rowAAChunk = new OffHeapArray(rdrCtx, INITIAL_CHUNK_ARRAY);
touchedTile = touchedTile_initial;
// tile used marks:
tileMin = Integer.MAX_VALUE;
tileMax = Integer.MIN_VALUE;
}
void init(int minx, int miny, int maxx, int maxy, int edgeSumDeltaY)
{
// assert maxy >= miny && maxx >= minx;
bboxX0 = minx;
bboxY0 = miny;
bboxX1 = maxx;
bboxY1 = maxy;
final int width = (maxx - minx);
if (FORCE_NO_RLE) {
useRLE = false;
} else if (FORCE_RLE) {
useRLE = true;
} else {
// heuristics: use both bbox area and complexity
// ie number of primitives:
// fast check min and max width (maxx < 23bits):
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);
// ((edgeSumDeltaY - heightSubPixel) * RLE_THRESHOLD);
// ((edgeSumDeltaY - heightSubPixel) << BLOCK_TH_LG);
if (doTrace && !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)
);
}
}
}
// the ceiling of (maxy - miny + 1) / TILE_SIZE;
final int nxTiles = (width + TILE_SIZE) >> TILE_SIZE_LG;
if (nxTiles > INITIAL_ARRAY) {
if (doStats) {
RendererContext.stats.stat_array_marlincache_touchedTile
.add(nxTiles);
}
touchedTile = rdrCtx.getIntArray(nxTiles);
}
}
/**
* Disposes this cache:
* clean up before reusing this instance
*/
void dispose() {
// Reset touchedTile if needed:
resetTileLine(0);
// Return arrays:
if (touchedTile != touchedTile_initial) {
rdrCtx.putIntArray(touchedTile, 0, 0); // already zero filled
touchedTile = touchedTile_initial;
}
// At last: resize back off-heap rowAA to initial size
if (rowAAChunk.length != INITIAL_CHUNK_ARRAY) {
// note: may throw OOME:
rowAAChunk.resize(INITIAL_CHUNK_ARRAY);
}
if (doCleanDirty) {
// Force zero-fill dirty arrays:
rowAAChunk.fill(BYTE_0);
}
}
void resetTileLine(final int pminY) {
// update bboxY0 to process a complete tile line [0 - 32]
bboxY0 = pminY;
// reset current pos
if (doStats) {
RendererContext.stats.stat_cache_rowAAChunk.add(rowAAChunkPos);
}
rowAAChunkPos = 0L;
// Reset touchedTile:
if (tileMin != Integer.MAX_VALUE) {
if (doStats) {
RendererContext.stats.stat_cache_tiles.add(tileMax - tileMin);
}
// clean only dirty touchedTile:
if (tileMax == 1) {
touchedTile[0] = 0;
} else {
IntArrayCache.fill(touchedTile, tileMin, tileMax, 0);
}
// reset tile used marks:
tileMin = Integer.MAX_VALUE;
tileMax = Integer.MIN_VALUE;
}
if (doCleanDirty) {
// Force zero-fill dirty arrays:
rowAAChunk.fill(BYTE_0);
}
}
void clearAARow(final int y) {
// process tile line [0 - 32]
final int row = y - bboxY0;
// update pixel range:
rowAAx0[row] = 0; // first pixel inclusive
rowAAx1[row] = 0; // last pixel exclusive
rowAAEnc[row] = 0; // raw encoding
// note: leave rowAAChunkIndex[row] undefined
// and rowAALen[row] & rowAAPos[row] (RLE)
}
/**
* Copy the given alpha data into the rowAA cache
* @param alphaRow alpha data to copy from
* @param y y pixel coordinate
* @param px0 first pixel inclusive x0
* @param px1 last pixel exclusive x1
*/
void copyAARowNoRLE(final int[] alphaRow, final int y,
final int px0, final int px1)
{
if (doMonitors) {
RendererContext.stats.mon_rdr_copyAARow.start();
}
// skip useless pixels above boundary
final int px_bbox1 = FloatMath.min(px1, bboxX1);
if (doLogBounds) {
MarlinUtils.logInfo("row = [" + px0 + " ... " + px_bbox1
+ " (" + px1 + ") [ for y=" + y);
}
final int row = y - bboxY0;
// update pixel range:
rowAAx0[row] = px0; // first pixel inclusive
rowAAx1[row] = px_bbox1; // last pixel exclusive
rowAAEnc[row] = 0; // raw encoding
// get current position (bytes):
final long pos = rowAAChunkPos;
// update row index to current position:
rowAAChunkIndex[row] = pos;
// determine need array size (may overflow):
final long needSize = pos + (px_bbox1 - px0);
// update next position (bytes):
rowAAChunkPos = needSize;
// update row data:
final OffHeapArray _rowAAChunk = rowAAChunk;
// ensure rowAAChunk capacity:
if (_rowAAChunk.length < needSize) {
expandRowAAChunk(needSize);
}
if (doStats) {
RendererContext.stats.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 from = px0 - bboxX0; // first pixel inclusive
final int to = px_bbox1 - bboxX0; // last pixel exclusive
final Unsafe _unsafe = OffHeapArray.unsafe;
final long SIZE_BYTE = 1L;
final long addr_alpha = ALPHA_MAP_UNSAFE.address;
long addr_off = _rowAAChunk.address + pos;
// compute alpha sum into rowAA:
for (int x = from, val = 0; x < to; x++) {
// alphaRow is in [0; MAX_COVERAGE]
val += alphaRow[x]; // [from; to[
// ensure values are in [0; MAX_AA_ALPHA] range
if (DO_AA_RANGE_CHECK) {
if (val < 0) {
System.out.println("Invalid coverage = " + val);
val = 0;
}
if (val > MAX_AA_ALPHA) {
System.out.println("Invalid coverage = " + val);
val = MAX_AA_ALPHA;
}
}
// store alpha sum (as byte):
if (val == 0) {
_unsafe.putByte(addr_off, (byte)0); // [0..255]
} else {
_unsafe.putByte(addr_off, _unsafe.getByte(addr_alpha + val)); // [0..255]
// update touchedTile
_touchedTile[x >> _TILE_SIZE_LG] += val;
}
addr_off += SIZE_BYTE;
}
// update tile used marks:
int tx = from >> _TILE_SIZE_LG; // inclusive
if (tx < tileMin) {
tileMin = tx;
}
tx = ((to - 1) >> _TILE_SIZE_LG) + 1; // exclusive (+1 to be sure)
if (tx > tileMax) {
tileMax = tx;
}
if (doLogBounds) {
MarlinUtils.logInfo("clear = [" + from + " ... " + to + "[");
}
// Clear alpha row for reuse:
IntArrayCache.fill(alphaRow, from, px1 - bboxX0, 0);
if (doMonitors) {
RendererContext.stats.mon_rdr_copyAARow.stop();
}
}
void copyAARowRLE_WithBlockFlags(final int[] blkFlags, final int[] alphaRow,
final int y, final int px0, final int px1)
{
if (doMonitors) {
RendererContext.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 from = px0 - _bboxX0; // first pixel inclusive
// skip useless pixels above boundary
final int px_bbox1 = FloatMath.min(px1, bboxX1);
final int to = px_bbox1 - _bboxX0; // last pixel exclusive
if (doLogBounds) {
MarlinUtils.logInfo("row = [" + px0 + " ... " + px_bbox1
+ " (" + px1 + ") [ for y=" + y);
}
// get current position:
final long initialPos = startRLERow(row, px0, px_bbox1);
// determine need array size:
// pessimistic: max needed size = deltaX x 4 (1 int)
final int maxLen = (to - from);
final long needSize = initialPos + (maxLen << 2);
// update row data:
OffHeapArray _rowAAChunk = rowAAChunk;
// ensure rowAAChunk capacity:
if (_rowAAChunk.length < needSize) {
expandRowAAChunk(needSize);
}
final Unsafe _unsafe = OffHeapArray.unsafe;
final long SIZE_INT = 4L;
final long addr_alpha = ALPHA_MAP_UNSAFE.address;
long addr_off = _rowAAChunk.address + initialPos;
final int[] _touchedTile = touchedTile;
final int _TILE_SIZE_LG = TILE_SIZE_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;
// Perform run-length encoding and store results in the piscesCache
int val = 0;
int cx0 = from;
int runLen;
final int _MAX_VALUE = Integer.MAX_VALUE;
int last_t0 = _MAX_VALUE;
int skip = 0;
for (int t = blkW, blk_x0, blk_x1, cx, delta; t <= blkE; t++) {
if (blkFlags[t] != 0) {
blkFlags[t] = 0;
if (last_t0 == _MAX_VALUE) {
last_t0 = t;
}
continue;
}
if (last_t0 != _MAX_VALUE) {
// emit blocks:
blk_x0 = FloatMath.max(last_t0 << _BLK_SIZE_LG, from);
last_t0 = _MAX_VALUE;
// (last block pixel+1) inclusive => +1
blk_x1 = FloatMath.min((t << _BLK_SIZE_LG) + 1, to);
for (cx = blk_x0; cx < blk_x1; cx++) {
if ((delta = alphaRow[cx]) != 0) {
alphaRow[cx] = 0;
// not first rle entry:
if (cx != cx0) {
runLen = cx - cx0;
// store alpha coverage (ensure within bounds):
// as [absX|val] where:
// absX is the absolute x-coordinate:
// note: last pixel exclusive (>= 0)
// note: it should check X is smaller than 23bits (overflow)!
// special case to encode entries into a single int:
if (val == 0) {
_unsafe.putInt(addr_off,
((_bboxX0 + cx) << 8)
);
} else {
_unsafe.putInt(addr_off,
((_bboxX0 + cx) << 8)
| (((int) _unsafe.getByte(addr_alpha + val)) & 0xFF) // [0..255]
);
if (runLen == 1) {
_touchedTile[cx0 >> _TILE_SIZE_LG] += val;
} else {
touchTile(cx0, val, cx, runLen, _touchedTile);
}
}
addr_off += SIZE_INT;
if (doStats) {
RendererContext.stats.hist_tile_generator_encoding_runLen
.add(runLen);
}
cx0 = cx;
}
// alpha value = running sum of coverage delta:
val += delta;
// ensure values are in [0; MAX_AA_ALPHA] range
if (DO_AA_RANGE_CHECK) {
if (val < 0) {
System.out.println("Invalid coverage = " + val);
val = 0;
}
if (val > MAX_AA_ALPHA) {
System.out.println("Invalid coverage = " + val);
val = MAX_AA_ALPHA;
}
}
}
}
} else if (doStats) {
skip++;
}
}
// Process remaining RLE run:
runLen = to - cx0;
// store alpha coverage (ensure within bounds):
// as (int)[absX|val] where:
// absX is the absolute x-coordinate in bits 31 to 8 and val in bits 0..7
// note: last pixel exclusive (>= 0)
// note: it should check X is smaller than 23bits (overflow)!
// special case to encode entries into a single int:
if (val == 0) {
_unsafe.putInt(addr_off,
((_bboxX0 + to) << 8)
);
} else {
_unsafe.putInt(addr_off,
((_bboxX0 + to) << 8)
| (((int) _unsafe.getByte(addr_alpha + val)) & 0xFF) // [0..255]
);
if (runLen == 1) {
_touchedTile[cx0 >> _TILE_SIZE_LG] += val;
} else {
touchTile(cx0, val, to, runLen, _touchedTile);
}
}
addr_off += SIZE_INT;
if (doStats) {
RendererContext.stats.hist_tile_generator_encoding_runLen
.add(runLen);
}
long len = (addr_off - _rowAAChunk.address);
// update coded length as bytes:
rowAALen[row] = (len - initialPos);
// update current position:
rowAAChunkPos = len;
if (doStats) {
RendererContext.stats.stat_cache_rowAA.add(rowAALen[row]);
RendererContext.stats.hist_tile_generator_encoding_ratio.add(
(100 * skip) / (blkE - blkW)
);
}
// update tile used marks:
int tx = from >> _TILE_SIZE_LG; // inclusive
if (tx < tileMin) {
tileMin = tx;
}
tx = ((to - 1) >> _TILE_SIZE_LG) + 1; // exclusive (+1 to be sure)
if (tx > tileMax) {
tileMax = tx;
}
// Clear alpha row for reuse:
if (px1 > bboxX1) {
alphaRow[to ] = 0;
alphaRow[to + 1] = 0;
}
if (doChecks) {
IntArrayCache.check(blkFlags, 0, blkFlags.length, 0);
IntArrayCache.check(alphaRow, 0, alphaRow.length, 0);
}
if (doMonitors) {
RendererContext.stats.mon_rdr_copyAARow.stop();
}
}
long startRLERow(final int row, final int x0, final int x1) {
// rows are supposed to be added by increasing y.
rowAAx0[row] = x0; // first pixel inclusive
rowAAx1[row] = x1; // last pixel exclusive
rowAAEnc[row] = 1; // RLE encoding
rowAAPos[row] = 0L; // position = 0
// update row index to current position:
return (rowAAChunkIndex[row] = rowAAChunkPos);
}
private void expandRowAAChunk(final long needSize) {
if (doStats) {
RendererContext.stats.stat_array_marlincache_rowAAChunk
.add(needSize);
}
// note: throw IOOB if neededSize > 2Gb:
final long newSize = ArrayCache.getNewLargeSize(rowAAChunk.length, needSize);
rowAAChunk.resize(newSize);
}
private void touchTile(final int x0, final int val, final int x1,
final int runLen,
final int[] _touchedTile)
{
// the x and y of the current row, minus bboxX0, bboxY0
// process tile line [0 - 32]
final int _TILE_SIZE_LG = TILE_SIZE_LG;
// update touchedTile
int tx = (x0 >> _TILE_SIZE_LG);
// handle trivial case: same tile (x0, x0+runLen)
if (tx == (x1 >> _TILE_SIZE_LG)) {
// same tile:
_touchedTile[tx] += val * runLen;
return;
}
final int tx1 = (x1 - 1) >> _TILE_SIZE_LG;
if (tx <= tx1) {
final int nextTileXCoord = (tx + 1) << _TILE_SIZE_LG;
_touchedTile[tx++] += val * (nextTileXCoord - x0);
}
if (tx < tx1) {
// don't go all the way to tx1 - we need to handle the last
// tile as a special case (just like we did with the first
final int tileVal = (val << _TILE_SIZE_LG);
for (; tx < tx1; tx++) {
_touchedTile[tx] += tileVal;
}
}
// they will be equal unless x0 >> TILE_SIZE_LG == tx1
if (tx == tx1) {
final int txXCoord = tx << _TILE_SIZE_LG;
final int nextTileXCoord = (tx + 1) << _TILE_SIZE_LG;
final int lastXCoord = (nextTileXCoord <= x1) ? nextTileXCoord : x1;
_touchedTile[tx] += val * (lastXCoord - txXCoord);
}
}
int alphaSumInTile(final int x) {
return touchedTile[(x - bboxX0) >> TILE_SIZE_LG];
}
@Override
public String toString() {
return "bbox = ["
+ bboxX0 + ", " + bboxY0 + " => "
+ bboxX1 + ", " + bboxY1 + "]\n";
}
private static byte[] buildAlphaMap(final int maxalpha) {
// double size !
final byte[] alMap = new byte[maxalpha << 1];
final int halfmaxalpha = maxalpha >> 2;
for (int i = 0; i <= maxalpha; i++) {
alMap[i] = (byte) ((i * 255 + halfmaxalpha) / maxalpha);
// System.out.println("alphaMap[" + i + "] = "
// + Byte.toUnsignedInt(alMap[i]));
}
return alMap;
}
}

View File

@ -1,121 +0,0 @@
/*
* Copyright (c) 2015, 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;
/**
* Marlin constant holder using System properties
*/
interface MarlinConst {
// enable Logs (logger or stdout)
static final boolean enableLogs = false;
// enable Logger
static final boolean useLogger = enableLogs && MarlinProperties.isUseLogger();
// log new RendererContext
static final boolean logCreateContext = enableLogs
&& MarlinProperties.isLogCreateContext();
// log misc.Unsafe alloc/realloc/free
static final boolean logUnsafeMalloc = enableLogs
&& MarlinProperties.isLogUnsafeMalloc();
// do statistics
static final boolean doStats = enableLogs && MarlinProperties.isDoStats();
// do monitors
// disabled to reduce byte-code size a bit...
static final boolean doMonitors = enableLogs && false; // MarlinProperties.isDoMonitors();
// do checks
static final boolean doChecks = false; // MarlinProperties.isDoChecks();
// do AA range checks: disable when algorithm / code is stable
static final boolean DO_AA_RANGE_CHECK = false;
// enable logs
static final boolean doLogWidenArray = enableLogs && false;
// enable oversize logs
static final boolean doLogOverSize = enableLogs && false;
// enable traces
static final boolean doTrace = enableLogs && false;
// do flush monitors
static final boolean doFlushMonitors = true;
// use one polling thread to dump statistics/monitors
static final boolean useDumpThread = false;
// thread dump interval (ms)
static final long statDump = 5000L;
// do clean dirty array
static final boolean doCleanDirty = false;
// flag to use line simplifier
static final boolean useSimplifier = MarlinProperties.isUseSimplifier();
// flag to enable logs related bounds checks
static final boolean doLogBounds = enableLogs && false;
// Initial Array sizing (initial context capacity) ~ 512K
// 2048 pixel (width x height) for initial capacity
static final int INITIAL_PIXEL_DIM
= MarlinProperties.getInitialImageSize();
// typical array sizes: only odd numbers allowed below
static final int INITIAL_ARRAY = 256;
static final int INITIAL_SMALL_ARRAY = 1024;
static final int INITIAL_MEDIUM_ARRAY = 4096;
static final int INITIAL_LARGE_ARRAY = 8192;
static final int INITIAL_ARRAY_16K = 16384;
static final int INITIAL_ARRAY_32K = 32768;
// alpha row dimension
static final int INITIAL_AA_ARRAY = INITIAL_PIXEL_DIM;
// initial edges (24 bytes) = 24K [ints] = 96K
static final int INITIAL_EDGES_CAPACITY = 4096 * 24; // 6 ints per edges
// zero value as byte
static final byte BYTE_0 = (byte) 0;
// subpixels expressed as log2
public static final int SUBPIXEL_LG_POSITIONS_X
= MarlinProperties.getSubPixel_Log2_X();
public static final int SUBPIXEL_LG_POSITIONS_Y
= MarlinProperties.getSubPixel_Log2_Y();
// number of subpixels
public static final int SUBPIXEL_POSITIONS_X = 1 << (SUBPIXEL_LG_POSITIONS_X);
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);
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 BLOCK_SIZE_LG = MarlinProperties.getBlockSize_Log2();
public static final int BLOCK_SIZE = 1 << BLOCK_SIZE_LG;
}

View File

@ -1,181 +0,0 @@
/*
* Copyright (c) 2007, 2015, 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.security.AccessController;
import static sun.java2d.marlin.MarlinUtils.logInfo;
import sun.security.action.GetPropertyAction;
public final class MarlinProperties {
private MarlinProperties() {
// no-op
}
// marlin system properties
public static boolean isUseThreadLocal() {
return getBoolean("sun.java2d.renderer.useThreadLocal", "true");
}
/**
* Return the initial pixel size used to define initial arrays
* (tile AA chunk, alpha line, buckets)
*
* @return 64 < initial pixel size < 32768 (2048 by default)
*/
public static int getInitialImageSize() {
return getInteger("sun.java2d.renderer.pixelsize", 2048, 64, 32 * 1024);
}
/**
* Return the log(2) corresponding to subpixel on x-axis (
*
* @return 1 (2 subpixels) < initial pixel size < 4 (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 the log(2) corresponding to subpixel on y-axis (
*
* @return 1 (2 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 the log(2) corresponding to the square tile size in pixels
*
* @return 3 (8x8 pixels) < tile size < 8 (256x256 pixels)
* (5 by default ie 32x32 pixels)
*/
public static int getTileSize_Log2() {
return getInteger("sun.java2d.renderer.tileSize_log2", 5, 3, 8);
}
/**
* Return the log(2) corresponding to the block size in pixels
*
* @return 3 (8 pixels) < block size < 8 (256 pixels)
* (5 by default ie 32 pixels)
*/
public static int getBlockSize_Log2() {
return getInteger("sun.java2d.renderer.blockSize_log2", 5, 3, 8);
}
// RLE / blockFlags settings
public static boolean isForceRLE() {
return getBoolean("sun.java2d.renderer.forceRLE", "false");
}
public static boolean isForceNoRLE() {
return getBoolean("sun.java2d.renderer.forceNoRLE", "false");
}
public static boolean isUseTileFlags() {
return getBoolean("sun.java2d.renderer.useTileFlags", "true");
}
public static boolean isUseTileFlagsWithHeuristics() {
return isUseTileFlags()
&& getBoolean("sun.java2d.renderer.useTileFlags.useHeuristics", "true");
}
public static int getRLEMinWidth() {
return getInteger("sun.java2d.renderer.rleMinWidth", 64, 0, Integer.MAX_VALUE);
}
// optimisation parameters
public static boolean isUseSimplifier() {
return getBoolean("sun.java2d.renderer.useSimplifier", "false");
}
// debugging parameters
public static boolean isDoStats() {
return getBoolean("sun.java2d.renderer.doStats", "false");
}
public static boolean isDoMonitors() {
return getBoolean("sun.java2d.renderer.doMonitors", "false");
}
public static boolean isDoChecks() {
return getBoolean("sun.java2d.renderer.doChecks", "false");
}
// logging parameters
public static boolean isUseLogger() {
return getBoolean("sun.java2d.renderer.useLogger", "false");
}
public static boolean isLogCreateContext() {
return getBoolean("sun.java2d.renderer.logCreateContext", "false");
}
public static boolean isLogUnsafeMalloc() {
return getBoolean("sun.java2d.renderer.logUnsafeMalloc", "false");
}
// system property utilities
static boolean getBoolean(final String key, final String def) {
return Boolean.valueOf(AccessController.doPrivileged(
new GetPropertyAction(key, def)));
}
static int getInteger(final String key, final int def,
final int min, final int max)
{
final String property = AccessController.doPrivileged(
new GetPropertyAction(key));
int value = def;
if (property != null) {
try {
value = Integer.decode(property);
} catch (NumberFormatException e) {
logInfo("Invalid integer value for " + key + " = " + property);
}
}
// check for invalid values
if ((value < min) || (value > max)) {
logInfo("Invalid value for " + key + " = " + value
+ "; expected value in range[" + min + ", " + max + "] !");
value = def;
}
return value;
}
}

View File

@ -1,465 +0,0 @@
/*
* Copyright (c) 2007, 2015, 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 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 final Renderer rdr;
private final MarlinCache cache;
private int x, y;
MarlinTileGenerator(Renderer r) {
this.rdr = r;
this.cache = r.cache;
}
MarlinTileGenerator init() {
this.x = cache.bboxX0;
this.y = cache.bboxY0;
return this; // fluent API
}
/**
* Disposes this tile generator:
* clean up before reusing this instance
*/
@Override
public void dispose() {
if (doMonitors) {
// called from AAShapePipe.renderTiles() (render tiles end):
RendererContext.stats.mon_pipe_renderTiles.stop();
}
// dispose cache:
cache.dispose();
// dispose renderer:
rdr.dispose();
// recycle the RendererContext instance
MarlinRenderingEngine.returnRendererContext(rdr.rdrCtx);
}
void getBbox(int bbox[]) {
bbox[0] = cache.bboxX0;
bbox[1] = cache.bboxY0;
bbox[2] = cache.bboxX1;
bbox[3] = cache.bboxY1;
}
/**
* Gets the width of the tiles that the generator batches output into.
* @return the width of the standard alpha tile
*/
@Override
public int getTileWidth() {
if (doMonitors) {
// called from AAShapePipe.renderTiles() (render tiles start):
RendererContext.stats.mon_pipe_renderTiles.start();
}
return TILE_SIZE;
}
/**
* Gets the height of the tiles that the generator batches output into.
* @return the height of the standard alpha tile
*/
@Override
public int getTileHeight() {
return TILE_SIZE;
}
/**
* Gets the typical alpha value that will characterize the current
* tile.
* The answer may be 0x00 to indicate that the current tile has
* no coverage in any of its pixels, or it may be 0xff to indicate
* that the current tile is completely covered by the path, or any
* other value to indicate non-trivial coverage cases.
* @return 0x00 for no coverage, 0xff for total coverage, or any other
* value for partial coverage of the tile
*/
@Override
public int getTypicalAlpha() {
int al = cache.alphaSumInTile(x);
// Note: if we have a filled rectangle that doesn't end on a tile
// border, we could still return 0xff, even though al!=maxTileAlphaSum
// This is because if we return 0xff, our users will fill a rectangle
// starting at x,y that has width = Math.min(TILE_SIZE, bboxX1-x),
// and height min(TILE_SIZE,bboxY1-y), which is what should happen.
// However, to support this, we would have to use 2 Math.min's
// and 2 multiplications per tile, instead of just 2 multiplications
// to compute maxTileAlphaSum. The savings offered would probably
// not be worth it, considering how rare this case is.
// Note: I have not tested this, so in the future if it is determined
// that it is worth it, it should be implemented. Perhaps this method's
// interface should be changed to take arguments the width and height
// of the current tile. This would eliminate the 2 Math.min calls that
// would be needed here, since our caller needs to compute these 2
// values anyway.
final int alpha = (al == 0x00 ? 0x00
: (al == MAX_TILE_ALPHA_SUM ? 0xff : 0x80));
if (doStats) {
RendererContext.stats.hist_tile_generator_alpha.add(alpha);
}
return alpha;
}
/**
* Skips the current tile and moves on to the next tile.
* Either this method, or the getAlpha() method should be called
* once per tile, but not both.
*/
@Override
public void nextTile() {
if ((x += TILE_SIZE) >= cache.bboxX1) {
x = cache.bboxX0;
y += TILE_SIZE;
if (y < cache.bboxY1) {
// compute for the tile line
// [ y; max(y + TILE_SIZE, bboxY1) ]
this.rdr.endRendering(y);
}
}
}
/**
* Gets the alpha coverage values for the current tile.
* Either this method, or the nextTile() method should be called
* once per tile, but not both.
*/
@Override
public void getAlpha(final byte tile[], final int offset,
final int rowstride)
{
if (cache.useRLE) {
getAlphaRLE(tile, offset, rowstride);
} else {
getAlphaNoRLE(tile, offset, rowstride);
}
}
/**
* Gets the alpha coverage values for the current tile.
* Either this method, or the nextTile() method should be called
* once per tile, but not both.
*/
private void getAlphaNoRLE(final byte tile[], final int offset,
final int rowstride)
{
if (doMonitors) {
RendererContext.stats.mon_ptg_getAlpha.start();
}
// local vars for performance:
final MarlinCache _cache = this.cache;
final long[] rowAAChunkIndex = _cache.rowAAChunkIndex;
final int[] rowAAx0 = _cache.rowAAx0;
final int[] rowAAx1 = _cache.rowAAx1;
final int x0 = this.x;
final int x1 = FloatMath.min(x0 + TILE_SIZE, _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;
if (doLogBounds) {
MarlinUtils.logInfo("getAlpha = [" + x0 + " ... " + x1
+ "[ [" + y0 + " ... " + y1 + "[");
}
final Unsafe _unsafe = OffHeapArray.unsafe;
final long SIZE = 1L;
final long addr_rowAA = _cache.rowAAChunk.address;
long addr;
final int skipRowPixels = (rowstride - (x1 - x0));
int aax0, aax1, end;
int idx = offset;
for (int cy = y0, cx; cy < y1; cy++) {
// empty line (default)
cx = x0;
aax1 = rowAAx1[cy]; // exclusive
// quick check if there is AA data
// corresponding to this tile [x0; x1[
if (aax1 > x0) {
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 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++) {
// cx inside tile[x0; x1[ :
tile[idx++] = _unsafe.getByte(addr); // [0..255]
addr += SIZE;
}
}
}
// fill line end
while (cx < x1) {
tile[idx++] = 0;
cx++;
}
if (doTrace) {
for (int i = idx - (x1 - x0); i < idx; i++) {
System.out.print(hex(tile[i], 2));
}
System.out.println();
}
idx += skipRowPixels;
}
nextTile();
if (doMonitors) {
RendererContext.stats.mon_ptg_getAlpha.stop();
}
}
/**
* Gets the alpha coverage values for the current tile.
* Either this method, or the nextTile() method should be called
* once per tile, but not both.
*/
private void getAlphaRLE(final byte tile[], final int offset,
final int rowstride)
{
if (doMonitors) {
RendererContext.stats.mon_ptg_getAlpha.start();
}
// Decode run-length encoded alpha mask data
// The data for row j begins at cache.rowOffsetsRLE[j]
// and is encoded as a set of 2-byte pairs (val, runLen)
// terminated by a (0, 0) pair.
// local vars for performance:
final MarlinCache _cache = this.cache;
final long[] rowAAChunkIndex = _cache.rowAAChunkIndex;
final int[] rowAAx0 = _cache.rowAAx0;
final int[] rowAAx1 = _cache.rowAAx1;
final int[] rowAAEnc = _cache.rowAAEnc;
final long[] rowAALen = _cache.rowAALen;
final long[] rowAAPos = _cache.rowAAPos;
final int x0 = this.x;
final int x1 = FloatMath.min(x0 + TILE_SIZE, _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;
if (doLogBounds) {
MarlinUtils.logInfo("getAlpha = [" + x0 + " ... " + x1
+ "[ [" + y0 + " ... " + y1 + "[");
}
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));
int cx, cy, cx1;
int rx0, rx1, runLen, end;
int packed;
byte val;
int idx = offset;
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 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;
}
}
}
} 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 (int i = x0; i < cx; i++) {
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) {
val = (byte)(packed & 0xFF); // [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 (doTrace) {
for (int i = idx - (x1 - x0); i < idx; i++) {
System.out.print(hex(tile[i], 2));
}
System.out.println();
}
idx += skipRowPixels;
}
nextTile();
if (doMonitors) {
RendererContext.stats.mon_ptg_getAlpha.stop();
}
}
static String hex(int v, int d) {
String s = Integer.toHexString(v);
while (s.length() < d) {
s = "0" + s;
}
return s.substring(0, d);
}
}

View File

@ -1,104 +0,0 @@
/*
* Copyright (c) 2015, 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 jdk.internal.misc.JavaLangAccess;
import jdk.internal.misc.SharedSecrets;
public final class MarlinUtils {
// TODO: use sun.util.logging.PlatformLogger once in JDK9
private static final java.util.logging.Logger log;
static {
if (MarlinConst.useLogger) {
log = java.util.logging.Logger.getLogger("sun.java2d.marlin");
} else {
log = null;
}
}
private MarlinUtils() {
// no-op
}
public static void logInfo(final String msg) {
if (MarlinConst.useLogger) {
log.info(msg);
} else if (MarlinConst.enableLogs) {
System.out.print("INFO: ");
System.out.println(msg);
}
}
public static void logException(final String msg, final Throwable th) {
if (MarlinConst.useLogger) {
// log.warning(msg, th);
log.log(java.util.logging.Level.WARNING, msg, th);
} else if (MarlinConst.enableLogs) {
System.out.print("WARNING: ");
System.out.println(msg);
th.printStackTrace(System.err);
}
}
// Returns the caller's class and method's name; best effort
// if cannot infer, return the logger's name.
static String getCallerInfo(String className) {
String sourceClassName = null;
String sourceMethodName = null;
JavaLangAccess access = SharedSecrets.getJavaLangAccess();
Throwable throwable = new Throwable();
int depth = access.getStackTraceDepth(throwable);
boolean lookingForClassName = true;
for (int ix = 0; ix < depth; ix++) {
// Calling getStackTraceElement directly prevents the VM
// from paying the cost of building the entire stack frame.
StackTraceElement frame = access.getStackTraceElement(throwable, ix);
String cname = frame.getClassName();
if (lookingForClassName) {
// Skip all frames until we have found the first frame having the class name.
if (cname.equals(className)) {
lookingForClassName = false;
}
} else {
if (!cname.equals(className)) {
// We've found the relevant frame.
sourceClassName = cname;
sourceMethodName = frame.getMethodName();
break;
}
}
}
if (sourceClassName != null) {
return sourceClassName + " " + sourceMethodName;
} else {
return "unknown";
}
}
}

View File

@ -1,177 +0,0 @@
/*
* Copyright (c) 2009, 2015, 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;
/**
* MergeSort adapted from (OpenJDK 8) java.util.Array.legacyMergeSort(Object[])
* to swap two arrays at the same time (x & y)
* and use external auxiliary storage for temporary arrays
*/
final class MergeSort {
// insertion sort threshold
public static final int INSERTION_SORT_THRESHOLD = 14;
/**
* Modified merge sort:
* Input arrays are in both auxX/auxY (sorted: 0 to insertionSortIndex)
* and x/y (unsorted: insertionSortIndex to toIndex)
* Outputs are stored in x/y arrays
*/
static void mergeSortNoCopy(final int[] x, final int[] y,
final int[] auxX, final int[] auxY,
final int toIndex,
final int insertionSortIndex)
{
if ((toIndex > x.length) || (toIndex > y.length)
|| (toIndex > auxX.length) || (toIndex > auxY.length)) {
// explicit check to avoid bound checks within hot loops (below):
throw new ArrayIndexOutOfBoundsException("bad arguments: toIndex="
+ toIndex);
}
// sort second part only using merge / insertion sort
// in auxiliary storage (auxX/auxY)
mergeSort(x, y, x, auxX, y, auxY, insertionSortIndex, toIndex);
// final pass to merge both
// Merge sorted parts (auxX/auxY) into x/y arrays
if ((insertionSortIndex == 0)
|| (auxX[insertionSortIndex - 1] <= auxX[insertionSortIndex])) {
// System.out.println("mergeSortNoCopy: ordered");
// 34 occurences
// no initial left part or both sublists (auxX, auxY) are sorted:
// copy back data into (x, y):
System.arraycopy(auxX, 0, x, 0, toIndex);
System.arraycopy(auxY, 0, y, 0, toIndex);
return;
}
for (int i = 0, p = 0, q = insertionSortIndex; i < toIndex; i++) {
if ((q >= toIndex) || ((p < insertionSortIndex)
&& (auxX[p] <= auxX[q]))) {
x[i] = auxX[p];
y[i] = auxY[p];
p++;
} else {
x[i] = auxX[q];
y[i] = auxY[q];
q++;
}
}
}
/**
* Src is the source array that starts at index 0
* Dest is the (possibly larger) array destination with a possible offset
* low is the index in dest to start sorting
* high is the end index in dest to end sorting
*/
private static void mergeSort(final int[] refX, final int[] refY,
final int[] srcX, final int[] dstX,
final int[] srcY, final int[] dstY,
final int low, final int high)
{
final int length = high - low;
/*
* Tuning parameter: list size at or below which insertion sort
* will be used in preference to mergesort.
*/
if (length <= INSERTION_SORT_THRESHOLD) {
// Insertion sort on smallest arrays
dstX[low] = refX[low];
dstY[low] = refY[low];
for (int i = low + 1, j = low, x, y; i < high; j = i++) {
x = refX[i];
y = refY[i];
while (dstX[j] > x) {
// swap element
dstX[j + 1] = dstX[j];
dstY[j + 1] = dstY[j];
if (j-- == low) {
break;
}
}
dstX[j + 1] = x;
dstY[j + 1] = y;
}
return;
}
// Recursively sort halves of dest into src
// note: use signed shift (not >>>) for performance
// as indices are small enough to exceed Integer.MAX_VALUE
final int mid = (low + high) >> 1;
mergeSort(refX, refY, dstX, srcX, dstY, srcY, low, mid);
mergeSort(refX, refY, dstX, srcX, dstY, srcY, mid, high);
// If arrays are inverted ie all(A) > all(B) do swap A and B to dst
if (srcX[high - 1] <= srcX[low]) {
// System.out.println("mergeSort: inverse ordered");
// 1561 occurences
final int left = mid - low;
final int right = high - mid;
final int off = (left != right) ? 1 : 0;
// swap parts:
System.arraycopy(srcX, low, dstX, mid + off, left);
System.arraycopy(srcX, mid, dstX, low, right);
System.arraycopy(srcY, low, dstY, mid + off, left);
System.arraycopy(srcY, mid, dstY, low, right);
return;
}
// If arrays are already sorted, just copy from src to dest. This is an
// optimization that results in faster sorts for nearly ordered lists.
if (srcX[mid - 1] <= srcX[mid]) {
// System.out.println("mergeSort: ordered");
// 14 occurences
System.arraycopy(srcX, low, dstX, low, length);
System.arraycopy(srcY, low, dstY, low, length);
return;
}
// Merge sorted halves (now in src) into dest
for (int i = low, p = low, q = mid; i < high; i++) {
if ((q >= high) || ((p < mid) && (srcX[p] <= srcX[q]))) {
dstX[i] = srcX[p];
dstY[i] = srcY[p];
p++;
} else {
dstX[i] = srcX[q];
dstY[i] = srcY[q];
q++;
}
}
}
private MergeSort() {
}
}

View File

@ -1,166 +0,0 @@
/*
* Copyright (c) 2007, 2015, 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.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.Vector;
import static sun.java2d.marlin.MarlinConst.logUnsafeMalloc;
import sun.awt.util.ThreadGroupUtils;
import jdk.internal.misc.Unsafe;
/**
*
* @author bourgesl
*/
final class OffHeapArray {
// unsafe reference
static final Unsafe unsafe;
// size of int / float
static final int SIZE_INT;
// RendererContext reference queue
private static final ReferenceQueue<Object> rdrQueue
= new ReferenceQueue<Object>();
// reference list
private static final Vector<OffHeapReference> refList
= new Vector<OffHeapReference>(32);
static {
unsafe = Unsafe.getUnsafe();
SIZE_INT = Unsafe.ARRAY_INT_INDEX_SCALE;
// Mimics Java2D Disposer:
AccessController.doPrivileged(
(PrivilegedAction<Void>) () -> {
/*
* The thread must be a member of a thread group
* which will not get GCed before VM exit.
* Make its parent the top-level thread group.
*/
final ThreadGroup rootTG
= ThreadGroupUtils.getRootThreadGroup();
final Thread t = new Thread(rootTG, new OffHeapDisposer(),
"MarlinRenderer Disposer");
t.setContextClassLoader(null);
t.setDaemon(true);
t.setPriority(Thread.MAX_PRIORITY);
t.start();
return null;
}
);
}
/* members */
long address;
long length;
int used;
OffHeapArray(final Object parent, final long len) {
// note: may throw OOME:
this.address = unsafe.allocateMemory(len);
this.length = len;
this.used = 0;
if (logUnsafeMalloc) {
MarlinUtils.logInfo(System.currentTimeMillis()
+ ": OffHeapArray.allocateMemory = "
+ len + " to addr = " + this.address);
}
// Create the phantom reference to ensure freeing off-heap memory:
refList.add(new OffHeapReference(parent, this));
}
/*
* As realloc may change the address, updating address is MANDATORY
* @param len new array length
* @throws OutOfMemoryError if the allocation is refused by the system
*/
void resize(final long len) {
// note: may throw OOME:
this.address = unsafe.reallocateMemory(address, len);
this.length = len;
if (logUnsafeMalloc) {
MarlinUtils.logInfo(System.currentTimeMillis()
+ ": OffHeapArray.reallocateMemory = "
+ len + " to addr = " + this.address);
}
}
void free() {
unsafe.freeMemory(this.address);
if (logUnsafeMalloc) {
MarlinUtils.logInfo(System.currentTimeMillis()
+ ": OffHeapEdgeArray.free = "
+ this.length
+ " at addr = " + this.address);
}
}
void fill(final byte val) {
unsafe.setMemory(this.address, this.length, val);
}
static final class OffHeapReference extends PhantomReference<Object> {
private final OffHeapArray array;
OffHeapReference(final Object parent, final OffHeapArray edges) {
super(parent, rdrQueue);
this.array = edges;
}
void dispose() {
// free off-heap blocks
this.array.free();
}
}
static final class OffHeapDisposer implements Runnable {
@Override
public void run() {
final Thread currentThread = Thread.currentThread();
OffHeapReference ref;
// check interrupted:
for (; !currentThread.isInterrupted();) {
try {
ref = (OffHeapReference)rdrQueue.remove();
ref.dispose();
refList.remove(ref);
} catch (InterruptedException ie) {
MarlinUtils.logException("OffHeapDisposer interrupted:",
ie);
}
}
}
}
}

View File

@ -1,471 +0,0 @@
/*
* Copyright (c) 2015, 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.SoftReference;
import java.lang.ref.WeakReference;
import java.util.concurrent.atomic.AtomicInteger;
import static sun.java2d.marlin.ArrayCache.*;
import sun.java2d.marlin.MarlinRenderingEngine.NormalizingPathIterator;
import static sun.java2d.marlin.MarlinUtils.getCallerInfo;
import static sun.java2d.marlin.MarlinUtils.logInfo;
/**
* This class is a renderer context dedicated to a single thread
*/
final class RendererContext implements MarlinConst {
private static final String className = RendererContext.class.getName();
// RendererContext creation counter
private static final AtomicInteger contextCount = new AtomicInteger(1);
// RendererContext statistics
static final RendererStats stats = (doStats || doMonitors)
? RendererStats.getInstance(): null;
private static final boolean USE_CACHE_HARD_REF = doStats
|| (MarlinRenderingEngine.REF_TYPE == MarlinRenderingEngine.REF_WEAK);
/**
* Create a new renderer context
*
* @return new RendererContext instance
*/
static RendererContext createContext() {
final RendererContext newCtx = new RendererContext("ctx"
+ Integer.toString(contextCount.getAndIncrement()));
if (RendererContext.stats != null) {
RendererContext.stats.allContexts.add(newCtx);
}
return newCtx;
}
// context name (debugging purposes)
final String name;
/*
* Reference to this instance (hard, soft or weak).
* @see MarlinRenderingEngine#REF_TYPE
*/
final Object reference;
// dirty flag indicating an exception occured during pipeline in pathTo()
boolean dirty = false;
// dynamic array caches kept using weak reference (low memory footprint)
WeakReference<ArrayCachesHolder> refArrayCaches = null;
// hard reference to array caches (for statistics)
ArrayCachesHolder hardRefArrayCaches = null;
// shared data
final float[] float6 = new float[6];
// shared curve (dirty) (Renderer / Stroker)
final Curve curve = new Curve();
// MarlinRenderingEngine NormalizingPathIterator NearestPixelCenter:
final NormalizingPathIterator nPCPathIterator;
// MarlinRenderingEngine NearestPixelQuarter NormalizingPathIterator:
final NormalizingPathIterator nPQPathIterator;
// MarlinRenderingEngine.TransformingPathConsumer2D
final TransformingPathConsumer2D transformerPC2D;
// recycled Path2D instance
Path2D.Float p2d = null;
final Renderer renderer;
final Stroker stroker;
// Simplifies out collinear lines
final CollinearSimplifier simplifier = new CollinearSimplifier();
final Dasher dasher;
final MarlinTileGenerator ptg;
final MarlinCache cache;
// flag indicating the shape is stroked (1) or filled (0)
int stroking = 0;
/**
* Constructor
*
* @param name
*/
RendererContext(final String name) {
if (logCreateContext) {
MarlinUtils.logInfo("new RendererContext = " + name);
}
this.name = name;
// NormalizingPathIterator instances:
nPCPathIterator = new NormalizingPathIterator.NearestPixelCenter(float6);
nPQPathIterator = new NormalizingPathIterator.NearestPixelQuarter(float6);
// MarlinRenderingEngine.TransformingPathConsumer2D
transformerPC2D = new TransformingPathConsumer2D();
// Renderer:
cache = new MarlinCache(this);
renderer = new Renderer(this); // needs MarlinCache from rdrCtx.cache
ptg = new MarlinTileGenerator(renderer);
stroker = new Stroker(this);
dasher = new Dasher(this);
// Create the reference to this instance (hard, soft or weak):
switch (MarlinRenderingEngine.REF_TYPE) {
default:
case MarlinRenderingEngine.REF_HARD:
reference = this;
break;
case MarlinRenderingEngine.REF_SOFT:
reference = new SoftReference<RendererContext>(this);
break;
case MarlinRenderingEngine.REF_WEAK:
reference = new WeakReference<RendererContext>(this);
break;
}
}
/**
* Disposes this renderer context:
* clean up before reusing this context
*/
void dispose() {
stroking = 0;
// reset hard reference to array caches if needed:
if (!USE_CACHE_HARD_REF) {
hardRefArrayCaches = null;
}
// 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;
}
}
// Array caches
ArrayCachesHolder getArrayCachesHolder() {
// Use hard reference first (cached resolved weak reference):
ArrayCachesHolder holder = hardRefArrayCaches;
if (holder == null) {
// resolve reference:
holder = (refArrayCaches != null)
? refArrayCaches.get()
: null;
// create a new ArrayCachesHolder if none is available
if (holder == null) {
if (logCreateContext) {
MarlinUtils.logInfo("new ArrayCachesHolder for "
+ "RendererContext = " + name);
}
holder = new ArrayCachesHolder();
if (USE_CACHE_HARD_REF) {
// update hard reference:
hardRefArrayCaches = holder;
}
// update weak reference:
refArrayCaches = new WeakReference<ArrayCachesHolder>(holder);
}
}
return holder;
}
// dirty byte array cache
ByteArrayCache getDirtyByteArrayCache(final int length) {
final int bucket = ArrayCache.getBucketDirtyBytes(length);
return getArrayCachesHolder().dirtyByteArrayCaches[bucket];
}
byte[] getDirtyByteArray(final int length) {
if (length <= MAX_DIRTY_BYTE_ARRAY_SIZE) {
return getDirtyByteArrayCache(length).getArray();
}
if (doStats) {
incOversize();
}
if (doLogOverSize) {
logInfo("getDirtyByteArray[oversize]: length=\t" + length
+ "\tfrom=\t" + getCallerInfo(className));
}
return new byte[length];
}
void putDirtyByteArray(final byte[] array) {
final int length = array.length;
// odd sized array are non-cached arrays (initial arrays)
// ensure to never store initial arrays in cache:
if (((length & 0x1) == 0) && (length <= MAX_DIRTY_BYTE_ARRAY_SIZE)) {
getDirtyByteArrayCache(length).putDirtyArray(array, length);
}
}
byte[] widenDirtyByteArray(final byte[] in,
final int usedSize, final int needSize)
{
final int length = in.length;
if (doChecks && length >= needSize) {
return in;
}
if (doStats) {
incResizeDirtyByte();
}
// maybe change bucket:
// ensure getNewSize() > newSize:
final byte[] res = getDirtyByteArray(getNewSize(usedSize, needSize));
System.arraycopy(in, 0, res, 0, usedSize); // copy only used elements
// maybe return current array:
// NO clean-up of array data = DIRTY ARRAY
putDirtyByteArray(in);
if (doLogWidenArray) {
logInfo("widenDirtyByteArray[" + res.length + "]: usedSize=\t"
+ usedSize + "\tlength=\t" + length + "\tneeded length=\t"
+ needSize + "\tfrom=\t" + getCallerInfo(className));
}
return res;
}
// int array cache
IntArrayCache getIntArrayCache(final int length) {
final int bucket = ArrayCache.getBucket(length);
return getArrayCachesHolder().intArrayCaches[bucket];
}
int[] getIntArray(final int length) {
if (length <= MAX_ARRAY_SIZE) {
return getIntArrayCache(length).getArray();
}
if (doStats) {
incOversize();
}
if (doLogOverSize) {
logInfo("getIntArray[oversize]: length=\t" + length + "\tfrom=\t"
+ getCallerInfo(className));
}
return new int[length];
}
// unused
int[] widenIntArray(final int[] in, final int usedSize,
final int needSize, final int clearTo)
{
final int length = in.length;
if (doChecks && length >= needSize) {
return in;
}
if (doStats) {
incResizeInt();
}
// maybe change bucket:
// ensure getNewSize() > newSize:
final int[] res = getIntArray(getNewSize(usedSize, needSize));
System.arraycopy(in, 0, res, 0, usedSize); // copy only used elements
// maybe return current array:
putIntArray(in, 0, clearTo); // ensure all array is cleared (grow-reduce algo)
if (doLogWidenArray) {
logInfo("widenIntArray[" + res.length + "]: usedSize=\t"
+ usedSize + "\tlength=\t" + length + "\tneeded length=\t"
+ needSize + "\tfrom=\t" + getCallerInfo(className));
}
return res;
}
void putIntArray(final int[] array, final int fromIndex,
final int toIndex)
{
final int length = array.length;
// odd sized array are non-cached arrays (initial arrays)
// ensure to never store initial arrays in cache:
if (((length & 0x1) == 0) && (length <= MAX_ARRAY_SIZE)) {
getIntArrayCache(length).putArray(array, length, fromIndex, toIndex);
}
}
// dirty int array cache
IntArrayCache getDirtyIntArrayCache(final int length) {
final int bucket = ArrayCache.getBucket(length);
return getArrayCachesHolder().dirtyIntArrayCaches[bucket];
}
int[] getDirtyIntArray(final int length) {
if (length <= MAX_ARRAY_SIZE) {
return getDirtyIntArrayCache(length).getArray();
}
if (doStats) {
incOversize();
}
if (doLogOverSize) {
logInfo("getDirtyIntArray[oversize]: length=\t" + length
+ "\tfrom=\t" + getCallerInfo(className));
}
return new int[length];
}
int[] widenDirtyIntArray(final int[] in,
final int usedSize, final int needSize)
{
final int length = in.length;
if (doChecks && length >= needSize) {
return in;
}
if (doStats) {
incResizeDirtyInt();
}
// maybe change bucket:
// ensure getNewSize() > newSize:
final int[] res = getDirtyIntArray(getNewSize(usedSize, needSize));
System.arraycopy(in, 0, res, 0, usedSize); // copy only used elements
// maybe return current array:
// NO clean-up of array data = DIRTY ARRAY
putDirtyIntArray(in);
if (doLogWidenArray) {
logInfo("widenDirtyIntArray[" + res.length + "]: usedSize=\t"
+ usedSize + "\tlength=\t" + length + "\tneeded length=\t"
+ needSize + "\tfrom=\t" + getCallerInfo(className));
}
return res;
}
void putDirtyIntArray(final int[] array) {
final int length = array.length;
// odd sized array are non-cached arrays (initial arrays)
// ensure to never store initial arrays in cache:
if (((length & 0x1) == 0) && (length <= MAX_ARRAY_SIZE)) {
getDirtyIntArrayCache(length).putDirtyArray(array, length);
}
}
// dirty float array cache
FloatArrayCache getDirtyFloatArrayCache(final int length) {
final int bucket = ArrayCache.getBucket(length);
return getArrayCachesHolder().dirtyFloatArrayCaches[bucket];
}
float[] getDirtyFloatArray(final int length) {
if (length <= MAX_ARRAY_SIZE) {
return getDirtyFloatArrayCache(length).getArray();
}
if (doStats) {
incOversize();
}
if (doLogOverSize) {
logInfo("getDirtyFloatArray[oversize]: length=\t" + length
+ "\tfrom=\t" + getCallerInfo(className));
}
return new float[length];
}
float[] widenDirtyFloatArray(final float[] in,
final int usedSize, final int needSize)
{
final int length = in.length;
if (doChecks && length >= needSize) {
return in;
}
if (doStats) {
incResizeDirtyFloat();
}
// maybe change bucket:
// ensure getNewSize() > newSize:
final float[] res = getDirtyFloatArray(getNewSize(usedSize, needSize));
System.arraycopy(in, 0, res, 0, usedSize); // copy only used elements
// maybe return current array:
// NO clean-up of array data = DIRTY ARRAY
putDirtyFloatArray(in);
if (doLogWidenArray) {
logInfo("widenDirtyFloatArray[" + res.length + "]: usedSize=\t"
+ usedSize + "\tlength=\t" + length + "\tneeded length=\t"
+ needSize + "\tfrom=\t" + getCallerInfo(className));
}
return res;
}
void putDirtyFloatArray(final float[] array) {
final int length = array.length;
// odd sized array are non-cached arrays (initial arrays)
// ensure to never store initial arrays in cache:
if (((length & 0x1) == 0) && (length <= MAX_ARRAY_SIZE)) {
getDirtyFloatArrayCache(length).putDirtyArray(array, length);
}
}
/* class holding all array cache instances */
static final class ArrayCachesHolder {
// zero-filled int array cache:
final IntArrayCache[] intArrayCaches;
// dirty array caches:
final IntArrayCache[] dirtyIntArrayCaches;
final FloatArrayCache[] dirtyFloatArrayCaches;
final ByteArrayCache[] dirtyByteArrayCaches;
ArrayCachesHolder() {
intArrayCaches = new IntArrayCache[BUCKETS];
dirtyIntArrayCaches = new IntArrayCache[BUCKETS];
dirtyFloatArrayCaches = new FloatArrayCache[BUCKETS];
dirtyByteArrayCaches = new ByteArrayCache[BUCKETS];
for (int i = 0; i < BUCKETS; i++) {
intArrayCaches[i] = new IntArrayCache(ARRAY_SIZES[i]);
// dirty array caches:
dirtyIntArrayCaches[i] = new IntArrayCache(ARRAY_SIZES[i]);
dirtyFloatArrayCaches[i] = new FloatArrayCache(ARRAY_SIZES[i]);
dirtyByteArrayCaches[i] = new ByteArrayCache(DIRTY_BYTE_ARRAY_SIZES[i]);
}
}
}
}

View File

@ -1,319 +0,0 @@
/*
* Copyright (c) 2015, 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.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentLinkedQueue;
import static sun.java2d.marlin.MarlinUtils.logInfo;
import sun.java2d.marlin.stats.Histogram;
import sun.java2d.marlin.stats.Monitor;
import sun.java2d.marlin.stats.StatLong;
/**
* This class gathers global rendering statistics for debugging purposes only
*/
public final class RendererStats implements MarlinConst {
// singleton
private static volatile RendererStats singleton = null;
static RendererStats getInstance() {
if (singleton == null) {
singleton = new RendererStats();
}
return singleton;
}
public static void dumpStats() {
if (singleton != null) {
singleton.dump();
}
}
/* RendererContext collection as hard references
(only used for debugging purposes) */
final ConcurrentLinkedQueue<RendererContext> allContexts
= new ConcurrentLinkedQueue<RendererContext>();
// stats
final StatLong stat_cache_rowAA
= new StatLong("cache.rowAA");
final StatLong stat_cache_rowAAChunk
= new StatLong("cache.rowAAChunk");
final StatLong stat_cache_tiles
= new StatLong("cache.tiles");
final StatLong stat_rdr_poly_stack_curves
= new StatLong("renderer.poly.stack.curves");
final StatLong stat_rdr_poly_stack_types
= new StatLong("renderer.poly.stack.types");
final StatLong stat_rdr_addLine
= new StatLong("renderer.addLine");
final StatLong stat_rdr_addLine_skip
= new StatLong("renderer.addLine.skip");
final StatLong stat_rdr_curveBreak
= new StatLong("renderer.curveBreakIntoLinesAndAdd");
final StatLong stat_rdr_curveBreak_dec
= new StatLong("renderer.curveBreakIntoLinesAndAdd.dec");
final StatLong stat_rdr_curveBreak_inc
= new StatLong("renderer.curveBreakIntoLinesAndAdd.inc");
final StatLong stat_rdr_quadBreak
= new StatLong("renderer.quadBreakIntoLinesAndAdd");
final StatLong stat_rdr_quadBreak_dec
= new StatLong("renderer.quadBreakIntoLinesAndAdd.dec");
final StatLong stat_rdr_edges
= new StatLong("renderer.edges");
final StatLong stat_rdr_edges_count
= new StatLong("renderer.edges.count");
final StatLong stat_rdr_edges_resizes
= new StatLong("renderer.edges.resize");
final StatLong stat_rdr_activeEdges
= new StatLong("renderer.activeEdges");
final StatLong stat_rdr_activeEdges_updates
= new StatLong("renderer.activeEdges.updates");
final StatLong stat_rdr_activeEdges_adds
= new StatLong("renderer.activeEdges.adds");
final StatLong stat_rdr_activeEdges_adds_high
= new StatLong("renderer.activeEdges.adds_high");
final StatLong stat_rdr_crossings_updates
= new StatLong("renderer.crossings.updates");
final StatLong stat_rdr_crossings_sorts
= new StatLong("renderer.crossings.sorts");
final StatLong stat_rdr_crossings_bsearch
= new StatLong("renderer.crossings.bsearch");
final StatLong stat_rdr_crossings_msorts
= new StatLong("renderer.crossings.msorts");
// growable arrays
final StatLong stat_array_dasher_dasher
= new StatLong("array.dasher.dasher.d_float");
final StatLong stat_array_dasher_firstSegmentsBuffer
= new StatLong("array.dasher.firstSegmentsBuffer.d_float");
final StatLong stat_array_stroker_polystack_curves
= new StatLong("array.stroker.polystack.curves.d_float");
final StatLong stat_array_stroker_polystack_curveTypes
= new StatLong("array.stroker.polystack.curveTypes.d_byte");
final StatLong stat_array_marlincache_rowAAChunk
= new StatLong("array.marlincache.rowAAChunk.d_byte");
final StatLong stat_array_marlincache_touchedTile
= new StatLong("array.marlincache.touchedTile.int");
final StatLong stat_array_renderer_alphaline
= new StatLong("array.renderer.alphaline.int");
final StatLong stat_array_renderer_crossings
= new StatLong("array.renderer.crossings.int");
final StatLong stat_array_renderer_aux_crossings
= new StatLong("array.renderer.aux_crossings.int");
final StatLong stat_array_renderer_edgeBuckets
= new StatLong("array.renderer.edgeBuckets.int");
final StatLong stat_array_renderer_edgeBucketCounts
= new StatLong("array.renderer.edgeBucketCounts.int");
final StatLong stat_array_renderer_edgePtrs
= new StatLong("array.renderer.edgePtrs.int");
final StatLong stat_array_renderer_aux_edgePtrs
= new StatLong("array.renderer.aux_edgePtrs.int");
// histograms
final Histogram hist_rdr_crossings
= new Histogram("renderer.crossings");
final Histogram hist_rdr_crossings_ratio
= new Histogram("renderer.crossings.ratio");
final Histogram hist_rdr_crossings_adds
= new Histogram("renderer.crossings.adds");
final Histogram hist_rdr_crossings_msorts
= new Histogram("renderer.crossings.msorts");
final Histogram hist_rdr_crossings_msorts_adds
= new Histogram("renderer.crossings.msorts.adds");
final Histogram hist_tile_generator_alpha
= new Histogram("tile_generator.alpha");
final Histogram hist_tile_generator_encoding
= new Histogram("tile_generator.encoding");
final Histogram hist_tile_generator_encoding_dist
= new Histogram("tile_generator.encoding.dist");
final Histogram hist_tile_generator_encoding_ratio
= new Histogram("tile_generator.encoding.ratio");
final Histogram hist_tile_generator_encoding_runLen
= new Histogram("tile_generator.encoding.runLen");
// all stats
final StatLong[] statistics = new StatLong[]{
stat_cache_rowAA,
stat_cache_rowAAChunk,
stat_cache_tiles,
stat_rdr_poly_stack_types,
stat_rdr_poly_stack_curves,
stat_rdr_addLine,
stat_rdr_addLine_skip,
stat_rdr_curveBreak,
stat_rdr_curveBreak_dec,
stat_rdr_curveBreak_inc,
stat_rdr_quadBreak,
stat_rdr_quadBreak_dec,
stat_rdr_edges,
stat_rdr_edges_count,
stat_rdr_edges_resizes,
stat_rdr_activeEdges,
stat_rdr_activeEdges_updates,
stat_rdr_activeEdges_adds,
stat_rdr_activeEdges_adds_high,
stat_rdr_crossings_updates,
stat_rdr_crossings_sorts,
stat_rdr_crossings_bsearch,
stat_rdr_crossings_msorts,
hist_rdr_crossings,
hist_rdr_crossings_ratio,
hist_rdr_crossings_adds,
hist_rdr_crossings_msorts,
hist_rdr_crossings_msorts_adds,
hist_tile_generator_alpha,
hist_tile_generator_encoding,
hist_tile_generator_encoding_dist,
hist_tile_generator_encoding_ratio,
hist_tile_generator_encoding_runLen,
stat_array_dasher_dasher,
stat_array_dasher_firstSegmentsBuffer,
stat_array_stroker_polystack_curves,
stat_array_stroker_polystack_curveTypes,
stat_array_marlincache_rowAAChunk,
stat_array_marlincache_touchedTile,
stat_array_renderer_alphaline,
stat_array_renderer_crossings,
stat_array_renderer_aux_crossings,
stat_array_renderer_edgeBuckets,
stat_array_renderer_edgeBucketCounts,
stat_array_renderer_edgePtrs,
stat_array_renderer_aux_edgePtrs
};
// monitors
final Monitor mon_pre_getAATileGenerator
= new Monitor("MarlinRenderingEngine.getAATileGenerator()");
final Monitor mon_npi_currentSegment
= new Monitor("NormalizingPathIterator.currentSegment()");
final Monitor mon_rdr_addLine
= new Monitor("Renderer.addLine()");
final Monitor mon_rdr_endRendering
= new Monitor("Renderer.endRendering()");
final Monitor mon_rdr_endRendering_Y
= new Monitor("Renderer._endRendering(Y)");
final Monitor mon_rdr_copyAARow
= new Monitor("Renderer.copyAARow()");
final Monitor mon_pipe_renderTiles
= new Monitor("AAShapePipe.renderTiles()");
final Monitor mon_ptg_getAlpha
= new Monitor("MarlinTileGenerator.getAlpha()");
final Monitor mon_debug
= new Monitor("DEBUG()");
// all monitors
final Monitor[] monitors = new Monitor[]{
mon_pre_getAATileGenerator,
mon_npi_currentSegment,
mon_rdr_addLine,
mon_rdr_endRendering,
mon_rdr_endRendering_Y,
mon_rdr_copyAARow,
mon_pipe_renderTiles,
mon_ptg_getAlpha,
mon_debug
};
private RendererStats() {
super();
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
dump();
}
});
if (useDumpThread) {
final Timer statTimer = new Timer("RendererStats");
statTimer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
dump();
}
}, statDump, statDump);
}
}
void dump() {
if (doStats) {
ArrayCache.dumpStats();
}
final RendererContext[] all = allContexts.toArray(
new RendererContext[allContexts.size()]);
for (RendererContext rdrCtx : all) {
logInfo("RendererContext: " + rdrCtx.name);
if (doMonitors) {
for (Monitor monitor : monitors) {
if (monitor.count != 0) {
logInfo(monitor.toString());
}
}
// As getAATileGenerator percents:
final long total = mon_pre_getAATileGenerator.sum;
if (total != 0L) {
for (Monitor monitor : monitors) {
logInfo(monitor.name + " : "
+ ((100d * monitor.sum) / total) + " %");
}
}
if (doFlushMonitors) {
for (Monitor m : monitors) {
m.reset();
}
}
}
if (doStats) {
for (StatLong stat : statistics) {
if (stat.count != 0) {
logInfo(stat.toString());
stat.reset();
}
}
// IntArrayCaches stats:
final RendererContext.ArrayCachesHolder holder
= rdrCtx.getArrayCachesHolder();
logInfo("Array caches for thread: " + rdrCtx.name);
for (IntArrayCache cache : holder.intArrayCaches) {
cache.dumpStats();
}
logInfo("Dirty Array caches for thread: " + rdrCtx.name);
for (IntArrayCache cache : holder.dirtyIntArrayCaches) {
cache.dumpStats();
}
for (FloatArrayCache cache : holder.dirtyFloatArrayCaches) {
cache.dumpStats();
}
for (ByteArrayCache cache : holder.dirtyByteArrayCaches) {
cache.dumpStats();
}
}
}
}
}

View File

@ -1,507 +0,0 @@
/*
* Copyright (c) 2007, 2015, 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 sun.awt.geom.PathConsumer2D;
import java.awt.geom.AffineTransform;
import java.awt.geom.Path2D;
final class TransformingPathConsumer2D {
TransformingPathConsumer2D() {
// used by RendererContext
}
// recycled PathConsumer2D instance from transformConsumer()
private final Path2DWrapper wp_Path2DWrapper = new Path2DWrapper();
PathConsumer2D wrapPath2d(Path2D.Float p2d)
{
return wp_Path2DWrapper.init(p2d);
}
// recycled PathConsumer2D instances from transformConsumer()
private final TranslateFilter tx_TranslateFilter = new TranslateFilter();
private final DeltaScaleFilter tx_DeltaScaleFilter = new DeltaScaleFilter();
private final ScaleFilter tx_ScaleFilter = new ScaleFilter();
private final DeltaTransformFilter tx_DeltaTransformFilter = new DeltaTransformFilter();
private final TransformFilter tx_TransformFilter = new TransformFilter();
PathConsumer2D transformConsumer(PathConsumer2D out,
AffineTransform at)
{
if (at == null) {
return out;
}
float mxx = (float) at.getScaleX();
float mxy = (float) at.getShearX();
float mxt = (float) at.getTranslateX();
float myx = (float) at.getShearY();
float myy = (float) at.getScaleY();
float myt = (float) at.getTranslateY();
if (mxy == 0f && myx == 0f) {
if (mxx == 1f && myy == 1f) {
if (mxt == 0f && myt == 0f) {
return out;
} else {
return tx_TranslateFilter.init(out, mxt, myt);
}
} else {
if (mxt == 0f && myt == 0f) {
return tx_DeltaScaleFilter.init(out, mxx, myy);
} else {
return tx_ScaleFilter.init(out, mxx, myy, mxt, myt);
}
}
} else if (mxt == 0f && myt == 0f) {
return tx_DeltaTransformFilter.init(out, mxx, mxy, myx, myy);
} else {
return tx_TransformFilter.init(out, mxx, mxy, mxt, myx, myy, myt);
}
}
// recycled PathConsumer2D instances from deltaTransformConsumer()
private final DeltaScaleFilter dt_DeltaScaleFilter = new DeltaScaleFilter();
private final DeltaTransformFilter dt_DeltaTransformFilter = new DeltaTransformFilter();
PathConsumer2D deltaTransformConsumer(PathConsumer2D out,
AffineTransform at)
{
if (at == null) {
return out;
}
float mxx = (float) at.getScaleX();
float mxy = (float) at.getShearX();
float myx = (float) at.getShearY();
float myy = (float) at.getScaleY();
if (mxy == 0f && myx == 0f) {
if (mxx == 1f && myy == 1f) {
return out;
} else {
return dt_DeltaScaleFilter.init(out, mxx, myy);
}
} else {
return dt_DeltaTransformFilter.init(out, mxx, mxy, myx, myy);
}
}
// recycled PathConsumer2D instances from inverseDeltaTransformConsumer()
private final DeltaScaleFilter iv_DeltaScaleFilter = new DeltaScaleFilter();
private final DeltaTransformFilter iv_DeltaTransformFilter = new DeltaTransformFilter();
PathConsumer2D inverseDeltaTransformConsumer(PathConsumer2D out,
AffineTransform at)
{
if (at == null) {
return out;
}
float mxx = (float) at.getScaleX();
float mxy = (float) at.getShearX();
float myx = (float) at.getShearY();
float myy = (float) at.getScaleY();
if (mxy == 0f && myx == 0f) {
if (mxx == 1f && myy == 1f) {
return out;
} else {
return iv_DeltaScaleFilter.init(out, 1.0f/mxx, 1.0f/myy);
}
} else {
float det = mxx * myy - mxy * myx;
return iv_DeltaTransformFilter.init(out,
myy / det,
-mxy / det,
-myx / det,
mxx / det);
}
}
static final class TranslateFilter implements PathConsumer2D {
private PathConsumer2D out;
private float tx, ty;
TranslateFilter() {}
TranslateFilter init(PathConsumer2D out,
float tx, float ty)
{
this.out = out;
this.tx = tx;
this.ty = ty;
return this; // fluent API
}
@Override
public void moveTo(float x0, float y0) {
out.moveTo(x0 + tx, y0 + ty);
}
@Override
public void lineTo(float x1, float y1) {
out.lineTo(x1 + tx, y1 + ty);
}
@Override
public void quadTo(float x1, float y1,
float x2, float y2)
{
out.quadTo(x1 + tx, y1 + ty,
x2 + tx, y2 + ty);
}
@Override
public void curveTo(float x1, float y1,
float x2, float y2,
float x3, float y3)
{
out.curveTo(x1 + tx, y1 + ty,
x2 + tx, y2 + ty,
x3 + tx, y3 + ty);
}
@Override
public void closePath() {
out.closePath();
}
@Override
public void pathDone() {
out.pathDone();
}
@Override
public long getNativeConsumer() {
return 0;
}
}
static final class ScaleFilter implements PathConsumer2D {
private PathConsumer2D out;
private float sx, sy, tx, ty;
ScaleFilter() {}
ScaleFilter init(PathConsumer2D out,
float sx, float sy,
float tx, float ty)
{
this.out = out;
this.sx = sx;
this.sy = sy;
this.tx = tx;
this.ty = ty;
return this; // fluent API
}
@Override
public void moveTo(float x0, float y0) {
out.moveTo(x0 * sx + tx, y0 * sy + ty);
}
@Override
public void lineTo(float x1, float y1) {
out.lineTo(x1 * sx + tx, y1 * sy + ty);
}
@Override
public void quadTo(float x1, float y1,
float x2, float y2)
{
out.quadTo(x1 * sx + tx, y1 * sy + ty,
x2 * sx + tx, y2 * sy + ty);
}
@Override
public void curveTo(float x1, float y1,
float x2, float y2,
float x3, float y3)
{
out.curveTo(x1 * sx + tx, y1 * sy + ty,
x2 * sx + tx, y2 * sy + ty,
x3 * sx + tx, y3 * sy + ty);
}
@Override
public void closePath() {
out.closePath();
}
@Override
public void pathDone() {
out.pathDone();
}
@Override
public long getNativeConsumer() {
return 0;
}
}
static final class TransformFilter implements PathConsumer2D {
private PathConsumer2D out;
private float mxx, mxy, mxt, myx, myy, myt;
TransformFilter() {}
TransformFilter init(PathConsumer2D out,
float mxx, float mxy, float mxt,
float myx, float myy, float myt)
{
this.out = out;
this.mxx = mxx;
this.mxy = mxy;
this.mxt = mxt;
this.myx = myx;
this.myy = myy;
this.myt = myt;
return this; // fluent API
}
@Override
public void moveTo(float x0, float y0) {
out.moveTo(x0 * mxx + y0 * mxy + mxt,
x0 * myx + y0 * myy + myt);
}
@Override
public void lineTo(float x1, float y1) {
out.lineTo(x1 * mxx + y1 * mxy + mxt,
x1 * myx + y1 * myy + myt);
}
@Override
public void quadTo(float x1, float y1,
float x2, float y2)
{
out.quadTo(x1 * mxx + y1 * mxy + mxt,
x1 * myx + y1 * myy + myt,
x2 * mxx + y2 * mxy + mxt,
x2 * myx + y2 * myy + myt);
}
@Override
public void curveTo(float x1, float y1,
float x2, float y2,
float x3, float y3)
{
out.curveTo(x1 * mxx + y1 * mxy + mxt,
x1 * myx + y1 * myy + myt,
x2 * mxx + y2 * mxy + mxt,
x2 * myx + y2 * myy + myt,
x3 * mxx + y3 * mxy + mxt,
x3 * myx + y3 * myy + myt);
}
@Override
public void closePath() {
out.closePath();
}
@Override
public void pathDone() {
out.pathDone();
}
@Override
public long getNativeConsumer() {
return 0;
}
}
static final class DeltaScaleFilter implements PathConsumer2D {
private PathConsumer2D out;
private float sx, sy;
DeltaScaleFilter() {}
DeltaScaleFilter init(PathConsumer2D out,
float mxx, float myy)
{
this.out = out;
sx = mxx;
sy = myy;
return this; // fluent API
}
@Override
public void moveTo(float x0, float y0) {
out.moveTo(x0 * sx, y0 * sy);
}
@Override
public void lineTo(float x1, float y1) {
out.lineTo(x1 * sx, y1 * sy);
}
@Override
public void quadTo(float x1, float y1,
float x2, float y2)
{
out.quadTo(x1 * sx, y1 * sy,
x2 * sx, y2 * sy);
}
@Override
public void curveTo(float x1, float y1,
float x2, float y2,
float x3, float 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 PathConsumer2D {
private PathConsumer2D out;
private float mxx, mxy, myx, myy;
DeltaTransformFilter() {}
DeltaTransformFilter init(PathConsumer2D out,
float mxx, float mxy,
float myx, float myy)
{
this.out = out;
this.mxx = mxx;
this.mxy = mxy;
this.myx = myx;
this.myy = myy;
return this; // fluent API
}
@Override
public void moveTo(float x0, float y0) {
out.moveTo(x0 * mxx + y0 * mxy,
x0 * myx + y0 * myy);
}
@Override
public void lineTo(float x1, float y1) {
out.lineTo(x1 * mxx + y1 * mxy,
x1 * myx + y1 * myy);
}
@Override
public void quadTo(float x1, float y1,
float x2, float y2)
{
out.quadTo(x1 * mxx + y1 * mxy,
x1 * myx + y1 * myy,
x2 * mxx + y2 * mxy,
x2 * myx + y2 * myy);
}
@Override
public void curveTo(float x1, float y1,
float x2, float y2,
float x3, float 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 PathConsumer2D {
private Path2D.Float p2d;
Path2DWrapper() {}
Path2DWrapper init(Path2D.Float p2d) {
this.p2d = p2d;
return this;
}
@Override
public void moveTo(float x0, float y0) {
p2d.moveTo(x0, y0);
}
@Override
public void lineTo(float x1, float y1) {
p2d.lineTo(x1, y1);
}
@Override
public void closePath() {
p2d.closePath();
}
@Override
public void pathDone() {}
@Override
public void curveTo(float x1, float y1,
float x2, float y2,
float x3, float y3)
{
p2d.curveTo(x1, y1, x2, y2, x3, y3);
}
@Override
public void quadTo(float x1, float y1, float x2, float y2) {
p2d.quadTo(x1, y1, x2, y2);
}
@Override
public long getNativeConsumer() {
throw new InternalError("Not using a native peer");
}
}
}

View File

@ -1,39 +0,0 @@
/*
* Copyright (c) 2015, 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 final class Version {
private static final String version = "marlin-0.7.2-Unsafe-OpenJDK";
public static String getVersion() {
return version;
}
private Version() {
}
}

View File

@ -1,102 +0,0 @@
/*
* Copyright (c) 2015, 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.stats;
import java.util.Arrays;
/**
* Generic histogram based on long statistics
*/
public final class Histogram extends StatLong {
static final int BUCKET = 2;
static final int MAX = 20;
static final int LAST = MAX - 1;
static final int[] STEPS = new int[MAX];
static {
STEPS[0] = 0;
STEPS[1] = 1;
for (int i = 2; i < MAX; i++) {
STEPS[i] = STEPS[i - 1] * BUCKET;
}
// System.out.println("Histogram.STEPS = " + Arrays.toString(STEPS));
}
static int bucket(int val) {
for (int i = 1; i < MAX; i++) {
if (val < STEPS[i]) {
return i - 1;
}
}
return LAST;
}
private final StatLong[] stats = new StatLong[MAX];
public Histogram(final String name) {
super(name);
for (int i = 0; i < MAX; i++) {
stats[i] = new StatLong(String.format("%5s .. %5s", STEPS[i],
((i + 1 < MAX) ? STEPS[i + 1] : "~")));
}
}
@Override
public void reset() {
super.reset();
for (int i = 0; i < MAX; i++) {
stats[i].reset();
}
}
@Override
public void add(int val) {
super.add(val);
stats[bucket(val)].add(val);
}
@Override
public void add(long val) {
add((int) val);
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder(2048);
super.toString(sb).append(" { ");
for (int i = 0; i < MAX; i++) {
if (stats[i].count != 0l) {
sb.append("\n ").append(stats[i].toString());
}
}
return sb.append(" }").toString();
}
}

View File

@ -1,53 +0,0 @@
/*
* Copyright (c) 2015, 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.stats;
/**
* Generic monitor ie gathers time statistics as nanos.
*/
public final class Monitor extends StatLong {
private static final long INVALID = -1L;
private long start = INVALID;
public Monitor(final String name) {
super(name);
}
public void start() {
start = System.nanoTime();
}
public void stop() {
final long elapsed = System.nanoTime() - start;
if (start != INVALID && elapsed > 0l) {
add(elapsed);
}
start = INVALID;
}
}

View File

@ -1,97 +0,0 @@
/*
* Copyright (c) 2015, 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.stats;
/**
* Statistics as long values
*/
public class StatLong {
public final String name;
public long count = 0l;
public long sum = 0l;
public long min = Integer.MAX_VALUE;
public long max = Integer.MIN_VALUE;
public StatLong(final String name) {
this.name = name;
}
public void reset() {
count = 0l;
sum = 0l;
min = Integer.MAX_VALUE;
max = Integer.MIN_VALUE;
}
public void add(final int val) {
count++;
sum += val;
if (val < min) {
min = val;
}
if (val > max) {
max = val;
}
}
public void add(final long val) {
count++;
sum += val;
if (val < min) {
min = val;
}
if (val > max) {
max = val;
}
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder(128);
toString(sb);
return sb.toString();
}
public final StringBuilder toString(final StringBuilder sb) {
sb.append(name).append('[').append(count);
sb.append("] sum: ").append(sum).append(" avg: ");
sb.append(trimTo3Digits(((double) sum) / count));
sb.append(" [").append(min).append(" | ").append(max).append("]");
return sb;
}
/**
* Adjust the given double value to keep only 3 decimal digits
*
* @param value value to adjust
* @return double value with only 3 decimal digits
*/
public static double trimTo3Digits(final double value) {
return ((long) (1e3d * value)) / 1e3d;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2015, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2013, 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,12 +22,14 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.pipe;
import java.awt.BasicStroke;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.Rectangle2D;
import java.awt.geom.PathIterator;
import sun.awt.SunHints;
import sun.java2d.SunGraphics2D;
@ -43,15 +45,6 @@ public class AAShapePipe
{
static RenderingEngine renderengine = RenderingEngine.getInstance();
// Per-thread TileState (~1K very small so do not use any Weak Reference)
private static final ThreadLocal<TileState> tileStateThreadLocal =
new ThreadLocal<TileState>() {
@Override
protected TileState initialValue() {
return new TileState();
}
};
CompositePipe outpipe;
public AAShapePipe(CompositePipe pipe) {
@ -75,6 +68,20 @@ public class AAShapePipe
renderPath(sg, s, null);
}
private static Rectangle2D computeBBox(double ux1, double uy1,
double ux2, double uy2)
{
if ((ux2 -= ux1) < 0) {
ux1 += ux2;
ux2 = -ux2;
}
if ((uy2 -= uy1) < 0) {
uy1 += uy2;
uy2 = -uy2;
}
return new Rectangle2D.Double(ux1, uy1, ux2, uy2);
}
public void fillParallelogram(SunGraphics2D sg,
double ux1, double uy1,
double ux2, double uy2,
@ -83,9 +90,7 @@ public class AAShapePipe
double dx2, double dy2)
{
Region clip = sg.getCompClip();
final TileState ts = tileStateThreadLocal.get();
final int[] abox = ts.abox;
int abox[] = new int[4];
AATileGenerator aatg =
renderengine.getAATileGenerator(x, y, dx1, dy1, dx2, dy2, 0, 0,
clip, abox);
@ -94,7 +99,7 @@ public class AAShapePipe
return;
}
renderTiles(sg, ts.computeBBox(ux1, uy1, ux2, uy2), aatg, abox, ts);
renderTiles(sg, computeBBox(ux1, uy1, ux2, uy2), aatg, abox);
}
public void drawParallelogram(SunGraphics2D sg,
@ -106,9 +111,7 @@ public class AAShapePipe
double lw1, double lw2)
{
Region clip = sg.getCompClip();
final TileState ts = tileStateThreadLocal.get();
final int[] abox = ts.abox;
int abox[] = new int[4];
AATileGenerator aatg =
renderengine.getAATileGenerator(x, y, dx1, dy1, dx2, dy2, lw1, lw2,
clip, abox);
@ -119,7 +122,23 @@ public class AAShapePipe
// Note that bbox is of the original shape, not the wide path.
// This is appropriate for handing to Paint methods...
renderTiles(sg, ts.computeBBox(ux1, uy1, ux2, uy2), aatg, abox, ts);
renderTiles(sg, computeBBox(ux1, uy1, ux2, uy2), aatg, abox);
}
private static byte[] theTile;
private static synchronized byte[] getAlphaTile(int len) {
byte[] t = theTile;
if (t == null || t.length < len) {
t = new byte[len];
} else {
theTile = null;
}
return t;
}
private static synchronized void dropAlphaTile(byte[] t) {
theTile = t;
}
public void renderPath(SunGraphics2D sg, Shape s, BasicStroke bs) {
@ -128,9 +147,7 @@ public class AAShapePipe
boolean thin = (sg.strokeState <= SunGraphics2D.STROKE_THINDASHED);
Region clip = sg.getCompClip();
final TileState ts = tileStateThreadLocal.get();
final int[] abox = ts.abox;
int abox[] = new int[4];
AATileGenerator aatg =
renderengine.getAATileGenerator(s, sg.transform, clip,
bs, thin, adjust, abox);
@ -139,30 +156,31 @@ public class AAShapePipe
return;
}
renderTiles(sg, s, aatg, abox, ts);
renderTiles(sg, s, aatg, abox);
}
public void renderTiles(SunGraphics2D sg, Shape s,
AATileGenerator aatg, int abox[], TileState ts)
AATileGenerator aatg, int abox[])
{
Object context = null;
byte alpha[] = null;
try {
context = outpipe.startSequence(sg, s,
ts.computeDevBox(abox),
new Rectangle(abox[0], abox[1],
abox[2] - abox[0],
abox[3] - abox[1]),
abox);
final int tw = aatg.getTileWidth();
final int th = aatg.getTileHeight();
int tw = aatg.getTileWidth();
int th = aatg.getTileHeight();
alpha = getAlphaTile(tw * th);
// get tile from thread local storage:
final byte[] alpha = ts.getAlphaTile(tw * th);
byte[] atile;
for (int y = abox[1]; y < abox[3]; y += th) {
int h = Math.min(th, abox[3] - y);
for (int x = abox[0]; x < abox[2]; x += tw) {
int w = Math.min(tw, abox[2] - x);
int h = Math.min(th, abox[3] - y);
int a = aatg.getTypicalAlpha();
if (a == 0x00 ||
@ -189,56 +207,9 @@ public class AAShapePipe
if (context != null) {
outpipe.endSequence(context);
}
if (alpha != null) {
dropAlphaTile(alpha);
}
}
}
// Tile state used by AAShapePipe
static final class TileState {
// cached tile (32 x 32 tile by default)
private byte[] theTile = new byte[32 * 32];
// dirty aabox array
final int[] abox = new int[4];
// dirty bbox rectangle
private final Rectangle dev = new Rectangle();
// dirty bbox rectangle2D.Double
private final Rectangle2D.Double bbox2D = new Rectangle2D.Double();
byte[] getAlphaTile(int len) {
byte[] t = theTile;
if (t.length < len) {
// create a larger tile and may free current theTile (too small)
theTile = t = new byte[len];
}
return t;
}
Rectangle computeDevBox(final int[] abox) {
final Rectangle box = this.dev;
box.x = abox[0];
box.y = abox[1];
box.width = abox[2] - abox[0];
box.height = abox[3] - abox[1];
return box;
}
Rectangle2D computeBBox(double ux1, double uy1,
double ux2, double uy2)
{
if ((ux2 -= ux1) < 0.0) {
ux1 += ux2;
ux2 = -ux2;
}
if ((uy2 -= uy1) < 0.0) {
uy1 += uy2;
uy2 = -uy2;
}
final Rectangle2D.Double box = this.bbox2D;
box.x = ux1;
box.y = uy1;
box.width = ux2;
box.height = uy2;
return box;
}
}
}

View File

@ -96,14 +96,9 @@ public abstract class RenderingEngine {
* </pre>
*
* If no specific {@code RenderingEngine} is specified on the command
* line or the requested class fails to load, then the Marlin
* renderer will be used as the default.
* <p>
* A printout of which RenderingEngine is loaded and used can be
* enabled by specifying the runtime flag:
* <pre>
* java -Dsun.java2d.renderer.verbose=true
* </pre>
* or Ductus renderer is specified, it will first attempt loading the
* sun.dc.DuctusRenderingEngine class using Class.forName, if that
* is not found, then it will look for Pisces.
* <p>
* Runtime tracing of the actions of the {@code RenderingEngine}
* can be enabled by specifying the runtime flag:
@ -118,23 +113,20 @@ public abstract class RenderingEngine {
return reImpl;
}
/* Look first for an app-override renderer,
* if not specified or present, then look for marlin.
/* Look first for ductus or an app-override renderer,
* if not specified or present, then look for pisces.
*/
final String ductusREClass = "sun.dc.DuctusRenderingEngine";
final String piscesREClass = "sun.java2d.pisces.PiscesRenderingEngine";
GetPropertyAction gpa =
new GetPropertyAction("sun.java2d.renderer");
new GetPropertyAction("sun.java2d.renderer", ductusREClass);
String reClass = AccessController.doPrivileged(gpa);
if (reClass != null) {
try {
Class<?> cls = Class.forName(reClass);
reImpl = (RenderingEngine) cls.newInstance();
} catch (ReflectiveOperationException ignored0) {
try {
Class<?> cls = Class.forName(reClass);
reImpl = (RenderingEngine) cls.newInstance();
} catch (ReflectiveOperationException ignored0) {
}
}
if (reImpl == null) {
final String marlinREClass = "sun.java2d.marlin.MarlinRenderingEngine";
try {
Class<?> cls = Class.forName(marlinREClass);
Class<?> cls = Class.forName(piscesREClass);
reImpl = (RenderingEngine) cls.newInstance();
} catch (ReflectiveOperationException ignored1) {
}
@ -144,12 +136,6 @@ public abstract class RenderingEngine {
throw new InternalError("No RenderingEngine module found");
}
gpa = new GetPropertyAction("sun.java2d.renderer.verbose");
String verbose = AccessController.doPrivileged(gpa);
if (verbose != null && verbose.startsWith("t")) {
System.out.println("RenderingEngine = "+reImpl);
}
gpa = new GetPropertyAction("sun.java2d.renderer.trace");
String reTrace = AccessController.doPrivileged(gpa);
if (reTrace != null) {

View File

@ -1,249 +0,0 @@
/*
* Copyright (c) 2015, 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.
*
* 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.
*/
import sun.java2d.marlin.FloatMath;
/*
* @test
* @summary Check for correct implementation of FloatMath.ceil/floor
* @run main CeilAndFloorTests
*/
public class CeilAndFloorTests {
public static String toHexString(float f) {
if (!Float.isNaN(f))
return Float.toHexString(f);
else
return "NaN(0x" + Integer.toHexString(Float.floatToRawIntBits(f)) + ")";
}
public static int test(String testName, float input,
float result, float expected) {
if (Float.compare(expected, result) != 0) {
System.err.println("Failure for " + testName + ":\n" +
"\tFor input " + input + "\t(" + toHexString(input) + ")\n" +
"\texpected " + expected + "\t(" + toHexString(expected) + ")\n" +
"\tgot " + result + "\t(" + toHexString(result) + ").");
return 1;
}
else
return 0;
}
public static int test_skip_0(String testName, float input,
float result, float expected)
{
// floor_int does not distinguish +0f and -0f
// but it is not critical for Marlin
if (Float.compare(expected, result) != 0 && (expected != 0f))
{
System.err.println("Failure for " + testName + ":\n" +
"\tFor input " + input + "\t(" + toHexString(input) + ")\n" +
"\texpected " + expected + "\t(" + toHexString(expected) + ")\n" +
"\tgot " + result + "\t(" + toHexString(result) + ").");
return 1;
}
else
return 0;
}
private static int testCeilCase(float input, float expected) {
int failures = 0;
// float result:
failures += test("FloatMath.ceil_f", input, FloatMath.ceil_f(input), expected);
// int result:
failures += test("FloatMath.ceil_int", input, FloatMath.ceil_int(input), (int)expected);
failures += test("FloatMath.ceil_f (int)", input, (int)FloatMath.ceil_f(input), (int)expected);
return failures;
}
private static int testFloorCase(float input, float expected) {
int failures = 0;
// float result:
failures += test ("FloatMath.floor_f", input, FloatMath.floor_f(input), expected);
// ignore difference between +0f and -0f:
failures += test_skip_0("FloatMath.floor_int", input, FloatMath.floor_int(input), (int)expected);
failures += test_skip_0("FloatMath.floor_f (int)", input, (int)FloatMath.floor_f(input), (int)expected);
return failures;
}
private static int nearIntegerTests() {
int failures = 0;
float [] fixedPoints = {
-0.0f,
0.0f,
-1.0f,
1.0f,
-0x1.0p52f,
0x1.0p52f,
-Float.MAX_VALUE,
Float.MAX_VALUE,
Float.NEGATIVE_INFINITY,
Float.POSITIVE_INFINITY,
Float.NaN,
};
for(float fixedPoint : fixedPoints) {
failures += testCeilCase(fixedPoint, fixedPoint);
failures += testFloorCase(fixedPoint, fixedPoint);
}
for(int i = Float.MIN_EXPONENT; i <= Float.MAX_EXPONENT; i++) {
float powerOfTwo = Math.scalb(1.0f, i);
float neighborDown = Math.nextDown(powerOfTwo);
float neighborUp = Math.nextUp(powerOfTwo);
if (i < 0) {
failures += testCeilCase( powerOfTwo, 1.0f);
failures += testCeilCase(-powerOfTwo, -0.0f);
failures += testFloorCase( powerOfTwo, 0.0f);
failures += testFloorCase(-powerOfTwo, -1.0f);
failures += testCeilCase( neighborDown, 1.0f);
failures += testCeilCase(-neighborDown, -0.0f);
failures += testFloorCase( neighborUp, 0.0f);
failures += testFloorCase(-neighborUp, -1.0f);
} else {
failures += testCeilCase(powerOfTwo, powerOfTwo);
failures += testFloorCase(powerOfTwo, powerOfTwo);
if (neighborDown==Math.rint(neighborDown)) {
failures += testCeilCase( neighborDown, neighborDown);
failures += testCeilCase(-neighborDown, -neighborDown);
failures += testFloorCase( neighborDown, neighborDown);
failures += testFloorCase(-neighborDown,-neighborDown);
} else {
failures += testCeilCase( neighborDown, powerOfTwo);
failures += testFloorCase(-neighborDown, -powerOfTwo);
}
if (neighborUp==Math.rint(neighborUp)) {
failures += testCeilCase(neighborUp, neighborUp);
failures += testCeilCase(-neighborUp, -neighborUp);
failures += testFloorCase(neighborUp, neighborUp);
failures += testFloorCase(-neighborUp, -neighborUp);
} else {
failures += testFloorCase(neighborUp, powerOfTwo);
failures += testCeilCase(-neighborUp, -powerOfTwo);
}
}
}
for(int i = -(0x10000); i <= 0x10000; i++) {
float f = (float) i;
float neighborDown = Math.nextDown(f);
float neighborUp = Math.nextUp(f);
failures += testCeilCase( f, f);
failures += testCeilCase(-f, -f);
failures += testFloorCase( f, f);
failures += testFloorCase(-f, -f);
if (Math.abs(f) > 1.0) {
failures += testCeilCase( neighborDown, f);
failures += testCeilCase(-neighborDown, -f+1);
failures += testFloorCase( neighborUp, f);
failures += testFloorCase(-neighborUp, -f-1);
}
}
return failures;
}
public static int roundingTests() {
int failures = 0;
float [][] testCases = {
{ Float.MIN_VALUE, 1.0f},
{-Float.MIN_VALUE, -0.0f},
{ Math.nextDown(Float.MIN_NORMAL), 1.0f},
{-Math.nextDown(Float.MIN_NORMAL), -0.0f},
{ Float.MIN_NORMAL, 1.0f},
{-Float.MIN_NORMAL, -0.0f},
{ 0.1f, 1.0f},
{-0.1f, -0.0f},
{ 0.5f, 1.0f},
{-0.5f, -0.0f},
{ 1.5f, 2.0f},
{-1.5f, -1.0f},
{ 2.5f, 3.0f},
{-2.5f, -2.0f},
{ 12.3456789f, 13.0f},
{-12.3456789f, -12.0f},
{ Math.nextDown(1.0f), 1.0f},
{ Math.nextDown(-1.0f), -1.0f},
{ Math.nextUp(1.0f), 2.0f},
{ Math.nextUp(-1.0f), -0.0f},
{ 0x1.0p22f, 0x1.0p22f},
{-0x1.0p22f, -0x1.0p22f},
{ Math.nextDown(0x1.0p22f), 0x1.0p22f},
{-Math.nextUp(0x1.0p22f), -0x1.0p22f},
{ Math.nextUp(0x1.0p22f), 0x1.0p22f+1f},
{-Math.nextDown(0x1.0p22f), -0x1.0p22f+1f},
{ Math.nextDown(0x1.0p23f), 0x1.0p23f},
{-Math.nextUp(0x1.0p23f), -0x1.0p23f-1f},
{ Math.nextUp(0x1.0p23f), 0x1.0p23f+1f},
{-Math.nextDown(0x1.0p23f), -0x1.0p23f+1f},
};
for(float[] testCase : testCases) {
failures += testCeilCase(testCase[0], testCase[1]);
failures += testFloorCase(-testCase[0], -testCase[1]);
}
return failures;
}
public static void main(String... args) {
int failures = 0;
System.out.println("nearIntegerTests");
failures += nearIntegerTests();
System.out.println("roundingTests");
failures += roundingTests();
if (failures > 0) {
System.err.println("Testing {FloatMath}.ceil/floor incurred "
+ failures + " failures.");
throw new RuntimeException();
}
}
}

View File

@ -1,289 +0,0 @@
/*
* Copyright (c) 2015, 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.
*
* 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.
*/
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.geom.Path2D;
import static java.awt.geom.Path2D.WIND_NON_ZERO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import sun.java2d.pipe.RenderingEngine;
/**
* Simple crash rendering test using huge GeneralPaths with marlin renderer
*
* run it with large heap (2g):
* java -Dsun.java2d.renderer=sun.java2d.marlin.MarlinRenderingEngine marlin.CrashTest
*
* @author bourgesl
*/
public class CrashTest {
static final boolean SAVE_IMAGE = false;
static boolean USE_ROUND_CAPS_AND_JOINS = true;
public static void main(String[] args) {
// try insane image sizes:
// subpixel coords may overflow:
// testHugeImage((Integer.MAX_VALUE >> 3) + 1, 6);
// larger than 23 bits: (RLE)
testHugeImage(8388608 + 1, 10);
test(0.1f, false, 0);
test(0.1f, true, 7f);
// Exceed 2Gb OffHeap buffer for edges:
try {
USE_ROUND_CAPS_AND_JOINS = true;
test(0.1f, true, 0.1f);
System.out.println("Exception MISSING.");
}
catch (Throwable th) {
if (th instanceof ArrayIndexOutOfBoundsException) {
System.out.println("ArrayIndexOutOfBoundsException expected.");
} else {
System.out.println("Exception occured:");
th.printStackTrace();
}
}
}
private static void test(final float lineStroke,
final boolean useDashes,
final float dashMinLen)
throws ArrayIndexOutOfBoundsException
{
System.out.println("---\n" + "test: "
+ "lineStroke=" + lineStroke
+ ", useDashes=" + useDashes
+", dashMinLen=" + dashMinLen
);
final String renderer = RenderingEngine.getInstance().getClass().getSimpleName();
System.out.println("Testing renderer = " + renderer);
final BasicStroke stroke = createStroke(lineStroke, useDashes, dashMinLen);
// TODO: test Dasher.firstSegmentsBuffer resizing ?
// array.dasher.firstSegmentsBuffer.d_float[2] sum: 6 avg: 3.0 [3 | 3]
/*
// Marlin growable arrays:
= new StatLong("array.dasher.firstSegmentsBuffer.d_float");
= new StatLong("array.stroker.polystack.curves.d_float");
= new StatLong("array.stroker.polystack.curveTypes.d_byte");
= new StatLong("array.marlincache.rowAAChunk.d_byte");
= new StatLong("array.marlincache.touchedTile.int");
= new StatLong("array.renderer.alphaline.int");
= new StatLong("array.renderer.crossings.int");
= new StatLong("array.renderer.aux_crossings.int");
= new StatLong("array.renderer.edgeBuckets.int");
= new StatLong("array.renderer.edgeBucketCounts.int");
= new StatLong("array.renderer.edgePtrs.int");
= new StatLong("array.renderer.aux_edgePtrs.int");
*/
// size > 8192 (exceed both tile and buckets arrays)
final int size = 9000;
System.out.println("image size = " + size);
final BufferedImage image = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB);
final Graphics2D g2d = (Graphics2D) image.getGraphics();
try {
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setClip(0, 0, size, size);
g2d.setBackground(Color.WHITE);
g2d.clearRect(0, 0, size, size);
g2d.setStroke(stroke);
g2d.setColor(Color.BLACK);
final long start = System.nanoTime();
paint(g2d, size - 10f);
final long time = System.nanoTime() - start;
System.out.println("paint: duration= " + (1e-6 * time) + " ms.");
if (SAVE_IMAGE) {
try {
final File file = new File("CrashTest-" + renderer + "-dash-" + useDashes + ".bmp");
System.out.println("Writing file: " + file.getAbsolutePath());
ImageIO.write(image, "BMP", file);
} catch (IOException ex) {
System.out.println("Writing file failure:");
ex.printStackTrace();
}
}
} finally {
g2d.dispose();
}
}
private static void testHugeImage(final int width, final int height)
throws ArrayIndexOutOfBoundsException
{
System.out.println("---\n" + "testHugeImage: "
+ "width=" + width
+ ", height=" + height
);
final String renderer = RenderingEngine.getInstance().getClass().getSimpleName();
System.out.println("Testing renderer = " + renderer);
final BasicStroke stroke = createStroke(2.5f, false, 0);
// size > 24bits (exceed both tile and buckets arrays)
System.out.println("image size = " + width + " x "+height);
final BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_GRAY);
final Graphics2D g2d = (Graphics2D) image.getGraphics();
try {
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setBackground(Color.WHITE);
g2d.clearRect(0, 0, width, height);
g2d.setStroke(stroke);
g2d.setColor(Color.BLACK);
final Path2D.Float path = new Path2D.Float(WIND_NON_ZERO, 32);
path.moveTo(0, 0);
path.lineTo(width, 0);
path.lineTo(width, height);
path.lineTo(0, height);
path.lineTo(0, 0);
final long start = System.nanoTime();
g2d.draw(path);
final long time = System.nanoTime() - start;
System.out.println("paint: duration= " + (1e-6 * time) + " ms.");
if (SAVE_IMAGE) {
try {
final File file = new File("CrashTest-" + renderer +
"-huge-" + width + "x" +height + ".bmp");
System.out.println("Writing file: " + file.getAbsolutePath());
ImageIO.write(image, "BMP", file);
} catch (IOException ex) {
System.out.println("Writing file failure:");
ex.printStackTrace();
}
}
} finally {
g2d.dispose();
}
}
private static void paint(final Graphics2D g2d, final float size) {
final double halfSize = size / 2.0;
final Path2D.Float path = new Path2D.Float(WIND_NON_ZERO, 32 * 1024);
// show cross:
path.moveTo(0, 0);
path.lineTo(size, size);
path.moveTo(size, 0);
path.lineTo(0, size);
path.moveTo(0, 0);
path.lineTo(size, 0);
path.moveTo(0, 0);
path.lineTo(0, size);
path.moveTo(0, 0);
double r = size;
final int ratio = 100;
int repeats = 1;
int n = 0;
while (r > 1.0) {
repeats *= ratio;
if (repeats > 10000) {
repeats = 10000;
}
for (int i = 0; i < repeats; i++) {
path.lineTo(halfSize - 0.5 * r + i * r / repeats,
halfSize - 0.5 * r);
n++;
path.lineTo(halfSize - 0.5 * r + i * r / repeats + 0.1,
halfSize + 0.5 * r);
n++;
}
r -= halfSize;
}
System.out.println("draw : " + n + " lines.");
g2d.draw(path);
}
private static BasicStroke createStroke(final float width,
final boolean useDashes,
final float dashMinLen) {
final float[] dashes;
if (useDashes) {
// huge dash array (exceed Dasher.INITIAL_ARRAY)
dashes = new float[512];
float cur = dashMinLen;
float step = 0.01f;
for (int i = 0; i < dashes.length; i += 2) {
dashes[i] = cur;
dashes[i + 1] = cur;
cur += step;
}
} else {
dashes = null;
}
if (USE_ROUND_CAPS_AND_JOINS) {
// Use both round Caps & Joins:
return new BasicStroke(width, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND, 100.0f, dashes, 0.0f);
}
return new BasicStroke(width, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 100.0f, dashes, 0.0f);
}
}