blob: 9768e90a77bb3690f9df95e68eea6c4213871422 [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;
import java.util.Arrays;
public class Renderer implements LineSink {
///////////////////////////////////////////////////////////////////////////////
// Scan line iterator and edge crossing data.
//////////////////////////////////////////////////////////////////////////////
private int[] crossings;
// This is an array of indices into the edge array. It is initialized to
// [i * SIZEOF_STRUCT_EDGE for i in range(0, edgesSize/SIZEOF_STRUCT_EDGE)]
// (where range(i, j) is i,i+1,...,j-1 -- just like in python).
// The reason for keeping this is because we need the edges array sorted
// by y0, but we don't want to move all that data around, so instead we
// sort the indices into the edge array, and use edgeIndices to access
// the edges array. This is meant to simulate a pointer array (hence the name)
private int[] edgePtrs;
// 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 minX, maxX;
private int nextY;
// indices into the edge pointer list. They indicate the "active" sublist in
// the edge list (the portion of the list that contains all the edges that
// cross the next scan line).
private int lo, hi;
private static final int INIT_CROSSINGS_SIZE = 50;
private void ScanLineItInitialize() {
crossings = new int[INIT_CROSSINGS_SIZE];
edgePtrs = new int[edgesSize / SIZEOF_STRUCT_EDGE];
for (int i = 0; i < edgePtrs.length; i++) {
edgePtrs[i] = i * SIZEOF_STRUCT_EDGE;
}
qsort(0, edgePtrs.length - 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 (lo = 0; lo < edgePtrs.length && edges[edgePtrs[lo]+Y1] <= nextY; lo++)
;
for (hi = lo; hi < edgePtrs.length && edges[edgePtrs[hi]+CURY] <= nextY; hi++)
; // the active list is *edgePtrs[lo] (inclusive) *edgePtrs[hi] (exclusive)
for (int i = lo; i < hi; i++) {
setCurY(edgePtrs[i], nextY);
}
// We accumulate X in the iterator because accumulating it in addEdge
// like we do with Y does not do much good: if there's an edge
// (0,0)->(1000,10000), and if y gets clipped to 1000, then the x
// bound should be 100, but the accumulator from addEdge would say 1000,
// so we'd still have to accumulate the X bounds as we add crossings.
minX = boundsMinX;
maxX = boundsMaxX;
}
private int ScanLineItCurrentY() {
return nextY - 1;
}
private int ScanLineItGoToNextYAndComputeCrossings() {
// we go through the active list and remove the ones that don't cross
// the nextY scanline.
int crossingIdx = 0;
for (int i = lo; i < hi; i++) {
if (edges[edgePtrs[i]+Y1] <= nextY) {
edgePtrs[i] = edgePtrs[lo++];
}
}
if (hi - lo > crossings.length) {
int newSize = Math.max(hi - lo, crossings.length * 2);
crossings = Arrays.copyOf(crossings, newSize);
}
// Now every edge between lo and hi crosses nextY. Compute it's
// crossing and put it in the crossings array.
for (int i = lo; i < hi; i++) {
addCrossing(nextY, getCurCrossing(edgePtrs[i]), (int)edges[edgePtrs[i]+OR], crossingIdx);
gotoNextY(edgePtrs[i]);
crossingIdx++;
}
nextY++;
// Expand active list to include new edges.
for (; hi < edgePtrs.length && edges[edgePtrs[hi]+CURY] <= nextY; hi++) {
setCurY(edgePtrs[hi], nextY);
}
Arrays.sort(crossings, 0, crossingIdx);
return crossingIdx;
}
private boolean ScanLineItHasNext() {
return nextY < maxY;
}
private void addCrossing(int y, int x, int or, int idx) {
if (x < minX) {
minX = x;
}
if (x > maxX) {
maxX = x;
}
x <<= 1;
crossings[idx] = ((or == 1) ? (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 void qsort(int first, int last) {
if (last > first) {
int p = partition(first, last);
if (first < p - 1) {
qsort(first, p - 1);
}
if (p < last) {
qsort(p, last);
}
}
}
// i, j are indices into edgePtrs.
private int partition(int i, int j) {
int pivotVal = edgePtrs[i];
while (i <= j) {
// edges[edgePtrs[i]+1] is equivalent to (*(edgePtrs[i])).y0 in C
while (edges[edgePtrs[i]+CURY] < edges[pivotVal+CURY]) { i++; }
while (edges[edgePtrs[j]+CURY] > edges[pivotVal+CURY]) { j--; }
if (i <= j) {
int tmp = edgePtrs[i];
edgePtrs[i] = edgePtrs[j];
edgePtrs[j] = tmp;
i++;
j--;
}
}
return i;
}
//============================================================================
//////////////////////////////////////////////////////////////////////////////
// EDGE LIST
//////////////////////////////////////////////////////////////////////////////
private static final int INIT_NUM_EDGES = 1000;
private static final int SIZEOF_STRUCT_EDGE = 5;
// The following array is a poor man's struct array:
// it simulates a struct array by having
// edges[SIZEOF_STRUCT_EDGE * i + j] be the jth field in the ith element
// of an array of edge structs.
private float[] edges;
private int edgesSize; // size of the edge list.
private static final int Y1 = 0;
private static final int SLOPE = 1;
private static final int OR = 2; // the orientation. This can be -1 or 1.
// -1 means up, 1 means down.
private static final int CURY = 3; // j = 5 corresponds to the "current Y".
// Each edge keeps track of the last scanline
// crossing it computed, and this is the y coord of
// that scanline.
private static final int CURX = 4; //the x coord of the current crossing.
// Note that while the array is declared as a float[] not all of it's
// elements should be floats. currentY and Orientation should be ints (or int and
// byte respectively), but they all need to be the same type. This isn't
// really a problem because floats can represent exactly all 23 bit integers,
// which should be more than enough.
// Note, also, that we only need x1 for slope computation, so we don't need
// to store it. x0, y0 don't need to be stored either. They can be put into
// curx, cury, and it's ok if they're lost when curx and cury are changed.
// We take this undeniably ugly and error prone approach (instead of simply
// making an Edge class) for performance reasons. Also, it would probably be nicer
// to have one array for each field, but that would defeat the purpose because
// it would make poor use of the processor cache, since we tend to access
// all the fields for one edge at a time.
private float edgeMinY;
private float edgeMaxY;
private void addEdge(float x0, float y0, float x1, float y1) {
float or = (y0 < y1) ? 1f : -1f; // orientation: 1 = UP; -1 = DOWN
if (or == -1) {
float tmp = y0;
y0 = y1;
y1 = tmp;
tmp = x0;
x0 = x1;
x1 = tmp;
}
// skip edges that don't cross a scanline
if (Math.ceil(y0) >= Math.ceil(y1)) {
return;
}
int newSize = edgesSize + SIZEOF_STRUCT_EDGE;
if (edges.length < newSize) {
edges = Arrays.copyOf(edges, newSize * 2);
}
edges[edgesSize+CURX] = x0;
edges[edgesSize+CURY] = y0;
edges[edgesSize+Y1] = y1;
edges[edgesSize+SLOPE] = (x1 - x0) / (y1 - y0);
edges[edgesSize+OR] = or;
// the crossing values can't be initialized meaningfully yet. This
// will have to wait until setCurY is called
edgesSize += SIZEOF_STRUCT_EDGE;
// Accumulate edgeMinY and edgeMaxY
if (y0 < edgeMinY) { edgeMinY = y0; }
if (y1 > edgeMaxY) { edgeMaxY = y1; }
}
// As far as the following methods care, this edges extends to infinity.
// They can compute the x intersect of any horizontal line.
// precondition: idx is the index to the start of the desired edge.
// So, if the ith edge is wanted, idx should be SIZEOF_STRUCT_EDGE * i
private void setCurY(int idx, int y) {
// compute the x crossing of edge at idx and horizontal line y
// currentXCrossing = (y - y0)*slope + x0
edges[idx + CURX] = (y - edges[idx + CURY]) * edges[idx + SLOPE] + edges[idx+CURX];
edges[idx + CURY] = (float)y;
}
private void gotoNextY(int idx) {
edges[idx + CURY] += 1f; // i.e. curY += 1
edges[idx + CURX] += edges[idx + SLOPE]; // i.e. curXCrossing += slope
}
private int getCurCrossing(int idx) {
return (int)edges[idx + CURX];
}
//====================================================================================
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
final PiscesCache cache;
// Bounds of the drawing region, at subpixel precision.
final private int boundsMinX, boundsMinY, boundsMaxX, boundsMaxY;
// Pixel bounding box for current primitive
private int pix_bboxX0, pix_bboxY0, pix_bboxX1, pix_bboxY1;
// Current winding rule
final private 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,
PiscesCache cache) {
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.edges = new float[SIZEOF_STRUCT_EDGE * INIT_NUM_EDGES];
edgeMinY = Float.POSITIVE_INFINITY;
edgeMaxY = Float.NEGATIVE_INFINITY;
edgesSize = 0;
this.windingRule = windingRule;
this.cache = cache;
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;
this.pix_bboxX0 = pix_boundsX;
this.pix_bboxY0 = pix_boundsY;
this.pix_bboxX1 = pix_boundsX + pix_boundsWidth;
this.pix_bboxY1 = pix_boundsY + pix_boundsHeight;
}
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) {
close();
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 */ }
public void lineTo(float pix_x1, float pix_y1) {
float x1 = tosubpixx(pix_x1);
float y1 = tosubpixy(pix_y1);
// Ignore horizontal lines
if (y0 == y1) {
this.x0 = x1;
return;
}
addEdge(x0, y0, x1, y1);
this.x0 = x1;
this.y0 = y1;
}
public void close() {
// lineTo expects its input in pixel coordinates.
lineTo(pix_sx0, pix_sy0);
}
public void end() {
close();
}
private void _endRendering() {
// 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 = ((boundsMaxX - boundsMinX) >> SUBPIXEL_LG_POSITIONS_X) + 1;
byte[] alpha = new byte[width+1];
// 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.
ScanLineItInitialize();
for ( ; ScanLineItHasNext(); ) {
int numCrossings = ScanLineItGoToNextYAndComputeCrossings();
y = ScanLineItCurrentY();
if (numCrossings > 0) {
int lowx = crossings[0] >> 1;
int highx = crossings[numCrossings - 1] >> 1;
int x0 = Math.max(lowx, boundsMinX);
int x1 = Math.min(highx, boundsMaxX);
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 = boundsMinX;
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, boundsMinX);
int x1 = Math.min(curx, boundsMaxX);
if (x0 < x1) {
x0 -= boundsMinX; // turn x0, x1 from coords to indeces
x1 -= boundsMinX; // 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;
}
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);
}
pix_bboxX0 = minX >> SUBPIXEL_LG_POSITIONS_X;
pix_bboxX1 = maxX >> SUBPIXEL_LG_POSITIONS_X;
pix_bboxY0 = minY >> SUBPIXEL_LG_POSITIONS_Y;
pix_bboxY1 = maxY >> SUBPIXEL_LG_POSITIONS_Y;
}
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[] pix_bbox) {
pix_bbox[0] = pix_bboxX0;
pix_bbox[1] = pix_bboxY0;
pix_bbox[2] = pix_bboxX1 - pix_bboxX0;
pix_bbox[3] = pix_bboxY1 - pix_bboxY0;
}
private void emitRow(byte[] 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, pix_to);
// Perform run-length encoding and store results in the cache
int from = pix_from - (boundsMinX >> SUBPIXEL_LG_POSITIONS_X);
int to = pix_to - (boundsMinX >> SUBPIXEL_LG_POSITIONS_X);
int runLen = 1;
byte startVal = alphaRow[from];
for (int i = from + 1; i <= to; i++) {
byte nextVal = (byte)(startVal + alphaRow[i]);
if (nextVal == startVal && runLen < 255) {
runLen++;
} else {
cache.addRLERun(startVal, runLen);
runLen = 1;
startVal = nextVal;
}
}
cache.addRLERun(startVal, runLen);
cache.addRLERun((byte)0, 0);
}
}
java.util.Arrays.fill(alphaRow, (byte)0);
}
}