| /* |
| * 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; |
| } |
| |
| } |