/*
* Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.pisces;
import java.util.Arrays;
import java.util.Iterator;
import sun.awt.geom.PathConsumer2D;
public class Renderer implements PathConsumer2D {
private class ScanlineIterator {
private int[] crossings;
// crossing bounds. The bounds are not necessarily tight (the scan line
// at minY, for example, might have no crossings). The x bounds will
// be accumulated as crossings are computed.
private int minY, maxY;
private int nextY;
// indices into the segment pointer lists. They indicate the "active"
// sublist in the segment lists (the portion of the list that contains
// all the segments that cross the next scan line).
private int elo, ehi;
private final int[] edgePtrs;
private int qlo, qhi;
private final int[] quadPtrs;
private int clo, chi;
private final int[] curvePtrs;
private static final int INIT_CROSSINGS_SIZE = 10;
private ScanlineIterator() {
crossings = new int[INIT_CROSSINGS_SIZE];
edgePtrs = new int[numEdges];
Helpers.fillWithIdxes(edgePtrs, SIZEOF_EDGE);
qsort(edges, edgePtrs, YMIN, 0, numEdges - 1);
quadPtrs = new int[numQuads];
Helpers.fillWithIdxes(quadPtrs, SIZEOF_QUAD);
qsort(quads, quadPtrs, YMIN, 0, numQuads - 1);
curvePtrs = new int[numCurves];
Helpers.fillWithIdxes(curvePtrs, SIZEOF_CURVE);
qsort(curves, curvePtrs, YMIN, 0, numCurves - 1);
// We don't care if we clip some of the line off with ceil, since
// no scan line crossings will be eliminated (in fact, the ceil is
// the y of the first scan line crossing).
nextY = minY = Math.max(boundsMinY, (int)Math.ceil(edgeMinY));
maxY = Math.min(boundsMaxY, (int)Math.ceil(edgeMaxY));
for (elo = 0; elo < numEdges && edges[edgePtrs[elo]+YMAX] <= minY; elo++)
;
// the active list is *edgePtrs[lo] (inclusive) *edgePtrs[hi] (exclusive)
for (ehi = elo; ehi < numEdges && edges[edgePtrs[ehi]+YMIN] <= minY; ehi++)
edgeSetCurY(edgePtrs[ehi], minY);// TODO: make minY a float to avoid casts
for (qlo = 0; qlo < numQuads && quads[quadPtrs[qlo]+YMAX] <= minY; qlo++)
;
for (qhi = qlo; qhi < numQuads && quads[quadPtrs[qhi]+YMIN] <= minY; qhi++)
quadSetCurY(quadPtrs[qhi], minY);
for (clo = 0; clo < numCurves && curves[curvePtrs[clo]+YMAX] <= minY; clo++)
;
for (chi = clo; chi < numCurves && curves[curvePtrs[chi]+YMIN] <= minY; chi++)
curveSetCurY(curvePtrs[chi], minY);
}
private int next() {
// we go through the active lists and remove segments that don't cross
// the nextY scanline.
int crossingIdx = 0;
for (int i = elo; i < ehi; i++) {
if (edges[edgePtrs[i]+YMAX] <= nextY) {
edgePtrs[i] = edgePtrs[elo++];
}
}
for (int i = qlo; i < qhi; i++) {
if (quads[quadPtrs[i]+YMAX] <= nextY) {
quadPtrs[i] = quadPtrs[qlo++];
}
}
for (int i = clo; i < chi; i++) {
if (curves[curvePtrs[i]+YMAX] <= nextY) {
curvePtrs[i] = curvePtrs[clo++];
}
}
crossings = Helpers.widenArray(crossings, 0, ehi-elo+qhi-qlo+chi-clo);
// Now every edge between lo and hi crosses nextY. Compute it's
// crossing and put it in the crossings array.
for (int i = elo; i < ehi; i++) {
int ptr = edgePtrs[i];
addCrossing(nextY, (int)edges[ptr+CURX], edges[ptr+OR], crossingIdx);
edgeGoToNextY(ptr);
crossingIdx++;
}
for (int i = qlo; i < qhi; i++) {
int ptr = quadPtrs[i];
addCrossing(nextY, (int)quads[ptr+CURX], quads[ptr+OR], crossingIdx);
quadGoToNextY(ptr);
crossingIdx++;
}
for (int i = clo; i < chi; i++) {
int ptr = curvePtrs[i];
addCrossing(nextY, (int)curves[ptr+CURX], curves[ptr+OR], crossingIdx);
curveGoToNextY(ptr);
crossingIdx++;
}
nextY++;
// Expand active lists to include new edges.
for (; ehi < numEdges && edges[edgePtrs[ehi]+YMIN] <= nextY; ehi++) {
edgeSetCurY(edgePtrs[ehi], nextY);
}
for (; qhi < numQuads && quads[quadPtrs[qhi]+YMIN] <= nextY; qhi++) {
quadSetCurY(quadPtrs[qhi], nextY);
}
for (; chi < numCurves && curves[curvePtrs[chi]+YMIN] <= nextY; chi++) {
curveSetCurY(curvePtrs[chi], nextY);
}
Arrays.sort(crossings, 0, crossingIdx);
return crossingIdx;
}
private boolean hasNext() {
return nextY < maxY;
}
private int curY() {
return nextY - 1;
}
private void addCrossing(int y, int x, float or, int idx) {
x <<= 1;
crossings[idx] = ((or > 0) ? (x | 0x1) : x);
}
}
// quicksort implementation for sorting the edge indices ("pointers")
// by increasing y0. first, last are indices into the "pointer" array
// It sorts the pointer array from first (inclusive) to last (inclusive)
private static void qsort(final float[] data, final int[] ptrs,
final int fieldForCmp, int first, int last)
{
if (last > first) {
int p = partition(data, ptrs, fieldForCmp, first, last);
if (first < p - 1) {
qsort(data, ptrs, fieldForCmp, first, p - 1);
}
if (p < last) {
qsort(data, ptrs, fieldForCmp, p, last);
}
}
}
// i, j are indices into edgePtrs.
private static int partition(final float[] data, final int[] ptrs,
final int fieldForCmp, int i, int j)
{
int pivotValFieldForCmp = ptrs[i]+fieldForCmp;
while (i <= j) {
// edges[edgePtrs[i]+1] is equivalent to (*(edgePtrs[i])).y0 in C
while (data[ptrs[i]+fieldForCmp] < data[pivotValFieldForCmp])
i++;
while (data[ptrs[j]+fieldForCmp] > data[pivotValFieldForCmp])
j--;
if (i <= j) {
int tmp = ptrs[i];
ptrs[i] = ptrs[j];
ptrs[j] = tmp;
i++;
j--;
}
}
return i;
}
//============================================================================
//////////////////////////////////////////////////////////////////////////////
// EDGE LIST
//////////////////////////////////////////////////////////////////////////////
// TODO(maybe): very tempting to use fixed point here. A lot of opportunities
// for shifts and just removing certain operations altogether.
// TODO: it might be worth it to make an EdgeList class. It would probably
// clean things up a bit and not impact performance much.
// common to all types of input path segments.
private static final int YMIN = 0;
private static final int YMAX = 1;
private static final int CURX = 2;
// this and OR are meant to be indeces into "int" fields, but arrays must
// be homogenous, so every field is a float. However floats can represent
// exactly up to 26 bit ints, so we're ok.
private static final int CURY = 3;
private static final int OR = 4;
// for straight lines only:
private static final int SLOPE = 5;
// for quads and cubics:
private static final int X0 = 5;
private static final int Y0 = 6;
private static final int XL = 7;
private static final int COUNT = 8;
private static final int CURSLOPE = 9;
private static final int DX = 10;
private static final int DY = 11;
private static final int DDX = 12;
private static final int DDY = 13;
// for cubics only
private static final int DDDX = 14;
private static final int DDDY = 15;
private float edgeMinY = Float.POSITIVE_INFINITY;
private float edgeMaxY = Float.NEGATIVE_INFINITY;
private float edgeMinX = Float.POSITIVE_INFINITY;
private float edgeMaxX = Float.NEGATIVE_INFINITY;
private static final int SIZEOF_EDGE = 6;
private float[] edges = null;
private int numEdges;
// these are static because we need them to be usable from ScanlineIterator
private void edgeSetCurY(final int idx, int y) {
edges[idx+CURX] += (y - edges[idx+CURY]) * edges[idx+SLOPE];
edges[idx+CURY] = y;
}
private void edgeGoToNextY(final int idx) {
edges[idx+CURY] += 1;
edges[idx+CURX] += edges[idx+SLOPE];
}
private static final int SIZEOF_QUAD = 14;
private float[] quads = null;
private int numQuads;
// This function should be called exactly once, to set the first scanline
// of the curve. Before it is called, the curve should think its first
// scanline is CEIL(YMIN).
private void quadSetCurY(final int idx, final int y) {
assert y < quads[idx+YMAX];
assert (quads[idx+CURY] > y);
assert (quads[idx+CURY] == Math.ceil(quads[idx+CURY]));
while (quads[idx+CURY] < ((float)y)) {
quadGoToNextY(idx);
}
}
private void quadGoToNextY(final int idx) {
quads[idx+CURY] += 1;
// this will get overriden if the while executes.
quads[idx+CURX] += quads[idx+CURSLOPE];
int count = (int)quads[idx+COUNT];
// this loop should never execute more than once because our
// curve is monotonic in Y. Still we put it in because you can
// never be too sure when dealing with floating point.
while(quads[idx+CURY] >= quads[idx+Y0] && count > 0) {
float x0 = quads[idx+X0], y0 = quads[idx+Y0];
count = executeQuadAFDIteration(idx);
float x1 = quads[idx+X0], y1 = quads[idx+Y0];
// our quads are monotonic, so this shouldn't happen, but
// it is conceivable that for very flat quads with different
// y values at their endpoints AFD might give us a horizontal
// segment.
if (y1 == y0) {
continue;
}
quads[idx+CURSLOPE] = (x1 - x0) / (y1 - y0);
quads[idx+CURX] = x0 + (quads[idx+CURY] - y0) * quads[idx+CURSLOPE];
}
}
private static final int SIZEOF_CURVE = 16;
private float[] curves = null;
private int numCurves;
private void curveSetCurY(final int idx, final int y) {
assert y < curves[idx+YMAX];
assert (curves[idx+CURY] > y);
assert (curves[idx+CURY] == Math.ceil(curves[idx+CURY]));
while (curves[idx+CURY] < ((float)y)) {
curveGoToNextY(idx);
}
}
private void curveGoToNextY(final int idx) {
curves[idx+CURY] += 1;
// this will get overriden if the while executes.
curves[idx+CURX] += curves[idx+CURSLOPE];
int count = (int)curves[idx+COUNT];
// this loop should never execute more than once because our
// curve is monotonic in Y. Still we put it in because you can
// never be too sure when dealing with floating point.
while(curves[idx+CURY] >= curves[idx+Y0] && count > 0) {
float x0 = curves[idx+X0], y0 = curves[idx+Y0];
count = executeCurveAFDIteration(idx);
float x1 = curves[idx+X0], y1 = curves[idx+Y0];
// our curves are monotonic, so this shouldn't happen, but
// it is conceivable that for very flat curves with different
// y values at their endpoints AFD might give us a horizontal
// segment.
if (y1 == y0) {
continue;
}
curves[idx+CURSLOPE] = (x1 - x0) / (y1 - y0);
curves[idx+CURX] = x0 + (curves[idx+CURY] - y0) * curves[idx+CURSLOPE];
}
}
private static final float DEC_BND = 20f;
private static final float INC_BND = 8f;
// Flattens using adaptive forward differencing. This only carries out
// one iteration of the AFD loop. All it does is update AFD variables (i.e.
// X0, Y0, D*[X|Y], COUNT; not variables used for computing scanline crossings).
private int executeQuadAFDIteration(int idx) {
int count = (int)quads[idx+COUNT];
float ddx = quads[idx+DDX];
float ddy = quads[idx+DDY];
float dx = quads[idx+DX];
float dy = quads[idx+DY];
while (Math.abs(ddx) > DEC_BND || Math.abs(ddy) > DEC_BND) {
ddx = ddx / 4;
ddy = ddy / 4;
dx = (dx - ddx) / 2;
dy = (dy - ddy) / 2;
count <<= 1;
}
// can only do this on even "count" values, because we must divide count by 2
while (count % 2 == 0 && Math.abs(dx) <= INC_BND && Math.abs(dy) <= INC_BND) {
dx = 2 * dx + ddx;
dy = 2 * dy + ddy;
ddx = 4 * ddx;
ddy = 4 * ddy;
count >>= 1;
}
count--;
if (count > 0) {
quads[idx+X0] += dx;
dx += ddx;
quads[idx+Y0] += dy;
dy += ddy;
} else {
quads[idx+X0] = quads[idx+XL];
quads[idx+Y0] = quads[idx+YMAX];
}
quads[idx+COUNT] = count;
quads[idx+DDX] = ddx;
quads[idx+DDY] = ddy;
quads[idx+DX] = dx;
quads[idx+DY] = dy;
return count;
}
private int executeCurveAFDIteration(int idx) {
int count = (int)curves[idx+COUNT];
float ddx = curves[idx+DDX];
float ddy = curves[idx+DDY];
float dx = curves[idx+DX];
float dy = curves[idx+DY];
float dddx = curves[idx+DDDX];
float dddy = curves[idx+DDDY];
while (Math.abs(ddx) > DEC_BND || Math.abs(ddy) > DEC_BND) {
dddx /= 8;
dddy /= 8;
ddx = ddx/4 - dddx;
ddy = ddy/4 - dddy;
dx = (dx - ddx) / 2;
dy = (dy - ddy) / 2;
count <<= 1;
}
// can only do this on even "count" values, because we must divide count by 2
while (count % 2 == 0 && Math.abs(dx) <= INC_BND && Math.abs(dy) <= INC_BND) {
dx = 2 * dx + ddx;
dy = 2 * dy + ddy;
ddx = 4 * (ddx + dddx);
ddy = 4 * (ddy + dddy);
dddx = 8 * dddx;
dddy = 8 * dddy;
count >>= 1;
}
count--;
if (count > 0) {
curves[idx+X0] += dx;
dx += ddx;
ddx += dddx;
curves[idx+Y0] += dy;
dy += ddy;
ddy += dddy;
} else {
curves[idx+X0] = curves[idx+XL];
curves[idx+Y0] = curves[idx+YMAX];
}
curves[idx+COUNT] = count;
curves[idx+DDDX] = dddx;
curves[idx+DDDY] = dddy;
curves[idx+DDX] = ddx;
curves[idx+DDY] = ddy;
curves[idx+DX] = dx;
curves[idx+DY] = dy;
return count;
}
private void initLine(final int idx, float[] pts, int or) {
edges[idx+SLOPE] = (pts[2] - pts[0]) / (pts[3] - pts[1]);
edges[idx+CURX] = pts[0] + (edges[idx+CURY] - pts[1]) * edges[idx+SLOPE];
}
private void initQuad(final int idx, float[] points, int or) {
final int countlg = 3;
final int count = 1 << countlg;
// the dx and dy refer to forward differencing variables, not the last
// coefficients of the "points" polynomial
final float ddx, ddy, dx, dy;
c.set(points, 6);
ddx = c.dbx / (1 << (2 * countlg));
ddy = c.dby / (1 << (2 * countlg));
dx = c.bx / (1 << (2 * countlg)) + c.cx / (1 << countlg);
dy = c.by / (1 << (2 * countlg)) + c.cy / (1 << countlg);
quads[idx+DDX] = ddx;
quads[idx+DDY] = ddy;
quads[idx+DX] = dx;
quads[idx+DY] = dy;
quads[idx+COUNT] = count;
quads[idx+XL] = points[4];
quads[idx+X0] = points[0];
quads[idx+Y0] = points[1];
executeQuadAFDIteration(idx);
float x1 = quads[idx+X0], y1 = quads[idx+Y0];
quads[idx+CURSLOPE] = (x1 - points[0]) / (y1 - points[1]);
quads[idx+CURX] = points[0] + (quads[idx+CURY] - points[1])*quads[idx+CURSLOPE];
}
private void initCurve(final int idx, float[] points, int or) {
final int countlg = 3;
final int count = 1 << countlg;
// the dx and dy refer to forward differencing variables, not the last
// coefficients of the "points" polynomial
final float dddx, dddy, ddx, ddy, dx, dy;
c.set(points, 8);
dddx = 2f * c.dax / (1 << (3 * countlg));
dddy = 2f * c.day / (1 << (3 * countlg));
ddx = dddx + c.dbx / (1 << (2 * countlg));
ddy = dddy + c.dby / (1 << (2 * countlg));
dx = c.ax / (1 << (3 * countlg)) + c.bx / (1 << (2 * countlg)) + c.cx / (1 << countlg);
dy = c.ay / (1 << (3 * countlg)) + c.by / (1 << (2 * countlg)) + c.cy / (1 << countlg);
curves[idx+DDDX] = dddx;
curves[idx+DDDY] = dddy;
curves[idx+DDX] = ddx;
curves[idx+DDY] = ddy;
curves[idx+DX] = dx;
curves[idx+DY] = dy;
curves[idx+COUNT] = count;
curves[idx+XL] = points[6];
curves[idx+X0] = points[0];
curves[idx+Y0] = points[1];
executeCurveAFDIteration(idx);
float x1 = curves[idx+X0], y1 = curves[idx+Y0];
curves[idx+CURSLOPE] = (x1 - points[0]) / (y1 - points[1]);
curves[idx+CURX] = points[0] + (curves[idx+CURY] - points[1])*curves[idx+CURSLOPE];
}
private void addPathSegment(float[] pts, final int type, final int or) {
int idx;
float[] addTo;
switch (type) {
case 4:
idx = numEdges * SIZEOF_EDGE;
addTo = edges = Helpers.widenArray(edges, numEdges*SIZEOF_EDGE, SIZEOF_EDGE);
numEdges++;
break;
case 6:
idx = numQuads * SIZEOF_QUAD;
addTo = quads = Helpers.widenArray(quads, numQuads*SIZEOF_QUAD, SIZEOF_QUAD);
numQuads++;
break;
case 8:
idx = numCurves * SIZEOF_CURVE;
addTo = curves = Helpers.widenArray(curves, numCurves*SIZEOF_CURVE, SIZEOF_CURVE);
numCurves++;
break;
default:
throw new InternalError();
}
// set the common fields, except CURX, for which we must know the kind
// of curve. NOTE: this must be done before the type specific fields
// are initialized, because those depend on the common ones.
addTo[idx+YMIN] = pts[1];
addTo[idx+YMAX] = pts[type-1];
addTo[idx+OR] = or;
addTo[idx+CURY] = (float)Math.ceil(pts[1]);
switch (type) {
case 4:
initLine(idx, pts, or);
break;
case 6:
initQuad(idx, pts, or);
break;
case 8:
initCurve(idx, pts, or);
break;
default:
throw new InternalError();
}
}
// precondition: the curve in pts must be monotonic and increasing in y.
private void somethingTo(float[] pts, final int type, final int or) {
// NOTE: it's very important that we check for or >= 0 below (as
// opposed to or == 1, or or > 0, or anything else). That's
// because if we check for or==1, when the curve being added
// is a horizontal line, or will be 0 so or==1 will be false and
// x0 and y0 will be updated to pts[0] and pts[1] instead of pts[type-2]
// and pts[type-1], which is the correct thing to do.
this.x0 = or >= 0 ? pts[type - 2] : pts[0];
this.y0 = or >= 0 ? pts[type - 1] : pts[1];
float minY = pts[1], maxY = pts[type - 1];
if (Math.ceil(minY) >= Math.ceil(maxY) ||
Math.ceil(minY) >= boundsMaxY || maxY < boundsMinY)
{
return;
}
if (minY < edgeMinY) { edgeMinY = minY; }
if (maxY > edgeMaxY) { edgeMaxY = maxY; }
int minXidx = (pts[0] < pts[type-2] ? 0 : type - 2);
float minX = pts[minXidx];
float maxX = pts[type - 2 - minXidx];
if (minX < edgeMinX) { edgeMinX = minX; }
if (maxX > edgeMaxX) { edgeMaxX = maxX; }
addPathSegment(pts, type, or);
}
// END EDGE LIST
//////////////////////////////////////////////////////////////////////////////
public static final int WIND_EVEN_ODD = 0;
public static final int WIND_NON_ZERO = 1;
// Antialiasing
final private int SUBPIXEL_LG_POSITIONS_X;
final private int SUBPIXEL_LG_POSITIONS_Y;
final private int SUBPIXEL_POSITIONS_X;
final private int SUBPIXEL_POSITIONS_Y;
final private int SUBPIXEL_MASK_X;
final private int SUBPIXEL_MASK_Y;
final int MAX_AA_ALPHA;
// Cache to store RLE-encoded coverage mask of the current primitive
PiscesCache cache;
// Bounds of the drawing region, at subpixel precision.
private final int boundsMinX, boundsMinY, boundsMaxX, boundsMaxY;
// Current winding rule
private final int windingRule;
// Current drawing position, i.e., final point of last segment
private float x0, y0;
// Position of most recent 'moveTo' command
private float pix_sx0, pix_sy0;
public Renderer(int subpixelLgPositionsX, int subpixelLgPositionsY,
int pix_boundsX, int pix_boundsY,
int pix_boundsWidth, int pix_boundsHeight,
int windingRule)
{
this.SUBPIXEL_LG_POSITIONS_X = subpixelLgPositionsX;
this.SUBPIXEL_LG_POSITIONS_Y = subpixelLgPositionsY;
this.SUBPIXEL_MASK_X = (1 << (SUBPIXEL_LG_POSITIONS_X)) - 1;
this.SUBPIXEL_MASK_Y = (1 << (SUBPIXEL_LG_POSITIONS_Y)) - 1;
this.SUBPIXEL_POSITIONS_X = 1 << (SUBPIXEL_LG_POSITIONS_X);
this.SUBPIXEL_POSITIONS_Y = 1 << (SUBPIXEL_LG_POSITIONS_Y);
this.MAX_AA_ALPHA = (SUBPIXEL_POSITIONS_X * SUBPIXEL_POSITIONS_Y);
this.windingRule = windingRule;
this.boundsMinX = pix_boundsX * SUBPIXEL_POSITIONS_X;
this.boundsMinY = pix_boundsY * SUBPIXEL_POSITIONS_Y;
this.boundsMaxX = (pix_boundsX + pix_boundsWidth) * SUBPIXEL_POSITIONS_X;
this.boundsMaxY = (pix_boundsY + pix_boundsHeight) * SUBPIXEL_POSITIONS_Y;
}
private float tosubpixx(float pix_x) {
return pix_x * SUBPIXEL_POSITIONS_X;
}
private float tosubpixy(float pix_y) {
return pix_y * SUBPIXEL_POSITIONS_Y;
}
public void moveTo(float pix_x0, float pix_y0) {
closePath();
this.pix_sx0 = pix_x0;
this.pix_sy0 = pix_y0;
this.y0 = tosubpixy(pix_y0);
this.x0 = tosubpixx(pix_x0);
}
public void lineJoin() { /* do nothing */ }
private final float[][] pts = new float[2][8];
private final float[] ts = new float[4];
private static void invertPolyPoints(float[] pts, int off, int type) {
for (int i = off, j = off + type - 2; i < j; i += 2, j -= 2) {
float tmp = pts[i];
pts[i] = pts[j];
pts[j] = tmp;
tmp = pts[i+1];
pts[i+1] = pts[j+1];
pts[j+1] = tmp;
}
}
// return orientation before making the curve upright.
private static int makeMonotonicCurveUpright(float[] pts, int off, int type) {
float y0 = pts[off + 1];
float y1 = pts[off + type - 1];
if (y0 > y1) {
invertPolyPoints(pts, off, type);
return -1;
} else if (y0 < y1) {
return 1;
}
return 0;
}
public void lineTo(float pix_x1, float pix_y1) {
pts[0][0] = x0; pts[0][1] = y0;
pts[0][2] = tosubpixx(pix_x1); pts[0][3] = tosubpixy(pix_y1);
int or = makeMonotonicCurveUpright(pts[0], 0, 4);
somethingTo(pts[0], 4, or);
}
Curve c = new Curve();
private void curveOrQuadTo(int type) {
c.set(pts[0], type);
int numTs = c.dxRoots(ts, 0);
numTs += c.dyRoots(ts, numTs);
numTs = Helpers.filterOutNotInAB(ts, 0, numTs, 0, 1);
Helpers.isort(ts, 0, numTs);
Iterator<float[]> it = Curve.breakPtsAtTs(pts, type, ts, numTs);
while(it.hasNext()) {
float[] curCurve = it.next();
int or = makeMonotonicCurveUpright(curCurve, 0, type);
somethingTo(curCurve, type, or);
}
}
@Override public void curveTo(float x1, float y1,
float x2, float y2,
float x3, float y3)
{
pts[0][0] = x0; pts[0][1] = y0;
pts[0][2] = tosubpixx(x1); pts[0][3] = tosubpixy(y1);
pts[0][4] = tosubpixx(x2); pts[0][5] = tosubpixy(y2);
pts[0][6] = tosubpixx(x3); pts[0][7] = tosubpixy(y3);
curveOrQuadTo(8);
}
@Override public void quadTo(float x1, float y1, float x2, float y2) {
pts[0][0] = x0; pts[0][1] = y0;
pts[0][2] = tosubpixx(x1); pts[0][3] = tosubpixy(y1);
pts[0][4] = tosubpixx(x2); pts[0][5] = tosubpixy(y2);
curveOrQuadTo(6);
}
public void closePath() {
// lineTo expects its input in pixel coordinates.
lineTo(pix_sx0, pix_sy0);
}
public void pathDone() {
closePath();
}
@Override
public long getNativeConsumer() {
throw new InternalError("Renderer does not use a native consumer.");
}
private void _endRendering(final int pix_bboxx0, final int pix_bboxy0,
final int pix_bboxx1, final int pix_bboxy1)
{
// Mask to determine the relevant bit of the crossing sum
// 0x1 if EVEN_ODD, all bits if NON_ZERO
int mask = (windingRule == WIND_EVEN_ODD) ? 0x1 : ~0x0;
// add 1 to better deal with the last pixel in a pixel row.
int width = pix_bboxx1 - pix_bboxx0 + 1;
int[] alpha = new int[width+1];
int bboxx0 = pix_bboxx0 << SUBPIXEL_LG_POSITIONS_X;
int bboxx1 = pix_bboxx1 << SUBPIXEL_LG_POSITIONS_X;
// Now we iterate through the scanlines. We must tell emitRow the coord
// of the first non-transparent pixel, so we must keep accumulators for
// the first and last pixels of the section of the current pixel row
// that we will emit.
// We also need to accumulate pix_bbox*, but the iterator does it
// for us. We will just get the values from it once this loop is done
int pix_maxX = Integer.MIN_VALUE;
int pix_minX = Integer.MAX_VALUE;
int y = boundsMinY; // needs to be declared here so we emit the last row properly.
ScanlineIterator it = this.new ScanlineIterator();
for ( ; it.hasNext(); ) {
int numCrossings = it.next();
int[] crossings = it.crossings;
y = it.curY();
if (numCrossings > 0) {
int lowx = crossings[0] >> 1;
int highx = crossings[numCrossings - 1] >> 1;
int x0 = Math.max(lowx, bboxx0);
int x1 = Math.min(highx, bboxx1);
pix_minX = Math.min(pix_minX, x0 >> SUBPIXEL_LG_POSITIONS_X);
pix_maxX = Math.max(pix_maxX, x1 >> SUBPIXEL_LG_POSITIONS_X);
}
int sum = 0;
int prev = bboxx0;
for (int i = 0; i < numCrossings; i++) {
int curxo = crossings[i];
int curx = curxo >> 1;
int crorientation = ((curxo & 0x1) == 0x1) ? 1 : -1;
if ((sum & mask) != 0) {
int x0 = Math.max(prev, bboxx0);
int x1 = Math.min(curx, bboxx1);
if (x0 < x1) {
x0 -= bboxx0; // turn x0, x1 from coords to indeces
x1 -= bboxx0; // in the alpha array.
int pix_x = x0 >> SUBPIXEL_LG_POSITIONS_X;
int pix_xmaxm1 = (x1 - 1) >> SUBPIXEL_LG_POSITIONS_X;
if (pix_x == pix_xmaxm1) {
// Start and end in same pixel
alpha[pix_x] += (x1 - x0);
alpha[pix_x+1] -= (x1 - x0);
} else {
int pix_xmax = x1 >> SUBPIXEL_LG_POSITIONS_X;
alpha[pix_x] += SUBPIXEL_POSITIONS_X - (x0 & SUBPIXEL_MASK_X);
alpha[pix_x+1] += (x0 & SUBPIXEL_MASK_X);
alpha[pix_xmax] -= SUBPIXEL_POSITIONS_X - (x1 & SUBPIXEL_MASK_X);
alpha[pix_xmax+1] -= (x1 & SUBPIXEL_MASK_X);
}
}
}
sum += crorientation;
prev = curx;
}
// even if this last row had no crossings, alpha will be zeroed
// from the last emitRow call. But this doesn't matter because
// maxX < minX, so no row will be emitted to the cache.
if ((y & SUBPIXEL_MASK_Y) == SUBPIXEL_MASK_Y) {
emitRow(alpha, y >> SUBPIXEL_LG_POSITIONS_Y, pix_minX, pix_maxX);
pix_minX = Integer.MAX_VALUE;
pix_maxX = Integer.MIN_VALUE;
}
}
// Emit final row
if (pix_maxX >= pix_minX) {
emitRow(alpha, y >> SUBPIXEL_LG_POSITIONS_Y, pix_minX, pix_maxX);
}
}
public void endRendering() {
final int bminx = boundsMinX >> SUBPIXEL_LG_POSITIONS_X;
final int bmaxx = boundsMaxX >> SUBPIXEL_LG_POSITIONS_X;
final int bminy = boundsMinY >> SUBPIXEL_LG_POSITIONS_Y;
final int bmaxy = boundsMaxY >> SUBPIXEL_LG_POSITIONS_Y;
final int eminx = ((int)Math.floor(edgeMinX)) >> SUBPIXEL_LG_POSITIONS_X;
final int emaxx = ((int)Math.ceil(edgeMaxX)) >> SUBPIXEL_LG_POSITIONS_X;
final int eminy = ((int)Math.floor(edgeMinY)) >> SUBPIXEL_LG_POSITIONS_Y;
final int emaxy = ((int)Math.ceil(edgeMaxY)) >> SUBPIXEL_LG_POSITIONS_Y;
final int minX = Math.max(bminx, eminx);
final int maxX = Math.min(bmaxx, emaxx);
final int minY = Math.max(bminy, eminy);
final int maxY = Math.min(bmaxy, emaxy);
if (minX > maxX || minY > maxY) {
this.cache = new PiscesCache(bminx, bminy, bmaxx, bmaxy);
return;
}
this.cache = new PiscesCache(minX, minY, maxX, maxY);
_endRendering(minX, minY, maxX, maxY);
}
public PiscesCache getCache() {
if (cache == null) {
throw new InternalError("cache not yet initialized");
}
return cache;
}
private void emitRow(int[] alphaRow, int pix_y, int pix_from, int pix_to) {
// Copy rowAA data into the cache if one is present
if (cache != null) {
if (pix_to >= pix_from) {
cache.startRow(pix_y, pix_from);
// Perform run-length encoding and store results in the cache
int from = pix_from - cache.bboxX0;
int to = pix_to - cache.bboxX0;
int runLen = 1;
int startVal = alphaRow[from];
for (int i = from + 1; i <= to; i++) {
int nextVal = startVal + alphaRow[i];
if (nextVal == startVal) {
runLen++;
} else {
cache.addRLERun(startVal, runLen);
runLen = 1;
startVal = nextVal;
}
}
cache.addRLERun(startVal, runLen);
}
}
java.util.Arrays.fill(alphaRow, 0);
}
}