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;
public static final int INITIAL_EDGES = 1000;
public static final int DEFAULT_INDICES_SIZE = 8192;
public static final int DEFAULT_CROSSINGS_SIZE = 32*1024;
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;
PiscesCache cache;
private int boundsMinX, boundsMinY, boundsMaxX, boundsMaxY;
private int rasterMinX, rasterMaxX, rasterMinY, rasterMaxY;
private int bboxX0, bboxY0, bboxX1, bboxY1;
private int windingRule;
private int x0, y0;
private int sx0, sy0;
private byte[] rowAA;
private int firstOrientation;
private int lastOrientation;
private int flips;
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) {
close();
this.sx0 = this.x0 = x0;
this.sy0 = this.y0 = y0;
this.lastOrientation = 0;
}
public void lineJoin() {
}
public void lineTo(int x1, int y1) {
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;
addEdge(x0, y0 | 0x1, x1, y1 | 0x1);
this.x0 = x1;
this.y0 = y1;
}
public void close() {
int orientation = lastOrientation;
if (y0 != sy0) {
orientation = (y0 < sy0) ? 1 : -1;
}
if (orientation != firstOrientation) {
++flips;
}
lineTo(sx0, sy0);
}
public void end() {
close();
}
private void computeCrossingsForEdge(int index,
int boundsMinY, int boundsMaxY) {
int iy0 = edges[index + 1];
int iy1 = edges[index + 3];
int clipy0 = (iy0 > boundsMinY) ? iy0 : boundsMinY;
int clipy1 = (iy1 < boundsMaxY) ? iy1 : boundsMaxY;
int minY = ((clipy0 + HYSTEP) & YMASK) + HYSTEP;
int maxY = ((clipy1 - HYSTEP) & YMASK) + HYSTEP;
if (minY > maxY) {
return;
}
int ix0 = edges[index];
int ix1 = edges[index + 2];
long dx = ((long) ix1) - ix0;
long dy = ((long) iy1) - iy0;
int orientation = edges[index + 4];
int y = minY;
long lx = (((long) y) - iy0)*dx/dy + ix0;
addCrossing(y >> YSHIFT, (int)(lx >> XSHIFT), orientation);
y += YSTEP;
if (y > maxY) {
return;
}
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 (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;
}
if (false &&
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;
x0 = clamp(x0, bMinX, bMaxX);
x1 = clamp(x1, bMinX, bMaxX);
y0 = clamp(y0, bMinY, bMaxY);
y1 = clamp(y1, bMinY, bMaxY);
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;
if (minY > maxY) {
bboxX0 = bboxY0 = 0;
bboxX1 = bboxY1 = -1;
return;
}
int iminY = (minY >> YSHIFT) & ~SUBPIXEL_MASK_Y;
int imaxY = (maxY >> YSHIFT) | SUBPIXEL_MASK_Y;
int yextent = (imaxY - iminY) + 1;
int size = flips*yextent;
int bmax = (boundsMaxY >> YSHIFT) - 1;
if (imaxY > bmax) {
imaxY = bmax;
}
bboxX0 = Integer.MAX_VALUE;
bboxX1 = Integer.MIN_VALUE;
bboxY0 = iminY >> SUBPIXEL_LG_POSITIONS_Y;
bboxY1 = (imaxY + SUBPIXEL_POSITIONS_Y - 1) >> SUBPIXEL_LG_POSITIONS_Y;
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) {
int last = Math.min(i + rows*SUBPIXEL_POSITIONS_Y - 1, imaxY);
setCrossingsExtents(i, last, flips);
int bminY = i << YSHIFT;
int bmaxY = (last << YSHIFT) | ~YMASK;
int maxIdx = edgeIdx;
for (int index = 0; index < maxIdx; index += 5) {
if (edges[index + 3] < bminY) {
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;
}
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();
}
crossingListFinished();
}
public void endRendering() {
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() {
int width = (rasterMaxX - rasterMinX + 1) >> SUBPIXEL_LG_POSITIONS_X;
alphaWidth = width;
int bufLen = width + 1;
if (this.rowAA == null || this.rowAA.length < bufLen) {
this.rowAA = new byte[bufLen];
}
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;
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) {
int x0 = prev > rasterMinX ? prev : rasterMinX;
int x1 = crx < rasterMaxX ? crx : rasterMaxX;
if (x1 > x0) {
x0 -= rasterMinX;
x1 -= rasterMinX;
int x = x0 >> SUBPIXEL_LG_POSITIONS_X;
int xmaxm1 = (x1 - 1) >> SUBPIXEL_LG_POSITIONS_X;
if (x == xmaxm1) {
rowAA[x] += x1 - x0;
} else {
rowAA[x++] += SUBPIXEL_POSITIONS_X -
(x0 & SUBPIXEL_MASK_X);
int xmax = x1 >> SUBPIXEL_LG_POSITIONS_X;
while (x < xmax) {
rowAA[x++] += SUBPIXEL_POSITIONS_X;
}
rowAA[x] += x1 & SUBPIXEL_MASK_X;
}
}
}
sum += crorientation;
prev = crx;
}
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;
}
}
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) {
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;
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;
}
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;
}
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;
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;
}
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;
if (crossingIndices == null || crossingIndices.length < yextent) {
this.crossingIndices =
new int[Math.max(yextent, DEFAULT_INDICES_SIZE)];
}
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;
}
private void crossingListFinished() {
if (crossings.length > DEFAULT_CROSSINGS_SIZE) {
crossings = new int[DEFAULT_CROSSINGS_SIZE];
}
if (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;
}
}
}