blob: fa58146a5ad532a31e5eefb8578e6a71f7aa426e [file] [log] [blame]
/*
* Copyright (c) 2007, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package sun.java2d.pisces;
public class Renderer extends LineSink {
public static final int WIND_EVEN_ODD = 0;
public static final int WIND_NON_ZERO = 1;
// Initial edge list size
// IMPL_NOTE - restore size after growth
public static final int INITIAL_EDGES = 1000;
// Recommended maximum scratchpad sizes. The arrays will grow
// larger if needed, but when finished() is called they will be released
// if they have grown larger than these sizes.
public static final int DEFAULT_INDICES_SIZE = 8192;
public static final int DEFAULT_CROSSINGS_SIZE = 32*1024;
// Antialiasing
private int SUBPIXEL_LG_POSITIONS_X;
private int SUBPIXEL_LG_POSITIONS_Y;
private int SUBPIXEL_MASK_X;
private int SUBPIXEL_MASK_Y;
private int SUBPIXEL_POSITIONS_X;
private int SUBPIXEL_POSITIONS_Y;
int MAX_AA_ALPHA;
private int MAX_AA_ALPHA_DENOM;
private int HALF_MAX_AA_ALPHA_DENOM;
private int XSHIFT;
private int YSHIFT;
private int YSTEP;
private int HYSTEP;
private int YMASK;
private static final int MIN_QUAD_OPT_WIDTH = 100 << 16;
// Cache to store RLE-encoded coverage mask of the current primitive
PiscesCache cache;
// Bounds of the drawing region, at S15.16 precsion
private int boundsMinX, boundsMinY, boundsMaxX, boundsMaxY;
// Bounds of the current primitive, at subsample precision
private int rasterMinX, rasterMaxX, rasterMinY, rasterMaxY;
// Pixel bounding box for current primitive
private int bboxX0, bboxY0, bboxX1, bboxY1;
// Current winding rule
private int windingRule;
// Current drawing position, i.e., final point of last segment
private int x0, y0;
// Position of most recent 'moveTo' command
private int sx0, sy0;
// Buffer to be filled with one row's worth of alpha values
private byte[] rowAA; // needs to be short if 16x16 subsampling
// Track the number of vertical extrema of the incoming edge list
// in order to determine the maximum number of crossings of a
// scanline
private int firstOrientation;
private int lastOrientation;
private int flips;
// Parameters for emitRow
private int alphaWidth;
public Renderer() {
}
public void setAntialiasing(int subpixelLgPositionsX,
int subpixelLgPositionsY) {
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.MAX_AA_ALPHA_DENOM = 255*MAX_AA_ALPHA;
this.HALF_MAX_AA_ALPHA_DENOM = MAX_AA_ALPHA_DENOM/2;
this.XSHIFT = 16 - SUBPIXEL_LG_POSITIONS_X;
this.YSHIFT = 16 - SUBPIXEL_LG_POSITIONS_Y;
this.YSTEP = 1 << YSHIFT;
this.HYSTEP = 1 << (YSHIFT - 1);
this.YMASK = ~(YSTEP - 1);
}
public int getSubpixelLgPositionsX() {
return SUBPIXEL_LG_POSITIONS_X;
}
public int getSubpixelLgPositionsY() {
return SUBPIXEL_LG_POSITIONS_Y;
}
public void setWindingRule(int windingRule) {
this.windingRule = windingRule;
}
public int getWindingRule() {
return windingRule;
}
public void beginRendering(int boundsX, int boundsY,
int boundsWidth, int boundsHeight) {
lastOrientation = 0;
flips = 0;
resetEdges();
this.boundsMinX = boundsX << 16;
this.boundsMinY = boundsY << 16;
this.boundsMaxX = (boundsX + boundsWidth) << 16;
this.boundsMaxY = (boundsY + boundsHeight) << 16;
this.bboxX0 = boundsX;
this.bboxY0 = boundsY;
this.bboxX1 = boundsX + boundsWidth;
this.bboxY1 = boundsY + boundsHeight;
}
public void moveTo(int x0, int y0) {
// System.out.println("Renderer: moveTo " + x0/65536.0 + " " + y0/65536.0);
close();
this.sx0 = this.x0 = x0;
this.sy0 = this.y0 = y0;
this.lastOrientation = 0;
}
public void lineJoin() {
// System.out.println("Renderer: lineJoin");
// do nothing
}
public void lineTo(int x1, int y1) {
// System.out.println("Renderer: lineTo " + x1/65536.0 + " " + y1/65536.0);
// Ignore horizontal lines
// Next line will count flip
if (y0 == y1) {
this.x0 = x1;
return;
}
int orientation = (y0 < y1) ? 1 : -1;
if (lastOrientation == 0) {
firstOrientation = orientation;
} else if (orientation != lastOrientation) {
++flips;
}
lastOrientation = orientation;
// Bias Y by 1 ULP so endpoints never lie on a scanline
addEdge(x0, y0 | 0x1, x1, y1 | 0x1);
this.x0 = x1;
this.y0 = y1;
}
public void close() {
// System.out.println("Renderer: close");
int orientation = lastOrientation;
if (y0 != sy0) {
orientation = (y0 < sy0) ? 1 : -1;
}
if (orientation != firstOrientation) {
++flips;
}
lineTo(sx0, sy0);
}
public void end() {
close();
// System.out.println("Renderer: end");
// do nothing
}
// Scan convert a single edge
private void computeCrossingsForEdge(int index,
int boundsMinY, int boundsMaxY) {
int iy0 = edges[index + 1];
int iy1 = edges[index + 3];
// Clip to valid Y range
int clipy0 = (iy0 > boundsMinY) ? iy0 : boundsMinY;
int clipy1 = (iy1 < boundsMaxY) ? iy1 : boundsMaxY;
int minY = ((clipy0 + HYSTEP) & YMASK) + HYSTEP;
int maxY = ((clipy1 - HYSTEP) & YMASK) + HYSTEP;
// IMPL_NOTE - If line falls outside the valid X range, could
// draw a vertical line instead
// Exit if no scanlines are crossed
if (minY > maxY) {
return;
}
// Scan convert line using a DDA approach
int ix0 = edges[index];
int ix1 = edges[index + 2];
long dx = ((long) ix1) - ix0;
long dy = ((long) iy1) - iy0;
// Compute first crossing point at y = minY
int orientation = edges[index + 4];
int y = minY;
long lx = (((long) y) - iy0)*dx/dy + ix0;
addCrossing(y >> YSHIFT, (int)(lx >> XSHIFT), orientation);
// Advance y to next scanline, exit if past endpoint
y += YSTEP;
if (y > maxY) {
return;
}
// Compute xstep only if additional scanlines are crossed
// For each scanline, add xstep to lx and YSTEP to y and
// emit the new crossing
long xstep = ((long)YSTEP*dx)/dy;
for (; y <= maxY; y += YSTEP) {
lx += xstep;
addCrossing(y >> YSHIFT, (int)(lx >> XSHIFT), orientation);
}
}
private void computeBounds() {
rasterMinX = crossingMinX & ~SUBPIXEL_MASK_X;
rasterMaxX = crossingMaxX | SUBPIXEL_MASK_X;
rasterMinY = crossingMinY & ~SUBPIXEL_MASK_Y;
rasterMaxY = crossingMaxY | SUBPIXEL_MASK_Y;
// If nothing was drawn, we have:
// minX = Integer.MAX_VALUE and maxX = Integer.MIN_VALUE
// so nothing to render
if (rasterMinX > rasterMaxX || rasterMinY > rasterMaxY) {
rasterMinX = 0;
rasterMaxX = -1;
rasterMinY = 0;
rasterMaxY = -1;
return;
}
if (rasterMinX < boundsMinX >> XSHIFT) {
rasterMinX = boundsMinX >> XSHIFT;
}
if (rasterMinY < boundsMinY >> YSHIFT) {
rasterMinY = boundsMinY >> YSHIFT;
}
if (rasterMaxX > boundsMaxX >> XSHIFT) {
rasterMaxX = boundsMaxX >> XSHIFT;
}
if (rasterMaxY > boundsMaxY >> YSHIFT) {
rasterMaxY = boundsMaxY >> YSHIFT;
}
}
private int clamp(int x, int min, int max) {
if (x < min) {
return min;
} else if (x > max) {
return max;
}
return x;
}
private void _endRendering() {
if (flips == 0) {
bboxX0 = bboxY0 = 0;
bboxX1 = bboxY1 = -1;
return;
}
// Special case for filling a single rect with a flat, opaque color
// REMIND: This special case was not originally written to fill a
// cache object and called directly to a Blit - it needs some code
// to fill the cache instead to be useful for this usage...
if (false /* Does not work with cache (yet?) */ &&
edgeIdx == 10 &&
edges[0] == edges[2] &&
edges[1] == edges[6] &&
edges[3] == edges[8] &&
edges[5] == edges[7] &&
Math.abs(edges[0] - edges[5]) > MIN_QUAD_OPT_WIDTH)
{
int x0 = edges[0] >> XSHIFT;
int y0 = edges[1] >> YSHIFT;
int x1 = edges[5] >> XSHIFT;
int y1 = edges[3] >> YSHIFT;
if (x0 > x1) {
int tmp = x0;
x0 = x1;
x1 = tmp;
}
if (y0 > y1) {
int tmp = y0;
y0 = y1;
y1 = tmp;
}
int bMinX = this.boundsMinX >> XSHIFT;
int bMinY = this.boundsMinY >> YSHIFT;
int bMaxX = this.boundsMaxX >> XSHIFT;
int bMaxY = this.boundsMaxY >> YSHIFT;
// Clip to image bounds in supersampled coordinates
x0 = clamp(x0, bMinX, bMaxX);
x1 = clamp(x1, bMinX, bMaxX);
y0 = clamp(y0, bMinY, bMaxY);
y1 = clamp(y1, bMinY, bMaxY);
/*
* REMIND: Need to fill the cache here instead...
Blit.fillRectSrcOver(this,
imageData, imageType,
imageOffset,
imageScanlineStride, imagePixelStride,
width, height,
x0, y0, x1, y1,
cred, cgreen, cblue);
*/
bboxX0 = x0 >> SUBPIXEL_LG_POSITIONS_X;
bboxY0 = y0 >> SUBPIXEL_LG_POSITIONS_Y;
bboxX1 = (x1 + SUBPIXEL_POSITIONS_X - 1)
>> SUBPIXEL_LG_POSITIONS_X;
bboxY1 = (y1 + SUBPIXEL_POSITIONS_Y - 1)
>> SUBPIXEL_LG_POSITIONS_Y;
return;
}
int minY = (edgeMinY > boundsMinY) ? edgeMinY : boundsMinY;
int maxY = (edgeMaxY < boundsMaxY) ? edgeMaxY : boundsMaxY;
// Check for empty intersection of primitive with the drawing area
if (minY > maxY) {
bboxX0 = bboxY0 = 0;
bboxX1 = bboxY1 = -1;
return;
}
// Compute Y extent in subpixel coordinates
int iminY = (minY >> YSHIFT) & ~SUBPIXEL_MASK_Y;
int imaxY = (maxY >> YSHIFT) | SUBPIXEL_MASK_Y;
int yextent = (imaxY - iminY) + 1;
// Maximum number of crossings
int size = flips*yextent;
int bmax = (boundsMaxY >> YSHIFT) - 1;
if (imaxY > bmax) {
imaxY = bmax;
}
// Initialize X bounds, will be refined for each strip
bboxX0 = Integer.MAX_VALUE;
bboxX1 = Integer.MIN_VALUE;
// Set Y bounds
bboxY0 = iminY >> SUBPIXEL_LG_POSITIONS_Y;
bboxY1 = (imaxY + SUBPIXEL_POSITIONS_Y - 1) >> SUBPIXEL_LG_POSITIONS_Y;
// Compute number of rows that can be processing using
// a crossings table no larger than DEFAULT_CROSSINGS_SIZE.
// However, we must process at least one row, so we grow the table
// temporarily if needed. This would require an object with a
// huge number of flips.
int rows = DEFAULT_CROSSINGS_SIZE/(flips*SUBPIXEL_POSITIONS_Y);
rows = Math.min(rows, yextent);
rows = Math.max(rows, 1);
for (int i = iminY; i <= imaxY; i += rows*SUBPIXEL_POSITIONS_Y) {
// Compute index of last scanline to be processed in this pass
int last = Math.min(i + rows*SUBPIXEL_POSITIONS_Y - 1, imaxY);
setCrossingsExtents(i, last, flips);
int bminY = i << YSHIFT;
int bmaxY = (last << YSHIFT) | ~YMASK;
// Process edges from the edge list
int maxIdx = edgeIdx;
for (int index = 0; index < maxIdx; index += 5) {
// Test y1 < min:
//
// If edge lies entirely above current strip,
// discard it
if (edges[index + 3] < bminY) {
// Overwrite the edge with the last edge
edgeIdx -= 5;
int fidx = edgeIdx;
int tidx = index;
edges[tidx++] = edges[fidx++];
edges[tidx++] = edges[fidx++];
edges[tidx++] = edges[fidx++];
edges[tidx++] = edges[fidx++];
edges[tidx ] = edges[fidx ];
maxIdx -= 5;
index -= 5;
continue;
}
// Test y0 > max:
//
// If edge lies entirely below current strip,
// skip it for now
if (edges[index + 1] > bmaxY) {
continue;
}
computeCrossingsForEdge(index, bminY, bmaxY);
}
computeBounds();
if (rasterMaxX < rasterMinX) {
continue;
}
bboxX0 = Math.min(bboxX0,
rasterMinX >> SUBPIXEL_LG_POSITIONS_X);
bboxX1 = Math.max(bboxX1,
(rasterMaxX + SUBPIXEL_POSITIONS_X - 1)
>> SUBPIXEL_LG_POSITIONS_X);
renderStrip();
}
// Free up any unusually large scratchpad memory used by the
// preceding primitive
crossingListFinished();
}
public void endRendering() {
// Set up the cache to accumulate the bounding box
if (cache != null) {
cache.bboxX0 = Integer.MAX_VALUE;
cache.bboxY0 = Integer.MAX_VALUE;
cache.bboxX1 = Integer.MIN_VALUE;
cache.bboxY1 = Integer.MIN_VALUE;
}
_endRendering();
}
public void getBoundingBox(int[] bbox) {
bbox[0] = bboxX0;
bbox[1] = bboxY0;
bbox[2] = bboxX1 - bboxX0;
bbox[3] = bboxY1 - bboxY0;
}
private void renderStrip() {
// Grow rowAA according to the raster width
int width = (rasterMaxX - rasterMinX + 1) >> SUBPIXEL_LG_POSITIONS_X;
alphaWidth = width;
// Allocate one extra entry in rowAA to avoid a conditional in
// the rendering loop
int bufLen = width + 1;
if (this.rowAA == null || this.rowAA.length < bufLen) {
this.rowAA = new byte[bufLen];
}
// 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;
int y = 0;
int prevY = rasterMinY - 1;
int minX = Integer.MAX_VALUE;
int maxX = Integer.MIN_VALUE;
iterateCrossings();
while (hasMoreCrossingRows()) {
y = crossingY;
// Emit any skipped rows
for (int j = prevY + 1; j < y; j++) {
if (((j & SUBPIXEL_MASK_Y) == SUBPIXEL_MASK_Y) ||
(j == rasterMaxY)) {
emitRow(j >> SUBPIXEL_LG_POSITIONS_Y, 0, -1);
}
}
prevY = y;
if (crossingRowIndex < crossingRowCount) {
int lx = crossings[crossingRowOffset + crossingRowIndex];
lx >>= 1;
int hx = crossings[crossingRowOffset + crossingRowCount - 1];
hx >>= 1;
int x0 = lx > rasterMinX ? lx : rasterMinX;
int x1 = hx < rasterMaxX ? hx : rasterMaxX;
x0 -= rasterMinX;
x1 -= rasterMinX;
minX = Math.min(minX, x0 >> SUBPIXEL_LG_POSITIONS_X);
maxX = Math.max(maxX, x1 >> SUBPIXEL_LG_POSITIONS_X);
}
int sum = 0;
int prev = rasterMinX;
while (crossingRowIndex < crossingRowCount) {
int crxo = crossings[crossingRowOffset + crossingRowIndex];
crossingRowIndex++;
int crx = crxo >> 1;
int crorientation = ((crxo & 0x1) == 0x1) ? 1 : -1;
if ((sum & mask) != 0) {
// Clip to active X range, if x1 < x0 loop will
// have no effect
int x0 = prev > rasterMinX ? prev : rasterMinX;
int x1 = crx < rasterMaxX ? crx : rasterMaxX;
// Empty spans
if (x1 > x0) {
x0 -= rasterMinX;
x1 -= rasterMinX;
// Accumulate alpha, equivalent to:
// for (int x = x0; x < x1; x++) {
// ++rowAA[x >> SUBPIXEL_LG_POSITIONS_X];
// }
//
// In the middle of the span, we can update a full
// pixel at a time (i.e., SUBPIXEL_POSITIONS_X
// subpixels)
int x = x0 >> SUBPIXEL_LG_POSITIONS_X;
int xmaxm1 = (x1 - 1) >> SUBPIXEL_LG_POSITIONS_X;
if (x == xmaxm1) {
// Start and end in same pixel
rowAA[x] += x1 - x0;
} else {
// Start and end in different pixels
rowAA[x++] += SUBPIXEL_POSITIONS_X -
(x0 & SUBPIXEL_MASK_X);
int xmax = x1 >> SUBPIXEL_LG_POSITIONS_X;
while (x < xmax) {
rowAA[x++] += SUBPIXEL_POSITIONS_X;
}
// Note - at this point it is possible that
// x == width, which implies that
// x1 & SUBPIXEL_MASK_X == 0. We allocate
// one extra entry in rowAA so this
// assignment will be harmless. The alternative
// is an extra conditional here, or some other
// scheme to deal with the last pixel better.
rowAA[x] += x1 & SUBPIXEL_MASK_X;
}
}
}
sum += crorientation;
prev = crx;
}
// Every SUBPIXEL_POSITIONS rows, output an antialiased row
if (((y & SUBPIXEL_MASK_Y) == SUBPIXEL_MASK_Y) ||
(y == rasterMaxY)) {
emitRow(y >> SUBPIXEL_LG_POSITIONS_Y, minX, maxX);
minX = Integer.MAX_VALUE;
maxX = Integer.MIN_VALUE;
}
}
// Emit final row
for (int j = prevY + 1; j <= rasterMaxY; j++) {
if (((j & SUBPIXEL_MASK_Y) == SUBPIXEL_MASK_Y) ||
(j == rasterMaxY)) {
emitRow(j >> SUBPIXEL_LG_POSITIONS_Y, minX, maxX);
minX = Integer.MAX_VALUE;
maxX = Integer.MIN_VALUE;
}
}
}
private void clearAlpha(byte[] alpha,
int width,
int minX, int maxX) {
if (maxX >= minX) {
int w = maxX - minX + 1;
if (w + minX > width) {
w = width - minX;
}
int aidx = minX;
for (int i = 0; i < w; i++, aidx++) {
alpha[aidx] = (byte)0;
}
}
}
private void emitRow(int y, int minX, int maxX) {
// Copy rowAA data into the cache if one is present
if (cache != null) {
if (maxX >= minX) {
int x0 = minX + (rasterMinX >> SUBPIXEL_LG_POSITIONS_X);
int x1 = maxX + (rasterMinX >> SUBPIXEL_LG_POSITIONS_X);
cache.startRow(y, x0, x1);
int srcIdx = minX;
// Perform run-length encoding
// and store results in the cache
byte startVal = rowAA[srcIdx++];
int runLen = 1;
while (srcIdx <= maxX) {
byte nextVal = rowAA[srcIdx++];
if (nextVal == startVal && runLen < 255) {
++runLen;
} else {
cache.addRLERun(startVal, runLen);
runLen = 1;
startVal = nextVal;
}
}
cache.addRLERun(startVal, runLen);
cache.addRLERun((byte)0, 0);
}
}
clearAlpha(rowAA,
alphaWidth,
minX, maxX);
}
public void setCache(PiscesCache cache) {
this.cache = cache;
}
// Edge list data
private int[] edges = new int[5*INITIAL_EDGES];
private int edgeIdx = 0;
private int edgeMinY = Integer.MAX_VALUE;
private int edgeMaxY = Integer.MIN_VALUE;
private void addEdge(int x0, int y0, int x1, int y1) {
int newLen = edgeIdx + 5;
if (edges.length < newLen) {
int[] tmp = new int[Math.max(11*edges.length/10, newLen)];
System.arraycopy(edges, 0, tmp, 0, edgeIdx);
this.edges = tmp;
}
int orientation = 1;
if (y0 > y1) {
int tmp = y0;
y0 = y1;
y1 = tmp;
orientation = -1;
}
// Skip edges that don't cross a subsampled scanline
int eminY = ((y0 + HYSTEP) & YMASK);
int emaxY = ((y1 - HYSTEP) & YMASK);
if (eminY > emaxY) {
return;
}
if (orientation == -1) {
int tmp = x0;
x0 = x1;
x1 = tmp;
}
edges[edgeIdx++] = x0;
edges[edgeIdx++] = y0;
edges[edgeIdx++] = x1;
edges[edgeIdx++] = y1;
edges[edgeIdx++] = orientation;
// Update Y bounds of primitive
if (y0 < edgeMinY) {
edgeMinY = y0;
}
if (y1 > edgeMaxY) {
edgeMaxY = y1;
}
}
private void resetEdges() {
this.edgeIdx = 0;
this.edgeMinY = Integer.MAX_VALUE;
this.edgeMaxY = Integer.MIN_VALUE;
}
// Crossing list data
private int[] crossingIndices;
private int[] crossings;
private int crossingMinY;
private int crossingMaxY;
private int crossingMinX = Integer.MAX_VALUE;
private int crossingMaxX = Integer.MIN_VALUE;
private int crossingMaxXEntries;
private int numCrossings = 0;
private boolean crossingsSorted = false;
private int crossingY;
private int crossingRowCount;
private int crossingRowOffset;
private int crossingRowIndex;
private void setCrossingsExtents(int minY, int maxY, int maxXEntries) {
int yextent = maxY - minY + 1;
// Grow indices array as needed
if (crossingIndices == null || crossingIndices.length < yextent) {
this.crossingIndices =
new int[Math.max(yextent, DEFAULT_INDICES_SIZE)];
}
// Grow crossings array as needed
if (crossings == null || crossings.length < yextent*maxXEntries) {
this.crossings = new int[Math.max(yextent*maxXEntries,
DEFAULT_CROSSINGS_SIZE)];
}
this.crossingMinY = minY;
this.crossingMaxY = maxY;
this.crossingMaxXEntries = maxXEntries;
resetCrossings();
}
private void resetCrossings() {
int yextent = crossingMaxY - crossingMinY + 1;
int start = 0;
for (int i = 0; i < yextent; i++) {
crossingIndices[i] = start;
start += crossingMaxXEntries;
}
crossingMinX = Integer.MAX_VALUE;
crossingMaxX = Integer.MIN_VALUE;
numCrossings = 0;
crossingsSorted = false;
}
// Free sorting arrays if larger than maximum size
private void crossingListFinished() {
if (crossings != null && crossings.length > DEFAULT_CROSSINGS_SIZE) {
crossings = new int[DEFAULT_CROSSINGS_SIZE];
}
if (crossingIndices != null &&
crossingIndices.length > DEFAULT_INDICES_SIZE)
{
crossingIndices = new int[DEFAULT_INDICES_SIZE];
}
}
private void sortCrossings(int[] x, int off, int len) {
for (int i = off + 1; i < off + len; i++) {
int j = i;
int xj = x[j];
int xjm1;
while (j > off && (xjm1 = x[j - 1]) > xj) {
x[j] = xjm1;
x[j - 1] = xj;
j--;
}
}
}
private void sortCrossings() {
int start = 0;
for (int i = 0; i <= crossingMaxY - crossingMinY; i++) {
sortCrossings(crossings, start, crossingIndices[i] - start);
start += crossingMaxXEntries;
}
}
private void addCrossing(int y, int x, int orientation) {
if (x < crossingMinX) {
crossingMinX = x;
}
if (x > crossingMaxX) {
crossingMaxX = x;
}
int index = crossingIndices[y - crossingMinY]++;
x <<= 1;
crossings[index] = (orientation == 1) ? (x | 0x1) : x;
++numCrossings;
}
private void iterateCrossings() {
if (!crossingsSorted) {
sortCrossings();
crossingsSorted = true;
}
crossingY = crossingMinY - 1;
crossingRowOffset = -crossingMaxXEntries;
}
private boolean hasMoreCrossingRows() {
if (++crossingY <= crossingMaxY) {
crossingRowOffset += crossingMaxXEntries;
int y = crossingY - crossingMinY;
crossingRowCount = crossingIndices[y] - y*crossingMaxXEntries;
crossingRowIndex = 0;
return true;
} else {
return false;
}
}
}