blob: c4267f3a90b2ce268c58e358a5c2ac0764fd97d0 [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;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.NoSuchElementException;
import org.apache.harmony.awt.internal.nls.Messages;
public class MultiRectArea implements Shape {
/**
* If CHECK is true validation check active
*/
private static final boolean CHECK = false;
boolean sorted = true;
/**
* Rectangle buffer
*/
public int[] rect;
/**
* Bounding box
*/
Rectangle bounds;
/**
* Result rectangle array
*/
Rectangle[] rectangles;
/**
* LineCash provides creating MultiRectArea line by line. Used in JavaShapeRasterizer.
*/
public static class LineCash extends MultiRectArea {
int lineY;
int bottomCount;
int[] bottom;
public LineCash(int size) {
super();
bottom = new int[size];
bottomCount = 0;
}
public void setLine(int y) {
lineY = y;
}
public void skipLine() {
lineY++;
bottomCount = 0;
}
public void addLine(int[] points, int pointCount) {
int bottomIndex = 0;
int pointIndex = 0;
int rectIndex = 0;
int pointX1 = 0;
int pointX2 = 0;
int bottomX1 = 0;
int bottomX2 = 0;
boolean appendRect = false;
boolean deleteRect = false;
int lastCount = bottomCount;
while (bottomIndex < lastCount || pointIndex < pointCount) {
appendRect = false;
deleteRect = false;
if (bottomIndex < lastCount) {
rectIndex = bottom[bottomIndex];
bottomX1 = rect[rectIndex];
bottomX2 = rect[rectIndex + 2];
} else {
appendRect = true;
}
if (pointIndex < pointCount) {
pointX1 = points[pointIndex];
pointX2 = points[pointIndex + 1];
} else {
deleteRect = true;
}
if (!deleteRect && !appendRect) {
if (pointX1 == bottomX1 && pointX2 == bottomX2) {
rect[rectIndex + 3] = rect[rectIndex + 3] + 1;
pointIndex += 2;
bottomIndex++;
continue;
}
deleteRect = pointX2 >= bottomX1;
appendRect = pointX1 <= bottomX2;
}
if (deleteRect) {
if (bottomIndex < bottomCount - 1) {
System.arraycopy(bottom, bottomIndex + 1, bottom, bottomIndex, bottomCount - bottomIndex - 1);
rectIndex -= 4;
}
bottomCount--;
lastCount--;
}
if (appendRect) {
int i = rect[0];
bottom[bottomCount++] = i;
rect = MultiRectAreaOp.checkBufSize(rect, 4);
rect[i++] = pointX1;
rect[i++] = lineY;
rect[i++] = pointX2;
rect[i++] = lineY;
pointIndex += 2;
}
}
lineY++;
invalidate();
}
}
/**
* RectCash provides simple creating MultiRectArea
*/
public static class RectCash extends MultiRectArea {
int[] cash;
public RectCash() {
super();
cash = new int[MultiRectAreaOp.RECT_CAPACITY];
cash[0] = 1;
}
public void addRectCashed(int x1, int y1, int x2, int y2) {
addRect(x1, y1, x2, y2);
invalidate();
/*
// Exclude from cash unnecessary rectangles
int i = 1;
while(i < cash[0]) {
if (rect[cash[i] + 3] >= y1 - 1) {
if (i > 1) {
System.arraycopy(cash, i, cash, 1, cash[0] - i);
}
break;
}
i++;
}
cash[0] -= i - 1;
// Find in cash rectangle to concatinate
i = 1;
while(i < cash[0]) {
int index = cash[i];
if (rect[index + 3] != y1 - 1) {
break;
}
if (rect[index] == x1 && rect[index + 2] == x2) {
rect[index + 3] += y2 - y1 + 1;
int pos = i + 1;
while(pos < cash[0]) {
if (rect[index + 3] <= rect[cash[i] + 3]) {
System.arraycopy(cash, i + 1, cash, i, pos - i);
break;
}
i++;
}
cash[pos - 1] = index;
invalidate();
return;
}
i++;
}
// Add rectangle to buffer
int index = rect[0];
rect = MultiRectAreaOp.checkBufSize(rect, 4);
rect[index + 0] = x1;
rect[index + 1] = y1;
rect[index + 2] = x2;
rect[index + 3] = y2;
// Add rectangle to cash
int length = cash[0];
cash = MultiRectAreaOp.checkBufSize(cash, 1);
while(i < length) {
if (y2 <= rect[cash[i] + 3]) {
System.arraycopy(cash, i, cash, i + 1, length - i);
break;
}
i++;
}
cash[i] = index;
invalidate();
*/
}
public void addRectCashed(int[] rect, int rectOff, int rectLength) {
for(int i = rectOff; i < rectOff + rectLength;) {
addRect(rect[i++], rect[i++], rect[i++], rect[i++]);
// addRectCashed(rect[i++], rect[i++], rect[i++], rect[i++]);
}
}
}
/**
* MultiRectArea path iterator
*/
class Iterator implements PathIterator {
int type;
int index;
int pos;
int[] rect;
AffineTransform t;
Iterator(MultiRectArea mra, AffineTransform t) {
rect = new int[mra.rect[0] - 1];
System.arraycopy(mra.rect, 1, rect, 0, rect.length);
this.t = t;
}
public int getWindingRule() {
return WIND_NON_ZERO;
}
public boolean isDone() {
return pos >= rect.length;
}
public void next() {
if (index == 4) {
pos += 4;
}
index = (index + 1) % 5;
}
public int currentSegment(double[] coords) {
if (isDone()) {
// awt.4B=Iiterator out of bounds
throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$
}
int type = 0;
switch(index) {
case 0 :
type = SEG_MOVETO;
coords[0] = rect[pos + 0];
coords[1] = rect[pos + 1];
break;
case 1:
type = SEG_LINETO;
coords[0] = rect[pos + 2];
coords[1] = rect[pos + 1];
break;
case 2:
type = SEG_LINETO;
coords[0] = rect[pos + 2];
coords[1] = rect[pos + 3];
break;
case 3:
type = SEG_LINETO;
coords[0] = rect[pos + 0];
coords[1] = rect[pos + 3];
break;
case 4:
type = SEG_CLOSE;
break;
}
if (t != null) {
t.transform(coords, 0, coords, 0, 1);
}
return type;
}
public int currentSegment(float[] coords) {
if (isDone()) {
// awt.4B=Iiterator out of bounds
throw new NoSuchElementException(Messages.getString("awt.4B")); //$NON-NLS-1$
}
int type = 0;
switch(index) {
case 0 :
type = SEG_MOVETO;
coords[0] = rect[pos + 0];
coords[1] = rect[pos + 1];
break;
case 1:
type = SEG_LINETO;
coords[0] = rect[pos + 2];
coords[1] = rect[pos + 1];
break;
case 2:
type = SEG_LINETO;
coords[0] = rect[pos + 2];
coords[1] = rect[pos + 3];
break;
case 3:
type = SEG_LINETO;
coords[0] = rect[pos + 0];
coords[1] = rect[pos + 3];
break;
case 4:
type = SEG_CLOSE;
break;
}
if (t != null) {
t.transform(coords, 0, coords, 0, 1);
}
return type;
}
}
/**
* Constructs a new empty MultiRectArea
*/
public MultiRectArea() {
rect = MultiRectAreaOp.createBuf(0);
}
public MultiRectArea(boolean sorted) {
this();
this.sorted = sorted;
}
/**
* Constructs a new MultiRectArea as a copy of another one
*/
public MultiRectArea(MultiRectArea mra) {
if (mra == null) {
rect = MultiRectAreaOp.createBuf(0);
} else {
rect = new int[mra.rect.length];
System.arraycopy(mra.rect, 0, rect, 0, mra.rect.length);
check(this, "MultiRectArea(MRA)"); //$NON-NLS-1$
}
}
/**
* Constructs a new MultiRectArea consists of single rectangle
*/
public MultiRectArea(Rectangle r) {
rect = MultiRectAreaOp.createBuf(0);
if (r != null && !r.isEmpty()) {
rect[0] = 5;
rect[1] = r.x;
rect[2] = r.y;
rect[3] = r.x + r.width - 1;
rect[4] = r.y + r.height - 1;
}
check(this, "MultiRectArea(Rectangle)"); //$NON-NLS-1$
}
/**
* Constructs a new MultiRectArea consists of single rectangle
*/
public MultiRectArea(int x0, int y0, int x1, int y1) {
rect = MultiRectAreaOp.createBuf(0);
if (x1 >= x0 && y1 >= y0) {
rect[0] = 5;
rect[1] = x0;
rect[2] = y0;
rect[3] = x1;
rect[4] = y1;
}
check(this, "MultiRectArea(Rectangle)"); //$NON-NLS-1$
}
/**
* Constructs a new MultiRectArea and append rectangle from buffer
*/
public MultiRectArea(Rectangle[] buf) {
this();
for (Rectangle element : buf) {
add(element);
}
}
/**
* Constructs a new MultiRectArea and append rectangle from array
*/
public MultiRectArea(ArrayList<Rectangle> buf) {
this();
for(int i = 0; i < buf.size(); i++) {
add(buf.get(i));
}
}
/**
* Sort rectangle buffer
*/
void resort() {
int[] buf = new int[4];
for(int i = 1; i < rect[0]; i += 4) {
int k = i;
int x1 = rect[k];
int y1 = rect[k + 1];
for(int j = i + 4; j < rect[0]; j += 4) {
int x2 = rect[j];
int y2 = rect[j + 1];
if (y1 > y2 || (y1 == y2 && x1 > x2)) {
x1 = x2;
y1 = y2;
k = j;
}
}
if (k != i) {
System.arraycopy(rect, i, buf, 0, 4);
System.arraycopy(rect, k, rect, i, 4);
System.arraycopy(buf, 0, rect, k, 4);
}
}
invalidate();
}
/**
* Tests equals with another object
*/
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof MultiRectArea) {
MultiRectArea mra = (MultiRectArea) obj;
for(int i = 0; i < rect[0]; i++) {
if (rect[i] != mra.rect[i]) {
return false;
}
}
return true;
}
return false;
}
/**
* Checks validation of MultiRectArea object
*/
static MultiRectArea check(MultiRectArea mra, String msg) {
if (CHECK && mra != null) {
if (MultiRectArea.checkValidation(mra.getRectangles(), mra.sorted) != -1) {
// awt.4C=Invalid MultiRectArea in method {0}
new RuntimeException(Messages.getString("awt.4C", msg)); //$NON-NLS-1$
}
}
return mra;
}
/**
* Checks validation of MultiRectArea object
*/
public static int checkValidation(Rectangle[] r, boolean sorted) {
// Check width and height
for(int i = 0; i < r.length; i++) {
if (r[i].width <= 0 || r[i].height <= 0) {
return i;
}
}
// Check order
if (sorted) {
for(int i = 1; i < r.length; i++) {
if (r[i - 1].y > r[i].y) {
return i;
}
if (r[i - 1].y == r[i].y) {
if (r[i - 1].x > r[i].x) {
return i;
}
}
}
}
// Check override
for(int i = 0; i < r.length; i++) {
for(int j = i + 1; j < r.length; j++) {
if (r[i].intersects(r[j])) {
return i;
}
}
}
return -1;
}
/**
* Assigns rectangle from another buffer
*/
protected void setRect(int[] buf, boolean copy) {
if (copy) {
rect = new int[buf.length];
System.arraycopy(buf, 0, rect, 0, buf.length);
} else {
rect = buf;
}
invalidate();
}
/**
* Union with another MultiRectArea object
*/
public void add(MultiRectArea mra) {
setRect(union(this, mra).rect, false);
invalidate();
}
/**
* Intersect with another MultiRectArea object
*/
public void intersect(MultiRectArea mra) {
setRect(intersect(this, mra).rect, false);
invalidate();
}
/**
* Subtract another MultiRectArea object
*/
public void substract(MultiRectArea mra) {
setRect(subtract(this, mra).rect, false);
invalidate();
}
/**
* Union with Rectangle object
*/
public void add(Rectangle rect) {
setRect(union(this, new MultiRectArea(rect)).rect, false);
invalidate();
}
/**
* Intersect with Rectangle object
*/
public void intersect(Rectangle rect) {
setRect(intersect(this, new MultiRectArea(rect)).rect, false);
invalidate();
}
/**
* Subtract rectangle object
*/
public void substract(Rectangle rect) {
setRect(subtract(this, new MultiRectArea(rect)).rect, false);
}
/**
* Union two MutliRectareArea objects
*/
public static MultiRectArea intersect(MultiRectArea src1, MultiRectArea src2) {
MultiRectArea res = check(MultiRectAreaOp.Intersection.getResult(src1, src2), "intersect(MRA,MRA)"); //$NON-NLS-1$
return res;
}
/**
* Intersect two MultiRectArea objects
*/
public static MultiRectArea union(MultiRectArea src1, MultiRectArea src2) {
MultiRectArea res = check(new MultiRectAreaOp.Union().getResult(src1, src2), "union(MRA,MRA)"); //$NON-NLS-1$
return res;
}
/**
* Subtract two MultiRectArea objects
*/
public static MultiRectArea subtract(MultiRectArea src1, MultiRectArea src2) {
MultiRectArea res = check(MultiRectAreaOp.Subtraction.getResult(src1, src2), "subtract(MRA,MRA)"); //$NON-NLS-1$
return res;
}
/**
* Print MultiRectArea object to output stream
*/
public static void print(MultiRectArea mra, String msg) {
if (mra == null) {
System.out.println(msg + "=null"); //$NON-NLS-1$
} else {
Rectangle[] rects = mra.getRectangles();
System.out.println(msg + "(" + rects.length + ")"); //$NON-NLS-1$ //$NON-NLS-2$
for (Rectangle element : rects) {
System.out.println(
element.x + "," + //$NON-NLS-1$
element.y + "," + //$NON-NLS-1$
(element.x + element.width - 1) + "," + //$NON-NLS-1$
(element.y + element.height - 1));
}
}
}
/**
* Translate MultiRectArea object by (x, y)
*/
public void translate(int x, int y) {
for(int i = 1; i < rect[0];) {
rect[i++] += x;
rect[i++] += y;
rect[i++] += x;
rect[i++] += y;
}
if (bounds != null && !bounds.isEmpty()) {
bounds.translate(x, y);
}
if (rectangles != null) {
for (Rectangle element : rectangles) {
element.translate(x, y);
}
}
}
/**
* Add rectangle to the buffer without any checking
*/
public void addRect(int x1, int y1, int x2, int y2) {
int i = rect[0];
rect = MultiRectAreaOp.checkBufSize(rect, 4);
rect[i++] = x1;
rect[i++] = y1;
rect[i++] = x2;
rect[i++] = y2;
}
/**
* Tests is MultiRectArea empty
*/
public boolean isEmpty() {
return rect[0] == 1;
}
void invalidate() {
bounds = null;
rectangles = null;
}
/**
* Returns bounds of MultiRectArea object
*/
public Rectangle getBounds() {
if (bounds != null) {
return bounds;
}
if (isEmpty()) {
return bounds = new Rectangle();
}
int x1 = rect[1];
int y1 = rect[2];
int x2 = rect[3];
int y2 = rect[4];
for(int i = 5; i < rect[0]; i += 4) {
int rx1 = rect[i + 0];
int ry1 = rect[i + 1];
int rx2 = rect[i + 2];
int ry2 = rect[i + 3];
if (rx1 < x1) {
x1 = rx1;
}
if (rx2 > x2) {
x2 = rx2;
}
if (ry1 < y1) {
y1 = ry1;
}
if (ry2 > y2) {
y2 = ry2;
}
}
return bounds = new Rectangle(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
}
/**
* Recturn rectangle count in the buffer
*/
public int getRectCount() {
return (rect[0] - 1) / 4;
}
/**
* Returns Rectangle array
*/
public Rectangle[] getRectangles() {
if (rectangles != null) {
return rectangles;
}
rectangles = new Rectangle[(rect[0] - 1) / 4];
int j = 0;
for(int i = 1; i < rect[0]; i += 4) {
rectangles[j++] = new Rectangle(
rect[i],
rect[i + 1],
rect[i + 2] - rect[i] + 1,
rect[i + 3] - rect[i + 1] + 1);
}
return rectangles;
}
/**
* Returns Bounds2D
*/
public Rectangle2D getBounds2D() {
return getBounds();
}
/**
* Tests does point lie inside MultiRectArea object
*/
public boolean contains(double x, double y) {
for(int i = 1; i < rect[0]; i+= 4) {
if (rect[i] <= x && x <= rect[i + 2] && rect[i + 1] <= y && y <= rect[i + 3]) {
return true;
}
}
return false;
}
/**
* Tests does Point2D lie inside MultiRectArea object
*/
public boolean contains(Point2D p) {
return contains(p.getX(), p.getY());
}
/**
* Tests does rectangle lie inside MultiRectArea object
*/
public boolean contains(double x, double y, double w, double h) {
throw new RuntimeException("Not implemented"); //$NON-NLS-1$
}
/**
* Tests does Rectangle2D lie inside MultiRectArea object
*/
public boolean contains(Rectangle2D r) {
throw new RuntimeException("Not implemented"); //$NON-NLS-1$
}
/**
* Tests does rectangle intersect MultiRectArea object
*/
public boolean intersects(double x, double y, double w, double h) {
Rectangle r = new Rectangle();
r.setRect(x, y, w, h);
return intersects(r);
}
/**
* Tests does Rectangle2D intersect MultiRectArea object
*/
public boolean intersects(Rectangle2D r) {
if (r == null || r.isEmpty()) {
return false;
}
for(int i = 1; i < rect[0]; i+= 4) {
if (r.intersects(rect[i], rect[i+1], rect[i + 2]-rect[i]+1, rect[i + 3]-rect[i + 1]+1)) {
return true;
}
}
return false;
}
/**
* Returns path iterator
*/
public PathIterator getPathIterator(AffineTransform t, double flatness) {
return new Iterator(this, t);
}
/**
* Returns path iterator
*/
public PathIterator getPathIterator(AffineTransform t) {
return new Iterator(this, t);
}
/**
* Returns MultiRectArea object converted to string
*/
@Override
public String toString() {
int cnt = getRectCount();
StringBuffer sb = new StringBuffer((cnt << 5) + 128);
sb.append(getClass().getName()).append(" ["); //$NON-NLS-1$
for(int i = 1; i < rect[0]; i += 4) {
sb.append(i > 1 ? ", [" : "[").append(rect[i]).append(", ").append(rect[i + 1]). //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
append(", ").append(rect[i + 2] - rect[i] + 1).append(", "). //$NON-NLS-1$ //$NON-NLS-2$
append(rect[i + 3] - rect[i + 1] + 1).append("]"); //$NON-NLS-1$
}
return sb.append("]").toString(); //$NON-NLS-1$
}
}