blob: dbaaf539a547768ea35f2948ed1930f7f2ec7652 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @author Denis M. Kishenko
* @version $Revision$
*/
package org.apache.harmony.awt.gl.render;
import java.awt.Shape;
import java.awt.geom.PathIterator;
import org.apache.harmony.awt.gl.MultiRectArea;
import org.apache.harmony.awt.internal.nls.Messages;
public class JavaShapeRasterizer {
static final int POINT_CAPACITY = 16;
int edgesCount;
int edgeCur;
int[] edgesX;
int[] edgesY;
int[] edgesYS; // Y coordinate of edge START point
int[] edgesN;
int[] edgesDY;
int[] bounds;
int boundCount;
boolean[] edgesExt; // Extremal points
int activeCount;
float[] activeX;
int[] activeYEnd;
float[] activeXStep;
int[] activeDY;
boolean[] activeExt;
int[] crossX;
int[] crossDY;
Filler filler;
/**
* Rasterization filler for different path rules
*/
static abstract class Filler {
static class NonZero extends Filler {
@Override
void add(MultiRectArea.LineCash rect, int[] points, int[] orient, int length, int y) {
int[] dst = new int[length];
int dstLength = 1;
dst[0] = points[0];
int count = 0;
boolean inside = true;
for(int i = 0; i < length; i++) {
count += orient[i] > 0 ? 1 : -1;
if (count == 0) {
dst[dstLength++] = points[i];
inside = false;
} else {
if (!inside) {
dst[dstLength++] = points[i];
inside = true;
}
}
}
for(int i = 1; i < dstLength; i += 2) {
dst[i]--;
}
dstLength = excludeEmpty(dst, dstLength);
// System.out.println("test");
dstLength = union(dst, dstLength);
rect.addLine(dst, dstLength);
}
}
static class EvenOdd extends Filler {
@Override
void add(MultiRectArea.LineCash rect, int[] points, int[] orient, int length, int y) {
/*
int[] buf = new int[length];
int j = 0;
for(int i = 0; i < length - 1; i++) {
if (points[i] != points[i + 1]) {
buf[j++] = points[i];
}
}
*/
for(int i = 1; i < length; i += 2) {
points[i]--;
}
length = excludeEmpty(points, length);
// System.out.println("test");
length = union(points, length);
rect.addLine(points, length);
/*
for(int i = 0; i < length;) {
rect.add(points[i++], y, points[i++], y);
}
*/
}
}
abstract void add(MultiRectArea.LineCash rect, int[] points, int[] orient, int length, int y);
static int excludeEmpty(int[] points, int length) {
int i = 0;
while(i < length) {
if (points[i] <= points[i + 1]) {
i += 2;
} else {
length -= 2;
System.arraycopy(points, i + 2, points, i, length - i);
}
}
return length;
}
static int union(int[] points, int length) {
int i = 1;
while(i < length - 1) {
if (points[i] < points[i - 1]) {
System.arraycopy(points, i + 1, points, i - 1, length - i - 1);
length -= 2;
} else
if (points[i] >= points[i + 1] - 1) {
System.arraycopy(points, i + 2, points, i, length - i - 2);
length -= 2;
} else {
i += 2;
}
}
return length;
}
}
public JavaShapeRasterizer() {
}
/**
* Checks buffer size and realloc if necessary
*/
int[] checkBufSize(int[] buf, int size) {
if (size == buf.length) {
int[] tmp;
tmp = new int[size + POINT_CAPACITY];
System.arraycopy(buf, 0, tmp, 0, buf.length);
buf = tmp;
}
return buf;
}
/**
* Adds to the buffers new edge
*/
void addEdge(int x, int y, int num) {
edgesX = checkBufSize(edgesX, edgesCount);
edgesY = checkBufSize(edgesY, edgesCount);
edgesN = checkBufSize(edgesN, edgesCount);
edgesX[edgesCount] = x;
edgesY[edgesCount] = y;
edgesN[edgesCount] = (num << 16) | edgesCount;
edgesCount++;
}
/**
* Prepare all buffers and variable to rasterize shape
*/
void makeBuffer(PathIterator path, double flatness) {
edgesX = new int[POINT_CAPACITY];
edgesY = new int[POINT_CAPACITY];
edgesN = new int[POINT_CAPACITY];
bounds = new int[POINT_CAPACITY];
boundCount = 0;
edgesCount = 0;
if (path.getWindingRule() == PathIterator.WIND_EVEN_ODD) {
filler = new Filler.EvenOdd();
} else {
filler = new Filler.NonZero();
}
float[] coords = new float[2];
boolean closed = true;
while (!path.isDone()) {
switch(path.currentSegment(coords)) {
case PathIterator.SEG_MOVETO:
if (!closed) {
boundCount++;
bounds = checkBufSize(bounds, boundCount);
bounds[boundCount] = edgesCount;
}
addEdge((int)coords[0], (int)coords[1], boundCount);
closed = false;
break;
case PathIterator.SEG_LINETO:
addEdge((int)coords[0], (int)coords[1], boundCount);
break;
case PathIterator.SEG_CLOSE:
boundCount++;
bounds = checkBufSize(bounds, boundCount);
bounds[boundCount] = edgesCount;
closed = true;
break;
default:
// awt.36=Wrong segment
throw new RuntimeException(Messages.getString("awt.36")); //$NON-NLS-1$
}
path.next();
}
if (!closed) {
boundCount++;
bounds = checkBufSize(bounds, boundCount);
bounds[boundCount] = edgesCount;
}
}
/**
* Sort buffers
*/
void sort(int[] master, int[] slave, int length) {
for(int i = 0; i < length - 1; i++) {
int num = i;
int min = master[num];
for(int j = i + 1; j < length; j++) {
if (master[j] < min) {
num = j;
min = master[num];
}
}
if (num != i) {
master[num] = master[i];
master[i] = min;
min = slave[num];
slave[num] = slave[i];
slave[i] = min;
}
}
}
int getNext(int cur) {
int n = edgesN[cur];
int bound = n >> 16;
int num = (n & 0xFFFF) + 1;
if (num == bounds[bound + 1]) {
return bounds[bound];
}
return num;
}
int getPrev(int cur) {
int n = edgesN[cur];
int bound = n >> 16;
int num = (n & 0xFFFF) - 1;
if (num < bounds[bound]) {
return bounds[bound + 1] - 1;
}
return num;
}
int getNextShape(int cur) {
int bound = edgesN[cur] >> 16;
return bounds[bound + 1];
}
void init() {
edgesYS = new int[edgesCount];
System.arraycopy(edgesY, 0, edgesYS, 0, edgesCount);
// Create edgesDY
edgesDY = new int[edgesCount];
for(int i = 0; i < edgesCount; i++) {
int dy = edgesY[getNext(i)] - edgesY[i];
edgesDY[i] = dy;
}
// Create edgesExt
edgesExt = new boolean[edgesCount];
int prev = -1;
int i = 0;
int pos = 0;
while(i < edgesCount) {
TOP: {
do {
if (edgesDY[i] > 0) {
break TOP;
}
i = getNext(i);
} while (i != pos);
i = pos = getNextShape(i);
continue;
}
BOTTOM: {
do {
if (edgesDY[i] < 0) {
break BOTTOM;
}
if (edgesDY[i] > 0) {
prev = i;
}
i = getNext(i);
} while (i != pos);
i = pos = getNextShape(i);
continue;
}
if (prev != -1) {
edgesExt[prev] = true;
}
edgesExt[i] = true;
}
// Sort edgesY and edgesN
sort(edgesYS, edgesN, edgesCount);
edgeCur = 0;
activeCount = 0;
activeX = new float[edgesCount];
activeYEnd = new int[edgesCount];
activeXStep = new float[edgesCount];
activeDY = new int[edgesCount];
activeExt = new boolean[edgesCount];
crossX = new int[edgesCount];
crossDY = new int[edgesCount];
}
/**
* Marks edge as active
*/
void addActiveEdge(int levelY, int start, int end, boolean back) {
int dy = back ? -edgesDY[end] : edgesDY[start];
if (dy <= 0) {
return;
}
int x1 = edgesX[start];
int dx = edgesX[end] - x1;
activeX[activeCount] = x1;
activeYEnd[activeCount] = edgesY[end];
activeXStep[activeCount] = dx / (float)dy;
activeDY[activeCount] = back ? -dy : dy;
activeExt[activeCount] = back ? edgesExt[end] : edgesExt[start];
activeCount++;
}
/**
* Find new active edges
*/
int findActiveEdges(int levelY) {
int edgeActive = edgeCur;
while (edgeActive < edgesCount && edgesYS[edgeActive] == levelY) {
edgeActive++;
}
int activeNext = edgeActive;
while (edgeActive > edgeCur) {
edgeActive--;
int num = edgesN[edgeActive] & 0xFFFF;
addActiveEdge(levelY, num, getPrev(edgeActive), true);
addActiveEdge(levelY, num, getNext(edgeActive), false);
}
edgeCur = activeNext;
if (activeNext == edgesCount) {
return edgesY[edgesCount - 1];
}
return edgesYS[activeNext];
}
/**
* Rasterizes shape with particular flatness
* @param shape - the souze Shape to be rasterized
* @param flatness - the rasterization flatness
* @return a MultiRectArea of rasterized shape
*/
public MultiRectArea rasterize(Shape shape, double flatness) {
PathIterator path = shape.getPathIterator(null, flatness);
// Shape is empty
if (path.isDone()) {
return new MultiRectArea();
}
makeBuffer(path, flatness);
init();
int y = edgesYS[0];
int nextY = y;
int crossCount;
MultiRectArea.LineCash rect = new MultiRectArea.LineCash(edgesCount);
rect.setLine(y);
while(y <= nextY) {
crossCount = 0;
if (y == nextY) {
int i = activeCount;
while(i > 0) {
i--;
if (activeYEnd[i] == y) {
activeCount--;
int length = activeCount - i;
if (length != 0) {
int pos = i + 1;
System.arraycopy(activeX, pos, activeX, i, length);
System.arraycopy(activeYEnd, pos, activeYEnd, i, length);
System.arraycopy(activeXStep, pos, activeXStep, i, length);
System.arraycopy(activeDY, pos, activeDY, i, length);
System.arraycopy(activeExt, pos, activeExt, i, length);
}
}
}
nextY = findActiveEdges(y);
}
// Get X crossings
for(int i = 0; i < activeCount; i++) {
crossX[crossCount] = (int)Math.ceil(activeX[i]);
crossDY[crossCount] = activeDY[i];
crossCount++;
}
if (crossCount == 0) {
rect.skipLine();
} else {
// Sort X crossings
sort(crossX, crossDY, crossCount);
filler.add(rect, crossX, crossDY, crossCount, y);
}
for(int i = 0; i < activeCount; i++) {
activeX[i] += activeXStep[i];
}
y++;
}
return rect;
}
}