1 /*
2  * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 /*
25  * @test %W% %E%
26  * @bug 6504874
27  * @summary This test verifies the operation (and performance) of the
28  *          various CAG operations on the internal Region class.
29  * @modules java.desktop/sun.java2d.pipe
30  * @run main RegionOps
31  */
32 
33 import java.awt.Rectangle;
34 import java.awt.geom.Area;
35 import java.awt.geom.AffineTransform;
36 import java.awt.image.BufferedImage;
37 import java.util.Random;
38 import sun.java2d.pipe.Region;
39 
40 public class RegionOps {
41     public static final int DEFAULT_NUMREGIONS = 50;
42     public static final int DEFAULT_MINSUBRECTS = 1;
43     public static final int DEFAULT_MAXSUBRECTS = 10;
44 
45     public static final int MINCOORD = -20;
46     public static final int MAXCOORD = 20;
47 
48     public static boolean useArea;
49 
50     static int numops;
51     static int numErrors;
52     static Random rand = new Random();
53     static boolean skipCheck;
54     static boolean countErrors;
55 
56     static {
57         // Instantiating BufferedImage initializes sun.java2d
58         BufferedImage bimg =
59             new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
60     }
61 
usage(String error)62     public static void usage(String error) {
63         if (error != null) {
64             System.err.println("Error: "+error);
65         }
66         System.err.println("Usage: java RegionOps "+
67                            "[-regions N] [-rects M] "+
68                            "[-[min|max]rects M] [-area]");
69         System.err.println("                      "+
70                            "[-add|union] [-sub|diff] "+
71                            "[-int[ersect]] [-xor]");
72         System.err.println("                      "+
73                            "[-seed S] [-nocheck] [-count[errors]] [-help]");
74         System.exit((error != null) ? 1 : 0);
75     }
76 
error(RectListImpl a, RectListImpl b, String problem)77     public static void error(RectListImpl a, RectListImpl b, String problem) {
78         System.err.println("Operating on:  "+a);
79         if (b != null) {
80             System.err.println("and:  "+b);
81         }
82         if (countErrors) {
83             System.err.println(problem);
84             numErrors++;
85         } else {
86             throw new RuntimeException(problem);
87         }
88     }
89 
main(String argv[])90     public static void main(String argv[]) {
91         int numregions = DEFAULT_NUMREGIONS;
92         int minsubrects = DEFAULT_MINSUBRECTS;
93         int maxsubrects = DEFAULT_MAXSUBRECTS;
94         boolean doUnion = false;
95         boolean doIntersect = false;
96         boolean doSubtract = false;
97         boolean doXor = false;
98 
99         for (int i = 0; i < argv.length; i++) {
100             String arg = argv[i];
101             if (arg.equalsIgnoreCase("-regions")) {
102                 if (i+1 >= argv.length) {
103                     usage("missing arg for -regions");
104                 }
105                 numregions = Integer.parseInt(argv[++i]);
106             } else if (arg.equalsIgnoreCase("-rects")) {
107                 if (i+1 >= argv.length) {
108                     usage("missing arg for -rects");
109                 }
110                 minsubrects = maxsubrects = Integer.parseInt(argv[++i]);
111             } else if (arg.equalsIgnoreCase("-minrects")) {
112                 if (i+1 >= argv.length) {
113                     usage("missing arg for -minrects");
114                 }
115                 minsubrects = Integer.parseInt(argv[++i]);
116             } else if (arg.equalsIgnoreCase("-maxrects")) {
117                 if (i+1 >= argv.length) {
118                     usage("missing arg for -maxrects");
119                 }
120                 maxsubrects = Integer.parseInt(argv[++i]);
121             } else if (arg.equalsIgnoreCase("-area")) {
122                 useArea = true;
123             } else if (arg.equalsIgnoreCase("-add") ||
124                        arg.equalsIgnoreCase("-union"))
125             {
126                 doUnion = true;
127             } else if (arg.equalsIgnoreCase("-sub") ||
128                        arg.equalsIgnoreCase("-diff"))
129             {
130                 doSubtract = true;
131             } else if (arg.equalsIgnoreCase("-int") ||
132                        arg.equalsIgnoreCase("-intersect"))
133             {
134                 doIntersect = true;
135             } else if (arg.equalsIgnoreCase("-xor")) {
136                 doXor = true;
137             } else if (arg.equalsIgnoreCase("-seed")) {
138                 if (i+1 >= argv.length) {
139                     usage("missing arg for -seed");
140                 }
141                 rand.setSeed(Long.decode(argv[++i]).longValue());
142             } else if (arg.equalsIgnoreCase("-nocheck")) {
143                 skipCheck = true;
144             } else if (arg.equalsIgnoreCase("-count") ||
145                        arg.equalsIgnoreCase("-counterrors"))
146             {
147                 countErrors = true;
148             } else if (arg.equalsIgnoreCase("-help")) {
149                 usage(null);
150             } else {
151                 usage("Unknown argument: "+arg);
152             }
153         }
154 
155         if (maxsubrects < minsubrects) {
156             usage("maximum number of subrectangles less than minimum");
157         }
158 
159         if (minsubrects <= 0) {
160             usage("minimum number of subrectangles must be positive");
161         }
162 
163         if (!doUnion && !doSubtract && !doIntersect && !doXor) {
164             doUnion = doSubtract = doIntersect = doXor = true;
165         }
166 
167         long start = System.currentTimeMillis();
168         RectListImpl rlist[] = new RectListImpl[numregions];
169         int totalrects = 0;
170         for (int i = 0; i < rlist.length; i++) {
171             RectListImpl rli = RectListImpl.getInstance();
172             int numsubrects =
173                 minsubrects + rand.nextInt(maxsubrects - minsubrects + 1);
174             for (int j = 0; j < numsubrects; j++) {
175                 addRectTo(rli);
176                 totalrects++;
177             }
178             rlist[i] = rli;
179         }
180         long end = System.currentTimeMillis();
181         System.out.println((end-start)+"ms to create "+
182                            rlist.length+" regions containing "+
183                            totalrects+" subrectangles");
184 
185         start = System.currentTimeMillis();
186         for (int i = 0; i < rlist.length; i++) {
187             RectListImpl a = rlist[i];
188             testTranslate(a);
189             for (int j = i; j < rlist.length; j++) {
190                 RectListImpl b = rlist[j];
191                 if (doUnion) testUnion(a, b);
192                 if (doSubtract) testDifference(a, b);
193                 if (doIntersect) testIntersection(a, b);
194                 if (doXor) testExclusiveOr(a, b);
195             }
196         }
197         end = System.currentTimeMillis();
198         System.out.println(numops+" ops took "+(end-start)+"ms");
199 
200         if (numErrors > 0) {
201             throw new RuntimeException(numErrors+" errors encountered");
202         }
203     }
204 
addRectTo(RectListImpl rli)205     public static void addRectTo(RectListImpl rli) {
206         int lox = MINCOORD + rand.nextInt(MAXCOORD - MINCOORD + 1);
207         int hix = MINCOORD + rand.nextInt(MAXCOORD - MINCOORD + 1);
208         int loy = MINCOORD + rand.nextInt(MAXCOORD - MINCOORD + 1);
209         int hiy = MINCOORD + rand.nextInt(MAXCOORD - MINCOORD + 1);
210         rli.addRect(lox, loy, hix, hiy);
211     }
212 
checkEqual(RectListImpl a, RectListImpl b, String optype)213     public static void checkEqual(RectListImpl a, RectListImpl b,
214                                   String optype)
215     {
216         if (a.hashCode() != b.hashCode()) {
217             error(a, b, "hashcode failed for "+optype);
218         }
219         if (!a.equals(b)) {
220             error(a, b, "equals failed for "+optype);
221         }
222     }
223 
testTranslate(RectListImpl a)224     public static void testTranslate(RectListImpl a) {
225         RectListImpl maxTrans =
226             a.getTranslation(Integer.MAX_VALUE, Integer.MAX_VALUE)
227             .getTranslation(Integer.MAX_VALUE, Integer.MAX_VALUE)
228             .getTranslation(Integer.MAX_VALUE, Integer.MAX_VALUE);
229         if (!maxTrans.checkTransEmpty()) {
230             error(maxTrans, null, "overflow translated RectList not empty");
231         }
232         RectListImpl minTrans =
233             a.getTranslation(Integer.MIN_VALUE, Integer.MIN_VALUE)
234             .getTranslation(Integer.MIN_VALUE, Integer.MIN_VALUE)
235             .getTranslation(Integer.MIN_VALUE, Integer.MIN_VALUE);
236         if (!minTrans.checkTransEmpty()) {
237             error(minTrans, null, "overflow translated RectList not empty");
238         }
239         testTranslate(a, Integer.MAX_VALUE, Integer.MAX_VALUE, false,
240                       MINCOORD, 0, MINCOORD, 0);
241         testTranslate(a, Integer.MAX_VALUE, Integer.MIN_VALUE, false,
242                       MINCOORD, 0, 0, MAXCOORD);
243         testTranslate(a, Integer.MIN_VALUE, Integer.MAX_VALUE, false,
244                       0, MAXCOORD, MINCOORD, 0);
245         testTranslate(a, Integer.MIN_VALUE, Integer.MIN_VALUE, false,
246                       0, MAXCOORD, 0, MAXCOORD);
247         for (int dy = -100; dy <= 100; dy += 50) {
248             for (int dx = -100; dx <= 100; dx += 50) {
249                 testTranslate(a, dx, dy, true,
250                               MINCOORD, MAXCOORD,
251                               MINCOORD, MAXCOORD);
252             }
253         }
254     }
255 
testTranslate(RectListImpl a, int dx, int dy, boolean isNonDestructive, int xmin, int xmax, int ymin, int ymax)256     public static void testTranslate(RectListImpl a, int dx, int dy,
257                                      boolean isNonDestructive,
258                                      int xmin, int xmax,
259                                      int ymin, int ymax)
260     {
261         RectListImpl theTrans = a.getTranslation(dx, dy); numops++;
262         if (skipCheck) return;
263         RectListImpl unTrans = theTrans.getTranslation(-dx, -dy);
264         if (isNonDestructive) checkEqual(a, unTrans, "Translate");
265         for (int x = xmin; x < xmax; x++) {
266             for (int y = ymin; y < ymax; y++) {
267                 boolean inside = a.contains(x, y);
268                 if (theTrans.contains(x+dx, y+dy) != inside) {
269                     error(a, null, "translation failed for "+
270                           dx+", "+dy+" at "+x+", "+y);
271                 }
272             }
273         }
274     }
275 
testUnion(RectListImpl a, RectListImpl b)276     public static void testUnion(RectListImpl a, RectListImpl b) {
277         RectListImpl aUb = a.getUnion(b); numops++;
278         RectListImpl bUa = b.getUnion(a); numops++;
279         if (skipCheck) return;
280         checkEqual(aUb, bUa, "Union");
281         testUnion(a, b, aUb);
282         testUnion(a, b, bUa);
283     }
284 
testUnion(RectListImpl a, RectListImpl b, RectListImpl theUnion)285     public static void testUnion(RectListImpl a, RectListImpl b,
286                                  RectListImpl theUnion)
287     {
288         for (int x = MINCOORD; x < MAXCOORD; x++) {
289             for (int y = MINCOORD; y < MAXCOORD; y++) {
290                 boolean inside = (a.contains(x, y) || b.contains(x, y));
291                 if (theUnion.contains(x, y) != inside) {
292                     error(a, b, "union failed at "+x+", "+y);
293                 }
294             }
295         }
296     }
297 
testDifference(RectListImpl a, RectListImpl b)298     public static void testDifference(RectListImpl a, RectListImpl b) {
299         RectListImpl aDb = a.getDifference(b); numops++;
300         RectListImpl bDa = b.getDifference(a); numops++;
301         if (skipCheck) return;
302         // Note that difference is not commutative so we cannot check equals
303         // checkEqual(a, b, "Difference");
304         testDifference(a, b, aDb);
305         testDifference(b, a, bDa);
306     }
307 
testDifference(RectListImpl a, RectListImpl b, RectListImpl theDifference)308     public static void testDifference(RectListImpl a, RectListImpl b,
309                                       RectListImpl theDifference)
310     {
311         for (int x = MINCOORD; x < MAXCOORD; x++) {
312             for (int y = MINCOORD; y < MAXCOORD; y++) {
313                 boolean inside = (a.contains(x, y) && !b.contains(x, y));
314                 if (theDifference.contains(x, y) != inside) {
315                     error(a, b, "difference failed at "+x+", "+y);
316                 }
317             }
318         }
319     }
320 
testIntersection(RectListImpl a, RectListImpl b)321     public static void testIntersection(RectListImpl a, RectListImpl b) {
322         RectListImpl aIb = a.getIntersection(b); numops++;
323         RectListImpl bIa = b.getIntersection(a); numops++;
324         if (skipCheck) return;
325         checkEqual(aIb, bIa, "Intersection");
326         testIntersection(a, b, aIb);
327         testIntersection(a, b, bIa);
328     }
329 
testIntersection(RectListImpl a, RectListImpl b, RectListImpl theIntersection)330     public static void testIntersection(RectListImpl a, RectListImpl b,
331                                         RectListImpl theIntersection)
332     {
333         for (int x = MINCOORD; x < MAXCOORD; x++) {
334             for (int y = MINCOORD; y < MAXCOORD; y++) {
335                 boolean inside = (a.contains(x, y) && b.contains(x, y));
336                 if (theIntersection.contains(x, y) != inside) {
337                     error(a, b, "intersection failed at "+x+", "+y);
338                 }
339             }
340         }
341     }
342 
testExclusiveOr(RectListImpl a, RectListImpl b)343     public static void testExclusiveOr(RectListImpl a, RectListImpl b) {
344         RectListImpl aXb = a.getExclusiveOr(b); numops++;
345         RectListImpl bXa = b.getExclusiveOr(a); numops++;
346         if (skipCheck) return;
347         checkEqual(aXb, bXa, "ExclusiveOr");
348         testExclusiveOr(a, b, aXb);
349         testExclusiveOr(a, b, bXa);
350     }
351 
testExclusiveOr(RectListImpl a, RectListImpl b, RectListImpl theExclusiveOr)352     public static void testExclusiveOr(RectListImpl a, RectListImpl b,
353                                        RectListImpl theExclusiveOr)
354     {
355         for (int x = MINCOORD; x < MAXCOORD; x++) {
356             for (int y = MINCOORD; y < MAXCOORD; y++) {
357                 boolean inside = (a.contains(x, y) != b.contains(x, y));
358                 if (theExclusiveOr.contains(x, y) != inside) {
359                     error(a, b, "xor failed at "+x+", "+y);
360                 }
361             }
362         }
363     }
364 
365     public abstract static class RectListImpl {
getInstance()366         public static RectListImpl getInstance() {
367             if (useArea) {
368                 return new AreaImpl();
369             } else {
370                 return new RegionImpl();
371             }
372         }
373 
addRect(int lox, int loy, int hix, int hiy)374         public abstract void addRect(int lox, int loy, int hix, int hiy);
375 
getTranslation(int dx, int dy)376         public abstract RectListImpl getTranslation(int dx, int dy);
377 
getIntersection(RectListImpl rli)378         public abstract RectListImpl getIntersection(RectListImpl rli);
getExclusiveOr(RectListImpl rli)379         public abstract RectListImpl getExclusiveOr(RectListImpl rli);
getDifference(RectListImpl rli)380         public abstract RectListImpl getDifference(RectListImpl rli);
getUnion(RectListImpl rli)381         public abstract RectListImpl getUnion(RectListImpl rli);
382 
383         // Used for making sure that 3xMAX translates yields an empty region
checkTransEmpty()384         public abstract boolean checkTransEmpty();
385 
contains(int x, int y)386         public abstract boolean contains(int x, int y);
387 
hashCode()388         public abstract int hashCode();
equals(RectListImpl other)389         public abstract boolean equals(RectListImpl other);
390     }
391 
392     public static class AreaImpl extends RectListImpl {
393         Area theArea;
394 
AreaImpl()395         public AreaImpl() {
396         }
397 
AreaImpl(Area a)398         public AreaImpl(Area a) {
399             theArea = a;
400         }
401 
addRect(int lox, int loy, int hix, int hiy)402         public void addRect(int lox, int loy, int hix, int hiy) {
403             Area a2 = new Area(new Rectangle(lox, loy, hix-lox, hiy-loy));
404             if (theArea == null) {
405                 theArea = a2;
406             } else {
407                 theArea.add(a2);
408             }
409         }
410 
getTranslation(int dx, int dy)411         public RectListImpl getTranslation(int dx, int dy) {
412             AffineTransform at = AffineTransform.getTranslateInstance(dx, dy);
413             return new AreaImpl(theArea.createTransformedArea(at));
414         }
415 
getIntersection(RectListImpl rli)416         public RectListImpl getIntersection(RectListImpl rli) {
417             Area a2 = new Area(theArea);
418             a2.intersect(((AreaImpl) rli).theArea);
419             return new AreaImpl(a2);
420         }
421 
getExclusiveOr(RectListImpl rli)422         public RectListImpl getExclusiveOr(RectListImpl rli) {
423             Area a2 = new Area(theArea);
424             a2.exclusiveOr(((AreaImpl) rli).theArea);
425             return new AreaImpl(a2);
426         }
427 
getDifference(RectListImpl rli)428         public RectListImpl getDifference(RectListImpl rli) {
429             Area a2 = new Area(theArea);
430             a2.subtract(((AreaImpl) rli).theArea);
431             return new AreaImpl(a2);
432         }
433 
getUnion(RectListImpl rli)434         public RectListImpl getUnion(RectListImpl rli) {
435             Area a2 = new Area(theArea);
436             a2.add(((AreaImpl) rli).theArea);
437             return new AreaImpl(a2);
438         }
439 
440         // Used for making sure that 3xMAX translates yields an empty region
checkTransEmpty()441         public boolean checkTransEmpty() {
442             // Area objects will actually survive 3 MAX translates so just
443             // pretend that it had the intended effect...
444             return true;
445         }
446 
contains(int x, int y)447         public boolean contains(int x, int y) {
448             return theArea.contains(x, y);
449         }
450 
hashCode()451         public int hashCode() {
452             // Area does not override hashCode...
453             return 0;
454         }
455 
equals(RectListImpl other)456         public boolean equals(RectListImpl other) {
457             return theArea.equals(((AreaImpl) other).theArea);
458         }
459 
toString()460         public String toString() {
461             return theArea.toString();
462         }
463     }
464 
465     public static class RegionImpl extends RectListImpl {
466         Region theRegion;
467 
RegionImpl()468         public RegionImpl() {
469         }
470 
RegionImpl(Region r)471         public RegionImpl(Region r) {
472             theRegion = r;
473         }
474 
addRect(int lox, int loy, int hix, int hiy)475         public void addRect(int lox, int loy, int hix, int hiy) {
476             Region r2 = Region.getInstanceXYXY(lox, loy, hix, hiy);
477             if (theRegion == null) {
478                 theRegion = r2;
479             } else {
480                 theRegion = theRegion.getUnion(r2);
481             }
482         }
483 
getTranslation(int dx, int dy)484         public RectListImpl getTranslation(int dx, int dy) {
485             return new RegionImpl(theRegion.getTranslatedRegion(dx, dy));
486         }
487 
getIntersection(RectListImpl rli)488         public RectListImpl getIntersection(RectListImpl rli) {
489             Region r2 = ((RegionImpl) rli).theRegion;
490             r2 = theRegion.getIntersection(r2);
491             return new RegionImpl(r2);
492         }
493 
getExclusiveOr(RectListImpl rli)494         public RectListImpl getExclusiveOr(RectListImpl rli) {
495             Region r2 = ((RegionImpl) rli).theRegion;
496             r2 = theRegion.getExclusiveOr(r2);
497             return new RegionImpl(r2);
498         }
499 
getDifference(RectListImpl rli)500         public RectListImpl getDifference(RectListImpl rli) {
501             Region r2 = ((RegionImpl) rli).theRegion;
502             r2 = theRegion.getDifference(r2);
503             return new RegionImpl(r2);
504         }
505 
getUnion(RectListImpl rli)506         public RectListImpl getUnion(RectListImpl rli) {
507             Region r2 = ((RegionImpl) rli).theRegion;
508             r2 = theRegion.getUnion(r2);
509             return new RegionImpl(r2);
510         }
511 
512         // Used for making sure that 3xMAX translates yields an empty region
checkTransEmpty()513         public boolean checkTransEmpty() {
514             // Region objects should be empty after 3 MAX translates...
515             return theRegion.isEmpty();
516         }
517 
contains(int x, int y)518         public boolean contains(int x, int y) {
519             return theRegion.contains(x, y);
520         }
521 
hashCode()522         public int hashCode() {
523             return theRegion.hashCode();
524         }
525 
equals(RectListImpl other)526         public boolean equals(RectListImpl other) {
527             return theRegion.equals(((RegionImpl) other).theRegion);
528         }
529 
toString()530         public String toString() {
531             return theRegion.toString();
532         }
533     }
534 }
535