提交 c17dc475 编写于 作者: L lbourges

8143849: Integrate Marlin renderer per JEP 265

Summary: Include Marlin changes from 8149896 to avoid need to introduce FloatMath.powerOfTwoD
Reviewed-by: alexsch, andrew
上级 c5e98c52
/*
* 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;
}
}
/*
* 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;
}
}
}
}
}
/*
* 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;
}
}
/*
* 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;
}
}
}
此差异已折叠。
/*
* 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;
}
}
}
}
}
/*
* 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;
}
}
/*
* 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;
}
}
}
/*
* 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;
}
}
}
}
}
此差异已折叠。
/*
* 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;
}
/*
* 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;
}
}
/*
* 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 sun.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);
}
}
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
# Pisces Rendering Engine module
sun.java2d.pisces.PiscesRenderingEngine
# Marlin Rendering Engine module
sun.java2d.marlin.MarlinRenderingEngine
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册