001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: /**
018: * @author Denis M. Kishenko
019: * @version $Revision$
020: */package org.apache.harmony.awt.gl;
021:
022: import java.awt.Rectangle;
023: import java.awt.Shape;
024: import java.awt.geom.AffineTransform;
025: import java.awt.geom.PathIterator;
026: import java.awt.geom.Point2D;
027: import java.awt.geom.Rectangle2D;
028: import java.util.ArrayList;
029: import java.util.NoSuchElementException;
030:
031: import org.apache.harmony.awt.internal.nls.Messages;
032:
033: public class MultiRectArea implements Shape {
034:
035: /**
036: * If CHECK is true validation check active
037: */
038: private static final boolean CHECK = false;
039:
040: boolean sorted = true;
041:
042: /**
043: * Rectangle buffer
044: */
045: public int[] rect;
046:
047: /**
048: * Bounding box
049: */
050: Rectangle bounds;
051:
052: /**
053: * Result rectangle array
054: */
055: Rectangle[] rectangles;
056:
057: /**
058: * LineCash provides creating MultiRectArea line by line. Used in JavaShapeRasterizer.
059: */
060: public static class LineCash extends MultiRectArea {
061:
062: int lineY;
063: int bottomCount;
064: int[] bottom;
065:
066: public LineCash(int size) {
067: super ();
068: bottom = new int[size];
069: bottomCount = 0;
070: }
071:
072: public void setLine(int y) {
073: lineY = y;
074: }
075:
076: public void skipLine() {
077: lineY++;
078: bottomCount = 0;
079: }
080:
081: public void addLine(int[] points, int pointCount) {
082: int bottomIndex = 0;
083: int pointIndex = 0;
084: int rectIndex = 0;
085: int pointX1 = 0;
086: int pointX2 = 0;
087: int bottomX1 = 0;
088: int bottomX2 = 0;
089: boolean appendRect = false;
090: boolean deleteRect = false;
091: int lastCount = bottomCount;
092:
093: while (bottomIndex < lastCount || pointIndex < pointCount) {
094:
095: appendRect = false;
096: deleteRect = false;
097:
098: if (bottomIndex < lastCount) {
099: rectIndex = bottom[bottomIndex];
100: bottomX1 = rect[rectIndex];
101: bottomX2 = rect[rectIndex + 2];
102: } else {
103: appendRect = true;
104: }
105:
106: if (pointIndex < pointCount) {
107: pointX1 = points[pointIndex];
108: pointX2 = points[pointIndex + 1];
109: } else {
110: deleteRect = true;
111: }
112:
113: if (!deleteRect && !appendRect) {
114: if (pointX1 == bottomX1 && pointX2 == bottomX2) {
115: rect[rectIndex + 3] = rect[rectIndex + 3] + 1;
116: pointIndex += 2;
117: bottomIndex++;
118: continue;
119: }
120: deleteRect = pointX2 >= bottomX1;
121: appendRect = pointX1 <= bottomX2;
122: }
123:
124: if (deleteRect) {
125: if (bottomIndex < bottomCount - 1) {
126: System.arraycopy(bottom, bottomIndex + 1,
127: bottom, bottomIndex, bottomCount
128: - bottomIndex - 1);
129: rectIndex -= 4;
130: }
131: bottomCount--;
132: lastCount--;
133: }
134:
135: if (appendRect) {
136: int i = rect[0];
137: bottom[bottomCount++] = i;
138: rect = MultiRectAreaOp.checkBufSize(rect, 4);
139: rect[i++] = pointX1;
140: rect[i++] = lineY;
141: rect[i++] = pointX2;
142: rect[i++] = lineY;
143: pointIndex += 2;
144: }
145: }
146: lineY++;
147:
148: invalidate();
149: }
150:
151: }
152:
153: /**
154: * RectCash provides simple creating MultiRectArea
155: */
156: public static class RectCash extends MultiRectArea {
157:
158: int[] cash;
159:
160: public RectCash() {
161: super ();
162: cash = new int[MultiRectAreaOp.RECT_CAPACITY];
163: cash[0] = 1;
164: }
165:
166: public void addRectCashed(int x1, int y1, int x2, int y2) {
167: addRect(x1, y1, x2, y2);
168: invalidate();
169: /*
170: // Exclude from cash unnecessary rectangles
171: int i = 1;
172: while(i < cash[0]) {
173: if (rect[cash[i] + 3] >= y1 - 1) {
174: if (i > 1) {
175: System.arraycopy(cash, i, cash, 1, cash[0] - i);
176: }
177: break;
178: }
179: i++;
180: }
181: cash[0] -= i - 1;
182:
183: // Find in cash rectangle to concatinate
184: i = 1;
185: while(i < cash[0]) {
186: int index = cash[i];
187: if (rect[index + 3] != y1 - 1) {
188: break;
189: }
190: if (rect[index] == x1 && rect[index + 2] == x2) {
191: rect[index + 3] += y2 - y1 + 1;
192:
193: int pos = i + 1;
194: while(pos < cash[0]) {
195: if (rect[index + 3] <= rect[cash[i] + 3]) {
196: System.arraycopy(cash, i + 1, cash, i, pos - i);
197: break;
198: }
199: i++;
200: }
201: cash[pos - 1] = index;
202:
203: invalidate();
204: return;
205: }
206: i++;
207: }
208:
209: // Add rectangle to buffer
210: int index = rect[0];
211: rect = MultiRectAreaOp.checkBufSize(rect, 4);
212: rect[index + 0] = x1;
213: rect[index + 1] = y1;
214: rect[index + 2] = x2;
215: rect[index + 3] = y2;
216:
217: // Add rectangle to cash
218: int length = cash[0];
219: cash = MultiRectAreaOp.checkBufSize(cash, 1);
220: while(i < length) {
221: if (y2 <= rect[cash[i] + 3]) {
222: System.arraycopy(cash, i, cash, i + 1, length - i);
223: break;
224: }
225: i++;
226: }
227: cash[i] = index;
228: invalidate();
229: */
230: }
231:
232: public void addRectCashed(int[] rect, int rectOff,
233: int rectLength) {
234: for (int i = rectOff; i < rectOff + rectLength;) {
235: addRect(rect[i++], rect[i++], rect[i++], rect[i++]);
236: // addRectCashed(rect[i++], rect[i++], rect[i++], rect[i++]);
237: }
238: }
239:
240: }
241:
242: /**
243: * MultiRectArea path iterator
244: */
245: class Iterator implements PathIterator {
246:
247: int type;
248: int index;
249: int pos;
250:
251: int[] rect;
252: AffineTransform t;
253:
254: Iterator(MultiRectArea mra, AffineTransform t) {
255: rect = new int[mra.rect[0] - 1];
256: System.arraycopy(mra.rect, 1, rect, 0, rect.length);
257: this .t = t;
258: }
259:
260: public int getWindingRule() {
261: return WIND_NON_ZERO;
262: }
263:
264: public boolean isDone() {
265: return pos >= rect.length;
266: }
267:
268: public void next() {
269: if (index == 4) {
270: pos += 4;
271: }
272: index = (index + 1) % 5;
273: }
274:
275: public int currentSegment(double[] coords) {
276: if (isDone()) {
277: // awt.4B=Iiterator out of bounds
278: throw new NoSuchElementException(Messages
279: .getString("awt.4B")); //$NON-NLS-1$
280: }
281: int type = 0;
282:
283: switch (index) {
284: case 0:
285: type = SEG_MOVETO;
286: coords[0] = rect[pos + 0];
287: coords[1] = rect[pos + 1];
288: break;
289: case 1:
290: type = SEG_LINETO;
291: coords[0] = rect[pos + 2];
292: coords[1] = rect[pos + 1];
293: break;
294: case 2:
295: type = SEG_LINETO;
296: coords[0] = rect[pos + 2];
297: coords[1] = rect[pos + 3];
298: break;
299: case 3:
300: type = SEG_LINETO;
301: coords[0] = rect[pos + 0];
302: coords[1] = rect[pos + 3];
303: break;
304: case 4:
305: type = SEG_CLOSE;
306: break;
307: }
308:
309: if (t != null) {
310: t.transform(coords, 0, coords, 0, 1);
311: }
312: return type;
313: }
314:
315: public int currentSegment(float[] coords) {
316: if (isDone()) {
317: // awt.4B=Iiterator out of bounds
318: throw new NoSuchElementException(Messages
319: .getString("awt.4B")); //$NON-NLS-1$
320: }
321: int type = 0;
322:
323: switch (index) {
324: case 0:
325: type = SEG_MOVETO;
326: coords[0] = rect[pos + 0];
327: coords[1] = rect[pos + 1];
328: break;
329: case 1:
330: type = SEG_LINETO;
331: coords[0] = rect[pos + 2];
332: coords[1] = rect[pos + 1];
333: break;
334: case 2:
335: type = SEG_LINETO;
336: coords[0] = rect[pos + 2];
337: coords[1] = rect[pos + 3];
338: break;
339: case 3:
340: type = SEG_LINETO;
341: coords[0] = rect[pos + 0];
342: coords[1] = rect[pos + 3];
343: break;
344: case 4:
345: type = SEG_CLOSE;
346: break;
347: }
348:
349: if (t != null) {
350: t.transform(coords, 0, coords, 0, 1);
351: }
352: return type;
353: }
354:
355: }
356:
357: /**
358: * Constructs a new empty MultiRectArea
359: */
360: public MultiRectArea() {
361: rect = MultiRectAreaOp.createBuf(0);
362: }
363:
364: public MultiRectArea(boolean sorted) {
365: this ();
366: this .sorted = sorted;
367: }
368:
369: /**
370: * Constructs a new MultiRectArea as a copy of another one
371: */
372: public MultiRectArea(MultiRectArea mra) {
373: if (mra == null) {
374: rect = MultiRectAreaOp.createBuf(0);
375: } else {
376: rect = new int[mra.rect.length];
377: System.arraycopy(mra.rect, 0, rect, 0, mra.rect.length);
378: check(this , "MultiRectArea(MRA)"); //$NON-NLS-1$
379: }
380: }
381:
382: /**
383: * Constructs a new MultiRectArea consists of single rectangle
384: */
385: public MultiRectArea(Rectangle r) {
386: rect = MultiRectAreaOp.createBuf(0);
387: if (r != null && !r.isEmpty()) {
388: rect[0] = 5;
389: rect[1] = r.x;
390: rect[2] = r.y;
391: rect[3] = r.x + r.width - 1;
392: rect[4] = r.y + r.height - 1;
393: }
394: check(this , "MultiRectArea(Rectangle)"); //$NON-NLS-1$
395: }
396:
397: /**
398: * Constructs a new MultiRectArea consists of single rectangle
399: */
400: public MultiRectArea(int x0, int y0, int x1, int y1) {
401: rect = MultiRectAreaOp.createBuf(0);
402: if (x1 >= x0 && y1 >= y0) {
403: rect[0] = 5;
404: rect[1] = x0;
405: rect[2] = y0;
406: rect[3] = x1;
407: rect[4] = y1;
408: }
409: check(this , "MultiRectArea(Rectangle)"); //$NON-NLS-1$
410: }
411:
412: /**
413: * Constructs a new MultiRectArea and append rectangle from buffer
414: */
415: public MultiRectArea(Rectangle[] buf) {
416: this ();
417: for (Rectangle element : buf) {
418: add(element);
419: }
420: }
421:
422: /**
423: * Constructs a new MultiRectArea and append rectangle from array
424: */
425: public MultiRectArea(ArrayList<Rectangle> buf) {
426: this ();
427: for (int i = 0; i < buf.size(); i++) {
428: add(buf.get(i));
429: }
430: }
431:
432: /**
433: * Sort rectangle buffer
434: */
435: void resort() {
436: int[] buf = new int[4];
437: for (int i = 1; i < rect[0]; i += 4) {
438: int k = i;
439: int x1 = rect[k];
440: int y1 = rect[k + 1];
441: for (int j = i + 4; j < rect[0]; j += 4) {
442: int x2 = rect[j];
443: int y2 = rect[j + 1];
444: if (y1 > y2 || (y1 == y2 && x1 > x2)) {
445: x1 = x2;
446: y1 = y2;
447: k = j;
448: }
449: }
450: if (k != i) {
451: System.arraycopy(rect, i, buf, 0, 4);
452: System.arraycopy(rect, k, rect, i, 4);
453: System.arraycopy(buf, 0, rect, k, 4);
454: }
455: }
456: invalidate();
457: }
458:
459: /**
460: * Tests equals with another object
461: */
462: @Override
463: public boolean equals(Object obj) {
464: if (obj == this ) {
465: return true;
466: }
467: if (obj instanceof MultiRectArea) {
468: MultiRectArea mra = (MultiRectArea) obj;
469: for (int i = 0; i < rect[0]; i++) {
470: if (rect[i] != mra.rect[i]) {
471: return false;
472: }
473: }
474: return true;
475: }
476: return false;
477: }
478:
479: /**
480: * Checks validation of MultiRectArea object
481: */
482: static MultiRectArea check(MultiRectArea mra, String msg) {
483: if (CHECK && mra != null) {
484: if (MultiRectArea.checkValidation(mra.getRectangles(),
485: mra.sorted) != -1) {
486: // awt.4C=Invalid MultiRectArea in method {0}
487: new RuntimeException(Messages.getString("awt.4C", msg)); //$NON-NLS-1$
488: }
489: }
490: return mra;
491: }
492:
493: /**
494: * Checks validation of MultiRectArea object
495: */
496: public static int checkValidation(Rectangle[] r, boolean sorted) {
497:
498: // Check width and height
499: for (int i = 0; i < r.length; i++) {
500: if (r[i].width <= 0 || r[i].height <= 0) {
501: return i;
502: }
503: }
504:
505: // Check order
506: if (sorted) {
507: for (int i = 1; i < r.length; i++) {
508: if (r[i - 1].y > r[i].y) {
509: return i;
510: }
511: if (r[i - 1].y == r[i].y) {
512: if (r[i - 1].x > r[i].x) {
513: return i;
514: }
515: }
516: }
517: }
518:
519: // Check override
520: for (int i = 0; i < r.length; i++) {
521: for (int j = i + 1; j < r.length; j++) {
522: if (r[i].intersects(r[j])) {
523: return i;
524: }
525: }
526: }
527:
528: return -1;
529: }
530:
531: /**
532: * Assigns rectangle from another buffer
533: */
534: protected void setRect(int[] buf, boolean copy) {
535: if (copy) {
536: rect = new int[buf.length];
537: System.arraycopy(buf, 0, rect, 0, buf.length);
538: } else {
539: rect = buf;
540: }
541: invalidate();
542: }
543:
544: /**
545: * Union with another MultiRectArea object
546: */
547: public void add(MultiRectArea mra) {
548: setRect(union(this , mra).rect, false);
549: invalidate();
550: }
551:
552: /**
553: * Intersect with another MultiRectArea object
554: */
555: public void intersect(MultiRectArea mra) {
556: setRect(intersect(this , mra).rect, false);
557: invalidate();
558: }
559:
560: /**
561: * Subtract another MultiRectArea object
562: */
563: public void substract(MultiRectArea mra) {
564: setRect(subtract(this , mra).rect, false);
565: invalidate();
566: }
567:
568: /**
569: * Union with Rectangle object
570: */
571: public void add(Rectangle rect) {
572: setRect(union(this , new MultiRectArea(rect)).rect, false);
573: invalidate();
574: }
575:
576: /**
577: * Intersect with Rectangle object
578: */
579: public void intersect(Rectangle rect) {
580: setRect(intersect(this , new MultiRectArea(rect)).rect, false);
581: invalidate();
582: }
583:
584: /**
585: * Subtract rectangle object
586: */
587: public void substract(Rectangle rect) {
588: setRect(subtract(this , new MultiRectArea(rect)).rect, false);
589: }
590:
591: /**
592: * Union two MutliRectareArea objects
593: */
594: public static MultiRectArea intersect(MultiRectArea src1,
595: MultiRectArea src2) {
596: MultiRectArea res = check(MultiRectAreaOp.Intersection
597: .getResult(src1, src2), "intersect(MRA,MRA)"); //$NON-NLS-1$
598: return res;
599: }
600:
601: /**
602: * Intersect two MultiRectArea objects
603: */
604: public static MultiRectArea union(MultiRectArea src1,
605: MultiRectArea src2) {
606: MultiRectArea res = check(new MultiRectAreaOp.Union()
607: .getResult(src1, src2), "union(MRA,MRA)"); //$NON-NLS-1$
608: return res;
609: }
610:
611: /**
612: * Subtract two MultiRectArea objects
613: */
614: public static MultiRectArea subtract(MultiRectArea src1,
615: MultiRectArea src2) {
616: MultiRectArea res = check(MultiRectAreaOp.Subtraction
617: .getResult(src1, src2), "subtract(MRA,MRA)"); //$NON-NLS-1$
618: return res;
619: }
620:
621: /**
622: * Print MultiRectArea object to output stream
623: */
624: public static void print(MultiRectArea mra, String msg) {
625: if (mra == null) {
626: System.out.println(msg + "=null"); //$NON-NLS-1$
627: } else {
628: Rectangle[] rects = mra.getRectangles();
629: System.out.println(msg + "(" + rects.length + ")"); //$NON-NLS-1$ //$NON-NLS-2$
630: for (Rectangle element : rects) {
631: System.out.println(element.x + "," + //$NON-NLS-1$
632: element.y + "," + //$NON-NLS-1$
633: (element.x + element.width - 1) + "," + //$NON-NLS-1$
634: (element.y + element.height - 1));
635: }
636: }
637: }
638:
639: /**
640: * Translate MultiRectArea object by (x, y)
641: */
642: public void translate(int x, int y) {
643: for (int i = 1; i < rect[0];) {
644: rect[i++] += x;
645: rect[i++] += y;
646: rect[i++] += x;
647: rect[i++] += y;
648: }
649:
650: if (bounds != null && !bounds.isEmpty()) {
651: bounds.translate(x, y);
652: }
653:
654: if (rectangles != null) {
655: for (Rectangle element : rectangles) {
656: element.translate(x, y);
657: }
658: }
659: }
660:
661: /**
662: * Add rectangle to the buffer without any checking
663: */
664: public void addRect(int x1, int y1, int x2, int y2) {
665: int i = rect[0];
666: rect = MultiRectAreaOp.checkBufSize(rect, 4);
667: rect[i++] = x1;
668: rect[i++] = y1;
669: rect[i++] = x2;
670: rect[i++] = y2;
671: }
672:
673: /**
674: * Tests is MultiRectArea empty
675: */
676: public boolean isEmpty() {
677: return rect[0] == 1;
678: }
679:
680: void invalidate() {
681: bounds = null;
682: rectangles = null;
683: }
684:
685: /**
686: * Returns bounds of MultiRectArea object
687: */
688: public Rectangle getBounds() {
689: if (bounds != null) {
690: return bounds;
691: }
692:
693: if (isEmpty()) {
694: return bounds = new Rectangle();
695: }
696:
697: int x1 = rect[1];
698: int y1 = rect[2];
699: int x2 = rect[3];
700: int y2 = rect[4];
701:
702: for (int i = 5; i < rect[0]; i += 4) {
703: int rx1 = rect[i + 0];
704: int ry1 = rect[i + 1];
705: int rx2 = rect[i + 2];
706: int ry2 = rect[i + 3];
707: if (rx1 < x1) {
708: x1 = rx1;
709: }
710: if (rx2 > x2) {
711: x2 = rx2;
712: }
713: if (ry1 < y1) {
714: y1 = ry1;
715: }
716: if (ry2 > y2) {
717: y2 = ry2;
718: }
719: }
720:
721: return bounds = new Rectangle(x1, y1, x2 - x1 + 1, y2 - y1 + 1);
722: }
723:
724: /**
725: * Return rectangle count in the buffer
726: */
727: public int getRectCount() {
728: return (rect[0] - 1) / 4;
729: }
730:
731: /**
732: * Returns Rectangle array
733: */
734: public Rectangle[] getRectangles() {
735: if (rectangles != null) {
736: return rectangles;
737: }
738:
739: rectangles = new Rectangle[(rect[0] - 1) / 4];
740: int j = 0;
741: for (int i = 1; i < rect[0]; i += 4) {
742: rectangles[j++] = new Rectangle(rect[i], rect[i + 1],
743: rect[i + 2] - rect[i] + 1, rect[i + 3]
744: - rect[i + 1] + 1);
745: }
746: return rectangles;
747: }
748:
749: /**
750: * Returns Bounds2D
751: */
752: public Rectangle2D getBounds2D() {
753: return getBounds();
754: }
755:
756: /**
757: * Tests does point lie inside MultiRectArea object
758: */
759: public boolean contains(double x, double y) {
760: for (int i = 1; i < rect[0]; i += 4) {
761: if (rect[i] <= x && x <= rect[i + 2] && rect[i + 1] <= y
762: && y <= rect[i + 3]) {
763: return true;
764: }
765: }
766: return false;
767: }
768:
769: /**
770: * Tests does Point2D lie inside MultiRectArea object
771: */
772: public boolean contains(Point2D p) {
773: return contains(p.getX(), p.getY());
774: }
775:
776: /**
777: * Tests does rectangle lie inside MultiRectArea object
778: */
779: public boolean contains(double x, double y, double w, double h) {
780: throw new RuntimeException("Not implemented"); //$NON-NLS-1$
781: }
782:
783: /**
784: * Tests does Rectangle2D lie inside MultiRectArea object
785: */
786: public boolean contains(Rectangle2D r) {
787: throw new RuntimeException("Not implemented"); //$NON-NLS-1$
788: }
789:
790: /**
791: * Tests does rectangle intersect MultiRectArea object
792: */
793: public boolean intersects(double x, double y, double w, double h) {
794: Rectangle r = new Rectangle();
795: r.setRect(x, y, w, h);
796: return intersects(r);
797: }
798:
799: /**
800: * Tests does Rectangle2D intersect MultiRectArea object
801: */
802: public boolean intersects(Rectangle2D r) {
803: if (r == null || r.isEmpty()) {
804: return false;
805: }
806: for (int i = 1; i < rect[0]; i += 4) {
807: if (r.intersects(rect[i], rect[i + 1], rect[i + 2]
808: - rect[i] + 1, rect[i + 3] - rect[i + 1] + 1)) {
809: return true;
810: }
811: }
812: return false;
813: }
814:
815: /**
816: * Returns path iterator
817: */
818: public PathIterator getPathIterator(AffineTransform t,
819: double flatness) {
820: return new Iterator(this , t);
821: }
822:
823: /**
824: * Returns path iterator
825: */
826: public PathIterator getPathIterator(AffineTransform t) {
827: return new Iterator(this , t);
828: }
829:
830: /**
831: * Returns MultiRectArea object converted to string
832: */
833: @Override
834: public String toString() {
835: int cnt = getRectCount();
836: StringBuffer sb = new StringBuffer((cnt << 5) + 128);
837: sb.append(getClass().getName()).append(" ["); //$NON-NLS-1$
838: for (int i = 1; i < rect[0]; i += 4) {
839: sb
840: .append(i > 1 ? ", [" : "[").append(rect[i]).append(", ").append(rect[i + 1]). //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
841: append(", ").append(rect[i + 2] - rect[i] + 1).append(", "). //$NON-NLS-1$ //$NON-NLS-2$
842: append(rect[i + 3] - rect[i + 1] + 1).append("]"); //$NON-NLS-1$
843: }
844: return sb.append("]").toString(); //$NON-NLS-1$
845: }
846:
847: }
|