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