Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/java2d/pipe/Region.java
38918 views
1
/*
2
* Copyright (c) 1998, 2013, 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. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
package sun.java2d.pipe;
27
28
import java.awt.Rectangle;
29
import java.awt.Shape;
30
import java.awt.geom.AffineTransform;
31
import java.awt.geom.RectangularShape;
32
33
import sun.java2d.loops.TransformHelper;
34
35
/**
36
* This class encapsulates a definition of a two dimensional region which
37
* consists of a number of Y ranges each containing multiple X bands.
38
* <p>
39
* A rectangular Region is allowed to have a null band list in which
40
* case the rectangular shape is defined by the bounding box parameters
41
* (lox, loy, hix, hiy).
42
* <p>
43
* The band list, if present, consists of a list of rows in ascending Y
44
* order, ending at endIndex which is the index beyond the end of the
45
* last row. Each row consists of at least 3 + 2n entries (n >= 1)
46
* where the first 3 entries specify the Y range as start, end, and
47
* the number of X ranges in that Y range. These 3 entries are
48
* followed by pairs of X coordinates in ascending order:
49
* <pre>
50
* bands[rowstart+0] = Y0; // starting Y coordinate
51
* bands[rowstart+1] = Y1; // ending Y coordinate - endY > startY
52
* bands[rowstart+2] = N; // number of X bands - N >= 1
53
*
54
* bands[rowstart+3] = X10; // starting X coordinate of first band
55
* bands[rowstart+4] = X11; // ending X coordinate of first band
56
* bands[rowstart+5] = X20; // starting X coordinate of second band
57
* bands[rowstart+6] = X21; // ending X coordinate of second band
58
* ...
59
* bands[rowstart+3+N*2-2] = XN0; // starting X coord of last band
60
* bands[rowstart+3+N*2-1] = XN1; // ending X coord of last band
61
*
62
* bands[rowstart+3+N*2] = ... // start of next Y row
63
* </pre>
64
*/
65
public class Region {
66
static final int INIT_SIZE = 50;
67
static final int GROW_SIZE = 50;
68
69
/**
70
* Immutable Region.
71
*/
72
private static final class ImmutableRegion extends Region {
73
protected ImmutableRegion(int lox, int loy, int hix, int hiy) {
74
super(lox, loy, hix, hiy);
75
}
76
77
// Override all the methods that mutate the object
78
public void appendSpans(sun.java2d.pipe.SpanIterator si) {}
79
public void setOutputArea(java.awt.Rectangle r) {}
80
public void setOutputAreaXYWH(int x, int y, int w, int h) {}
81
public void setOutputArea(int[] box) {}
82
public void setOutputAreaXYXY(int lox, int loy, int hix, int hiy) {}
83
}
84
85
public static final Region EMPTY_REGION = new ImmutableRegion(0, 0, 0, 0);
86
public static final Region WHOLE_REGION = new ImmutableRegion(
87
Integer.MIN_VALUE,
88
Integer.MIN_VALUE,
89
Integer.MAX_VALUE,
90
Integer.MAX_VALUE);
91
92
int lox;
93
int loy;
94
int hix;
95
int hiy;
96
97
int endIndex;
98
int[] bands;
99
100
private static native void initIDs();
101
102
static {
103
initIDs();
104
}
105
106
/**
107
* Adds the dimension <code>dim</code> to the coordinate
108
* <code>start</code> with appropriate clipping. If
109
* <code>dim</code> is non-positive then the method returns
110
* the start coordinate. If the sum overflows an integer
111
* data type then the method returns <code>Integer.MAX_VALUE</code>.
112
*/
113
public static int dimAdd(int start, int dim) {
114
if (dim <= 0) return start;
115
if ((dim += start) < start) return Integer.MAX_VALUE;
116
return dim;
117
}
118
119
/**
120
* Adds the delta {@code dv} to the value {@code v} with
121
* appropriate clipping to the bounds of Integer resolution.
122
* If the answer would be greater than {@code Integer.MAX_VALUE}
123
* then {@code Integer.MAX_VALUE} is returned.
124
* If the answer would be less than {@code Integer.MIN_VALUE}
125
* then {@code Integer.MIN_VALUE} is returned.
126
* Otherwise the sum is returned.
127
*/
128
public static int clipAdd(int v, int dv) {
129
int newv = v + dv;
130
if ((newv > v) != (dv > 0)) {
131
newv = (dv < 0) ? Integer.MIN_VALUE : Integer.MAX_VALUE;
132
}
133
return newv;
134
}
135
136
/**
137
* Multiply the scale factor {@code sv} and the value {@code v} with
138
* appropriate clipping to the bounds of Integer resolution. If the answer
139
* would be greater than {@code Integer.MAX_VALUE} then {@code
140
* Integer.MAX_VALUE} is returned. If the answer would be less than {@code
141
* Integer.MIN_VALUE} then {@code Integer.MIN_VALUE} is returned. Otherwise
142
* the multiplication is returned.
143
*/
144
public static int clipScale(final int v, final double sv) {
145
if (sv == 1.0) {
146
return v;
147
}
148
final double newv = v * sv;
149
if (newv < Integer.MIN_VALUE) {
150
return Integer.MIN_VALUE;
151
}
152
if (newv > Integer.MAX_VALUE) {
153
return Integer.MAX_VALUE;
154
}
155
return (int) Math.round(newv);
156
}
157
158
protected Region(int lox, int loy, int hix, int hiy) {
159
this.lox = lox;
160
this.loy = loy;
161
this.hix = hix;
162
this.hiy = hiy;
163
}
164
165
private Region(int lox, int loy, int hix, int hiy, int[] bands, int end) {
166
this.lox = lox;
167
this.loy = loy;
168
this.hix = hix;
169
this.hiy = hiy;
170
this.bands = bands;
171
this.endIndex = end;
172
}
173
174
/**
175
* Returns a Region object covering the pixels which would be
176
* touched by a fill or clip operation on a Graphics implementation
177
* on the specified Shape object under the optionally specified
178
* AffineTransform object.
179
*
180
* @param s a non-null Shape object specifying the geometry enclosing
181
* the pixels of interest
182
* @param at an optional <code>AffineTransform</code> to be applied to the
183
* coordinates as they are returned in the iteration, or
184
* <code>null</code> if untransformed coordinates are desired
185
*/
186
public static Region getInstance(Shape s, AffineTransform at) {
187
return getInstance(WHOLE_REGION, false, s, at);
188
}
189
190
/**
191
* Returns a Region object covering the pixels which would be
192
* touched by a fill or clip operation on a Graphics implementation
193
* on the specified Shape object under the optionally specified
194
* AffineTransform object further restricted by the specified
195
* device bounds.
196
* <p>
197
* Note that only the bounds of the specified Region are used to
198
* restrict the resulting Region.
199
* If devBounds is non-rectangular and clipping to the specific
200
* bands of devBounds is needed, then an intersection of the
201
* resulting Region with devBounds must be performed in a
202
* subsequent step.
203
*
204
* @param devBounds a non-null Region specifying some bounds to
205
* clip the geometry to
206
* @param s a non-null Shape object specifying the geometry enclosing
207
* the pixels of interest
208
* @param at an optional <code>AffineTransform</code> to be applied to the
209
* coordinates as they are returned in the iteration, or
210
* <code>null</code> if untransformed coordinates are desired
211
*/
212
public static Region getInstance(Region devBounds,
213
Shape s, AffineTransform at)
214
{
215
return getInstance(devBounds, false, s, at);
216
}
217
218
/**
219
* Returns a Region object covering the pixels which would be
220
* touched by a fill or clip operation on a Graphics implementation
221
* on the specified Shape object under the optionally specified
222
* AffineTransform object further restricted by the specified
223
* device bounds.
224
* If the normalize parameter is true then coordinate normalization
225
* is performed as per the 2D Graphics non-antialiasing implementation
226
* of the VALUE_STROKE_NORMALIZE hint.
227
* <p>
228
* Note that only the bounds of the specified Region are used to
229
* restrict the resulting Region.
230
* If devBounds is non-rectangular and clipping to the specific
231
* bands of devBounds is needed, then an intersection of the
232
* resulting Region with devBounds must be performed in a
233
* subsequent step.
234
*
235
* @param devBounds a non-null Region specifying some bounds to
236
* clip the geometry to
237
* @param normalize a boolean indicating whether or not to apply
238
* normalization
239
* @param s a non-null Shape object specifying the geometry enclosing
240
* the pixels of interest
241
* @param at an optional <code>AffineTransform</code> to be applied to the
242
* coordinates as they are returned in the iteration, or
243
* <code>null</code> if untransformed coordinates are desired
244
*/
245
public static Region getInstance(Region devBounds, boolean normalize,
246
Shape s, AffineTransform at)
247
{
248
// Optimize for empty shapes to avoid involving the SpanIterator
249
if (s instanceof RectangularShape &&
250
((RectangularShape)s).isEmpty())
251
{
252
return EMPTY_REGION;
253
}
254
255
int box[] = new int[4];
256
ShapeSpanIterator sr = new ShapeSpanIterator(normalize);
257
try {
258
sr.setOutputArea(devBounds);
259
sr.appendPath(s.getPathIterator(at));
260
sr.getPathBox(box);
261
Region r = Region.getInstance(box);
262
r.appendSpans(sr);
263
return r;
264
} finally {
265
sr.dispose();
266
}
267
}
268
269
/**
270
* Returns a Region object with a rectangle of interest specified by the
271
* indicated rectangular area in lox, loy, hix, hiy and edges array, which
272
* is located relative to the rectangular area. Edges array - 0,1 are y
273
* range, 2N,2N+1 are x ranges, 1 per y range.
274
*
275
* @see TransformHelper
276
*/
277
static Region getInstance(final int lox, final int loy, final int hix,
278
final int hiy, final int[] edges) {
279
final int y1 = edges[0];
280
final int y2 = edges[1];
281
if (hiy <= loy || hix <= lox || y2 <= y1) {
282
return EMPTY_REGION;
283
}
284
// rowsNum * (3 + 1 * 2)
285
final int[] bands = new int[(y2 - y1) * 5];
286
int end = 0;
287
int index = 2;
288
for (int y = y1; y < y2; ++y) {
289
final int spanlox = Math.max(clipAdd(lox, edges[index++]), lox);
290
final int spanhix = Math.min(clipAdd(lox, edges[index++]), hix);
291
if (spanlox < spanhix) {
292
final int spanloy = Math.max(clipAdd(loy, y), loy);
293
final int spanhiy = Math.min(clipAdd(spanloy, 1), hiy);
294
if (spanloy < spanhiy) {
295
bands[end++] = spanloy;
296
bands[end++] = spanhiy;
297
bands[end++] = 1; // 1 span per row
298
bands[end++] = spanlox;
299
bands[end++] = spanhix;
300
}
301
}
302
}
303
return end != 0 ? new Region(lox, loy, hix, hiy, bands, end)
304
: EMPTY_REGION;
305
}
306
307
/**
308
* Returns a Region object with a rectangle of interest specified
309
* by the indicated Rectangle object.
310
* <p>
311
* This method can also be used to create a simple rectangular
312
* region.
313
*/
314
public static Region getInstance(Rectangle r) {
315
return Region.getInstanceXYWH(r.x, r.y, r.width, r.height);
316
}
317
318
/**
319
* Returns a Region object with a rectangle of interest specified
320
* by the indicated rectangular area in x, y, width, height format.
321
* <p>
322
* This method can also be used to create a simple rectangular
323
* region.
324
*/
325
public static Region getInstanceXYWH(int x, int y, int w, int h) {
326
return Region.getInstanceXYXY(x, y, dimAdd(x, w), dimAdd(y, h));
327
}
328
329
/**
330
* Returns a Region object with a rectangle of interest specified
331
* by the indicated span array.
332
* <p>
333
* This method can also be used to create a simple rectangular
334
* region.
335
*/
336
public static Region getInstance(int box[]) {
337
return new Region(box[0], box[1], box[2], box[3]);
338
}
339
340
/**
341
* Returns a Region object with a rectangle of interest specified
342
* by the indicated rectangular area in lox, loy, hix, hiy format.
343
* <p>
344
* This method can also be used to create a simple rectangular
345
* region.
346
*/
347
public static Region getInstanceXYXY(int lox, int loy, int hix, int hiy) {
348
return new Region(lox, loy, hix, hiy);
349
}
350
351
/**
352
* Sets the rectangle of interest for storing and returning
353
* region bands.
354
* <p>
355
* This method can also be used to initialize a simple rectangular
356
* region.
357
*/
358
public void setOutputArea(Rectangle r) {
359
setOutputAreaXYWH(r.x, r.y, r.width, r.height);
360
}
361
362
/**
363
* Sets the rectangle of interest for storing and returning
364
* region bands. The rectangle is specified in x, y, width, height
365
* format and appropriate clipping is performed as per the method
366
* <code>dimAdd</code>.
367
* <p>
368
* This method can also be used to initialize a simple rectangular
369
* region.
370
*/
371
public void setOutputAreaXYWH(int x, int y, int w, int h) {
372
setOutputAreaXYXY(x, y, dimAdd(x, w), dimAdd(y, h));
373
}
374
375
/**
376
* Sets the rectangle of interest for storing and returning
377
* region bands. The rectangle is specified as a span array.
378
* <p>
379
* This method can also be used to initialize a simple rectangular
380
* region.
381
*/
382
public void setOutputArea(int box[]) {
383
this.lox = box[0];
384
this.loy = box[1];
385
this.hix = box[2];
386
this.hiy = box[3];
387
}
388
389
/**
390
* Sets the rectangle of interest for storing and returning
391
* region bands. The rectangle is specified in lox, loy,
392
* hix, hiy format.
393
* <p>
394
* This method can also be used to initialize a simple rectangular
395
* region.
396
*/
397
public void setOutputAreaXYXY(int lox, int loy, int hix, int hiy) {
398
this.lox = lox;
399
this.loy = loy;
400
this.hix = hix;
401
this.hiy = hiy;
402
}
403
404
/**
405
* Appends the list of spans returned from the indicated
406
* SpanIterator. Each span must be at a higher starting
407
* Y coordinate than the previous data or it must have a
408
* Y range equal to the highest Y band in the region and a
409
* higher X coordinate than any of the spans in that band.
410
*/
411
public void appendSpans(SpanIterator si) {
412
int[] box = new int[6];
413
414
while (si.nextSpan(box)) {
415
appendSpan(box);
416
}
417
418
endRow(box);
419
calcBBox();
420
}
421
422
/**
423
* Returns a Region object that represents the same list of rectangles as
424
* the current Region object, scaled by the specified sx, sy factors.
425
*/
426
public Region getScaledRegion(final double sx, final double sy) {
427
if (sx == 0 || sy == 0 || this == EMPTY_REGION) {
428
return EMPTY_REGION;
429
}
430
if ((sx == 1.0 && sy == 1.0) || (this == WHOLE_REGION)) {
431
return this;
432
}
433
434
int tlox = clipScale(lox, sx);
435
int tloy = clipScale(loy, sy);
436
int thix = clipScale(hix, sx);
437
int thiy = clipScale(hiy, sy);
438
Region ret = new Region(tlox, tloy, thix, thiy);
439
int bands[] = this.bands;
440
if (bands != null) {
441
int end = endIndex;
442
int newbands[] = new int[end];
443
int i = 0; // index for source bands
444
int j = 0; // index for translated newbands
445
int ncol;
446
while (i < end) {
447
int y1, y2;
448
newbands[j++] = y1 = clipScale(bands[i++], sy);
449
newbands[j++] = y2 = clipScale(bands[i++], sy);
450
newbands[j++] = ncol = bands[i++];
451
int savej = j;
452
if (y1 < y2) {
453
while (--ncol >= 0) {
454
int x1 = clipScale(bands[i++], sx);
455
int x2 = clipScale(bands[i++], sx);
456
if (x1 < x2) {
457
newbands[j++] = x1;
458
newbands[j++] = x2;
459
}
460
}
461
} else {
462
i += ncol * 2;
463
}
464
// Did we get any non-empty bands in this row?
465
if (j > savej) {
466
newbands[savej-1] = (j - savej) / 2;
467
} else {
468
j = savej - 3;
469
}
470
}
471
if (j <= 5) {
472
if (j < 5) {
473
// No rows or bands were generated...
474
ret.lox = ret.loy = ret.hix = ret.hiy = 0;
475
} else {
476
// Only generated one single rect in the end...
477
ret.loy = newbands[0];
478
ret.hiy = newbands[1];
479
ret.lox = newbands[3];
480
ret.hix = newbands[4];
481
}
482
// ret.endIndex and ret.bands were never initialized...
483
// ret.endIndex = 0;
484
// ret.newbands = null;
485
} else {
486
// Generated multiple bands and/or multiple rows...
487
ret.endIndex = j;
488
ret.bands = newbands;
489
}
490
}
491
return ret;
492
}
493
494
495
/**
496
* Returns a Region object that represents the same list of
497
* rectangles as the current Region object, translated by
498
* the specified dx, dy translation factors.
499
*/
500
public Region getTranslatedRegion(int dx, int dy) {
501
if ((dx | dy) == 0) {
502
return this;
503
}
504
int tlox = lox + dx;
505
int tloy = loy + dy;
506
int thix = hix + dx;
507
int thiy = hiy + dy;
508
if ((tlox > lox) != (dx > 0) ||
509
(tloy > loy) != (dy > 0) ||
510
(thix > hix) != (dx > 0) ||
511
(thiy > hiy) != (dy > 0))
512
{
513
return getSafeTranslatedRegion(dx, dy);
514
}
515
Region ret = new Region(tlox, tloy, thix, thiy);
516
int bands[] = this.bands;
517
if (bands != null) {
518
int end = endIndex;
519
ret.endIndex = end;
520
int newbands[] = new int[end];
521
ret.bands = newbands;
522
int i = 0;
523
int ncol;
524
while (i < end) {
525
newbands[i] = bands[i] + dy; i++;
526
newbands[i] = bands[i] + dy; i++;
527
newbands[i] = ncol = bands[i]; i++;
528
while (--ncol >= 0) {
529
newbands[i] = bands[i] + dx; i++;
530
newbands[i] = bands[i] + dx; i++;
531
}
532
}
533
}
534
return ret;
535
}
536
537
private Region getSafeTranslatedRegion(int dx, int dy) {
538
int tlox = clipAdd(lox, dx);
539
int tloy = clipAdd(loy, dy);
540
int thix = clipAdd(hix, dx);
541
int thiy = clipAdd(hiy, dy);
542
Region ret = new Region(tlox, tloy, thix, thiy);
543
int bands[] = this.bands;
544
if (bands != null) {
545
int end = endIndex;
546
int newbands[] = new int[end];
547
int i = 0; // index for source bands
548
int j = 0; // index for translated newbands
549
int ncol;
550
while (i < end) {
551
int y1, y2;
552
newbands[j++] = y1 = clipAdd(bands[i++], dy);
553
newbands[j++] = y2 = clipAdd(bands[i++], dy);
554
newbands[j++] = ncol = bands[i++];
555
int savej = j;
556
if (y1 < y2) {
557
while (--ncol >= 0) {
558
int x1 = clipAdd(bands[i++], dx);
559
int x2 = clipAdd(bands[i++], dx);
560
if (x1 < x2) {
561
newbands[j++] = x1;
562
newbands[j++] = x2;
563
}
564
}
565
} else {
566
i += ncol * 2;
567
}
568
// Did we get any non-empty bands in this row?
569
if (j > savej) {
570
newbands[savej-1] = (j - savej) / 2;
571
} else {
572
j = savej - 3;
573
}
574
}
575
if (j <= 5) {
576
if (j < 5) {
577
// No rows or bands were generated...
578
ret.lox = ret.loy = ret.hix = ret.hiy = 0;
579
} else {
580
// Only generated one single rect in the end...
581
ret.loy = newbands[0];
582
ret.hiy = newbands[1];
583
ret.lox = newbands[3];
584
ret.hix = newbands[4];
585
}
586
// ret.endIndex and ret.bands were never initialized...
587
// ret.endIndex = 0;
588
// ret.newbands = null;
589
} else {
590
// Generated multiple bands and/or multiple rows...
591
ret.endIndex = j;
592
ret.bands = newbands;
593
}
594
}
595
return ret;
596
}
597
598
/**
599
* Returns a Region object that represents the intersection of
600
* this object with the specified Rectangle. The return value
601
* may be this same object if no clipping occurs.
602
*/
603
public Region getIntersection(Rectangle r) {
604
return getIntersectionXYWH(r.x, r.y, r.width, r.height);
605
}
606
607
/**
608
* Returns a Region object that represents the intersection of
609
* this object with the specified rectangular area. The return
610
* value may be this same object if no clipping occurs.
611
*/
612
public Region getIntersectionXYWH(int x, int y, int w, int h) {
613
return getIntersectionXYXY(x, y, dimAdd(x, w), dimAdd(y, h));
614
}
615
616
/**
617
* Returns a Region object that represents the intersection of
618
* this object with the specified rectangular area. The return
619
* value may be this same object if no clipping occurs.
620
*/
621
public Region getIntersectionXYXY(int lox, int loy, int hix, int hiy) {
622
if (isInsideXYXY(lox, loy, hix, hiy)) {
623
return this;
624
}
625
Region ret = new Region((lox < this.lox) ? this.lox : lox,
626
(loy < this.loy) ? this.loy : loy,
627
(hix > this.hix) ? this.hix : hix,
628
(hiy > this.hiy) ? this.hiy : hiy);
629
if (bands != null) {
630
ret.appendSpans(this.getSpanIterator());
631
}
632
return ret;
633
}
634
635
/**
636
* Returns a Region object that represents the intersection of this
637
* object with the specified Region object.
638
* <p>
639
* If {@code A} and {@code B} are both Region Objects and
640
* <code>C = A.getIntersection(B);</code> then a point will
641
* be contained in {@code C} iff it is contained in both
642
* {@code A} and {@code B}.
643
* <p>
644
* The return value may be this same object or the argument
645
* Region object if no clipping occurs.
646
*/
647
public Region getIntersection(Region r) {
648
if (this.isInsideQuickCheck(r)) {
649
return this;
650
}
651
if (r.isInsideQuickCheck(this)) {
652
return r;
653
}
654
Region ret = new Region((r.lox < this.lox) ? this.lox : r.lox,
655
(r.loy < this.loy) ? this.loy : r.loy,
656
(r.hix > this.hix) ? this.hix : r.hix,
657
(r.hiy > this.hiy) ? this.hiy : r.hiy);
658
if (!ret.isEmpty()) {
659
ret.filterSpans(this, r, INCLUDE_COMMON);
660
}
661
return ret;
662
}
663
664
/**
665
* Returns a Region object that represents the union of this
666
* object with the specified Region object.
667
* <p>
668
* If {@code A} and {@code B} are both Region Objects and
669
* <code>C = A.getUnion(B);</code> then a point will
670
* be contained in {@code C} iff it is contained in either
671
* {@code A} or {@code B}.
672
* <p>
673
* The return value may be this same object or the argument
674
* Region object if no augmentation occurs.
675
*/
676
public Region getUnion(Region r) {
677
if (r.isEmpty() || r.isInsideQuickCheck(this)) {
678
return this;
679
}
680
if (this.isEmpty() || this.isInsideQuickCheck(r)) {
681
return r;
682
}
683
Region ret = new Region((r.lox > this.lox) ? this.lox : r.lox,
684
(r.loy > this.loy) ? this.loy : r.loy,
685
(r.hix < this.hix) ? this.hix : r.hix,
686
(r.hiy < this.hiy) ? this.hiy : r.hiy);
687
ret.filterSpans(this, r, INCLUDE_A | INCLUDE_B | INCLUDE_COMMON);
688
return ret;
689
}
690
691
/**
692
* Returns a Region object that represents the difference of the
693
* specified Region object subtracted from this object.
694
* <p>
695
* If {@code A} and {@code B} are both Region Objects and
696
* <code>C = A.getDifference(B);</code> then a point will
697
* be contained in {@code C} iff it is contained in
698
* {@code A} but not contained in {@code B}.
699
* <p>
700
* The return value may be this same object or the argument
701
* Region object if no clipping occurs.
702
*/
703
public Region getDifference(Region r) {
704
if (!r.intersectsQuickCheck(this)) {
705
return this;
706
}
707
if (this.isInsideQuickCheck(r)) {
708
return EMPTY_REGION;
709
}
710
Region ret = new Region(this.lox, this.loy, this.hix, this.hiy);
711
ret.filterSpans(this, r, INCLUDE_A);
712
return ret;
713
}
714
715
/**
716
* Returns a Region object that represents the exclusive or of this
717
* object with the specified Region object.
718
* <p>
719
* If {@code A} and {@code B} are both Region Objects and
720
* <code>C = A.getExclusiveOr(B);</code> then a point will
721
* be contained in {@code C} iff it is contained in either
722
* {@code A} or {@code B}, but not if it is contained in both.
723
* <p>
724
* The return value may be this same object or the argument
725
* Region object if either is empty.
726
*/
727
public Region getExclusiveOr(Region r) {
728
if (r.isEmpty()) {
729
return this;
730
}
731
if (this.isEmpty()) {
732
return r;
733
}
734
Region ret = new Region((r.lox > this.lox) ? this.lox : r.lox,
735
(r.loy > this.loy) ? this.loy : r.loy,
736
(r.hix < this.hix) ? this.hix : r.hix,
737
(r.hiy < this.hiy) ? this.hiy : r.hiy);
738
ret.filterSpans(this, r, INCLUDE_A | INCLUDE_B);
739
return ret;
740
}
741
742
static final int INCLUDE_A = 1;
743
static final int INCLUDE_B = 2;
744
static final int INCLUDE_COMMON = 4;
745
746
private void filterSpans(Region ra, Region rb, int flags) {
747
int abands[] = ra.bands;
748
int bbands[] = rb.bands;
749
if (abands == null) {
750
abands = new int[] {ra.loy, ra.hiy, 1, ra.lox, ra.hix};
751
}
752
if (bbands == null) {
753
bbands = new int[] {rb.loy, rb.hiy, 1, rb.lox, rb.hix};
754
}
755
int box[] = new int[6];
756
int acolstart = 0;
757
int ay1 = abands[acolstart++];
758
int ay2 = abands[acolstart++];
759
int acolend = abands[acolstart++];
760
acolend = acolstart + 2 * acolend;
761
int bcolstart = 0;
762
int by1 = bbands[bcolstart++];
763
int by2 = bbands[bcolstart++];
764
int bcolend = bbands[bcolstart++];
765
bcolend = bcolstart + 2 * bcolend;
766
int y = loy;
767
while (y < hiy) {
768
if (y >= ay2) {
769
if (acolend < ra.endIndex) {
770
acolstart = acolend;
771
ay1 = abands[acolstart++];
772
ay2 = abands[acolstart++];
773
acolend = abands[acolstart++];
774
acolend = acolstart + 2 * acolend;
775
} else {
776
if ((flags & INCLUDE_B) == 0) break;
777
ay1 = ay2 = hiy;
778
}
779
continue;
780
}
781
if (y >= by2) {
782
if (bcolend < rb.endIndex) {
783
bcolstart = bcolend;
784
by1 = bbands[bcolstart++];
785
by2 = bbands[bcolstart++];
786
bcolend = bbands[bcolstart++];
787
bcolend = bcolstart + 2 * bcolend;
788
} else {
789
if ((flags & INCLUDE_A) == 0) break;
790
by1 = by2 = hiy;
791
}
792
continue;
793
}
794
int yend;
795
if (y < by1) {
796
if (y < ay1) {
797
y = Math.min(ay1, by1);
798
continue;
799
}
800
// We are in a set of rows that belong only to A
801
yend = Math.min(ay2, by1);
802
if ((flags & INCLUDE_A) != 0) {
803
box[1] = y;
804
box[3] = yend;
805
int acol = acolstart;
806
while (acol < acolend) {
807
box[0] = abands[acol++];
808
box[2] = abands[acol++];
809
appendSpan(box);
810
}
811
}
812
} else if (y < ay1) {
813
// We are in a set of rows that belong only to B
814
yend = Math.min(by2, ay1);
815
if ((flags & INCLUDE_B) != 0) {
816
box[1] = y;
817
box[3] = yend;
818
int bcol = bcolstart;
819
while (bcol < bcolend) {
820
box[0] = bbands[bcol++];
821
box[2] = bbands[bcol++];
822
appendSpan(box);
823
}
824
}
825
} else {
826
// We are in a set of rows that belong to both A and B
827
yend = Math.min(ay2, by2);
828
box[1] = y;
829
box[3] = yend;
830
int acol = acolstart;
831
int bcol = bcolstart;
832
int ax1 = abands[acol++];
833
int ax2 = abands[acol++];
834
int bx1 = bbands[bcol++];
835
int bx2 = bbands[bcol++];
836
int x = Math.min(ax1, bx1);
837
if (x < lox) x = lox;
838
while (x < hix) {
839
if (x >= ax2) {
840
if (acol < acolend) {
841
ax1 = abands[acol++];
842
ax2 = abands[acol++];
843
} else {
844
if ((flags & INCLUDE_B) == 0) break;
845
ax1 = ax2 = hix;
846
}
847
continue;
848
}
849
if (x >= bx2) {
850
if (bcol < bcolend) {
851
bx1 = bbands[bcol++];
852
bx2 = bbands[bcol++];
853
} else {
854
if ((flags & INCLUDE_A) == 0) break;
855
bx1 = bx2 = hix;
856
}
857
continue;
858
}
859
int xend;
860
boolean appendit;
861
if (x < bx1) {
862
if (x < ax1) {
863
xend = Math.min(ax1, bx1);
864
appendit = false;
865
} else {
866
xend = Math.min(ax2, bx1);
867
appendit = ((flags & INCLUDE_A) != 0);
868
}
869
} else if (x < ax1) {
870
xend = Math.min(ax1, bx2);
871
appendit = ((flags & INCLUDE_B) != 0);
872
} else {
873
xend = Math.min(ax2, bx2);
874
appendit = ((flags & INCLUDE_COMMON) != 0);
875
}
876
if (appendit) {
877
box[0] = x;
878
box[2] = xend;
879
appendSpan(box);
880
}
881
x = xend;
882
}
883
}
884
y = yend;
885
}
886
endRow(box);
887
calcBBox();
888
}
889
890
/**
891
* Returns a Region object that represents the bounds of the
892
* intersection of this object with the bounds of the specified
893
* Region object.
894
* <p>
895
* The return value may be this same object if no clipping occurs
896
* and this Region is rectangular.
897
*/
898
public Region getBoundsIntersection(Rectangle r) {
899
return getBoundsIntersectionXYWH(r.x, r.y, r.width, r.height);
900
}
901
902
/**
903
* Returns a Region object that represents the bounds of the
904
* intersection of this object with the bounds of the specified
905
* rectangular area in x, y, width, height format.
906
* <p>
907
* The return value may be this same object if no clipping occurs
908
* and this Region is rectangular.
909
*/
910
public Region getBoundsIntersectionXYWH(int x, int y, int w, int h) {
911
return getBoundsIntersectionXYXY(x, y, dimAdd(x, w), dimAdd(y, h));
912
}
913
914
/**
915
* Returns a Region object that represents the bounds of the
916
* intersection of this object with the bounds of the specified
917
* rectangular area in lox, loy, hix, hiy format.
918
* <p>
919
* The return value may be this same object if no clipping occurs
920
* and this Region is rectangular.
921
*/
922
public Region getBoundsIntersectionXYXY(int lox, int loy,
923
int hix, int hiy)
924
{
925
if (this.bands == null &&
926
this.lox >= lox && this.loy >= loy &&
927
this.hix <= hix && this.hiy <= hiy)
928
{
929
return this;
930
}
931
return new Region((lox < this.lox) ? this.lox : lox,
932
(loy < this.loy) ? this.loy : loy,
933
(hix > this.hix) ? this.hix : hix,
934
(hiy > this.hiy) ? this.hiy : hiy);
935
}
936
937
/**
938
* Returns a Region object that represents the intersection of
939
* this object with the bounds of the specified Region object.
940
* <p>
941
* The return value may be this same object or the argument
942
* Region object if no clipping occurs and the Regions are
943
* rectangular.
944
*/
945
public Region getBoundsIntersection(Region r) {
946
if (this.encompasses(r)) {
947
return r;
948
}
949
if (r.encompasses(this)) {
950
return this;
951
}
952
return new Region((r.lox < this.lox) ? this.lox : r.lox,
953
(r.loy < this.loy) ? this.loy : r.loy,
954
(r.hix > this.hix) ? this.hix : r.hix,
955
(r.hiy > this.hiy) ? this.hiy : r.hiy);
956
}
957
958
/**
959
* Appends a single span defined by the 4 parameters
960
* spanlox, spanloy, spanhix, spanhiy.
961
* This span must be at a higher starting Y coordinate than
962
* the previous data or it must have a Y range equal to the
963
* highest Y band in the region and a higher X coordinate
964
* than any of the spans in that band.
965
*/
966
private void appendSpan(int box[]) {
967
int spanlox, spanloy, spanhix, spanhiy;
968
if ((spanlox = box[0]) < lox) spanlox = lox;
969
if ((spanloy = box[1]) < loy) spanloy = loy;
970
if ((spanhix = box[2]) > hix) spanhix = hix;
971
if ((spanhiy = box[3]) > hiy) spanhiy = hiy;
972
if (spanhix <= spanlox || spanhiy <= spanloy) {
973
return;
974
}
975
976
int curYrow = box[4];
977
if (endIndex == 0 || spanloy >= bands[curYrow + 1]) {
978
if (bands == null) {
979
bands = new int[INIT_SIZE];
980
} else {
981
needSpace(5);
982
endRow(box);
983
curYrow = box[4];
984
}
985
bands[endIndex++] = spanloy;
986
bands[endIndex++] = spanhiy;
987
bands[endIndex++] = 0;
988
} else if (spanloy == bands[curYrow] &&
989
spanhiy == bands[curYrow + 1] &&
990
spanlox >= bands[endIndex - 1]) {
991
if (spanlox == bands[endIndex - 1]) {
992
bands[endIndex - 1] = spanhix;
993
return;
994
}
995
needSpace(2);
996
} else {
997
throw new InternalError("bad span");
998
}
999
bands[endIndex++] = spanlox;
1000
bands[endIndex++] = spanhix;
1001
bands[curYrow + 2]++;
1002
}
1003
1004
private void needSpace(int num) {
1005
if (endIndex + num >= bands.length) {
1006
int[] newbands = new int[bands.length + GROW_SIZE];
1007
System.arraycopy(bands, 0, newbands, 0, endIndex);
1008
bands = newbands;
1009
}
1010
}
1011
1012
private void endRow(int box[]) {
1013
int cur = box[4];
1014
int prev = box[5];
1015
if (cur > prev) {
1016
int[] bands = this.bands;
1017
if (bands[prev + 1] == bands[cur] &&
1018
bands[prev + 2] == bands[cur + 2])
1019
{
1020
int num = bands[cur + 2] * 2;
1021
cur += 3;
1022
prev += 3;
1023
while (num > 0) {
1024
if (bands[cur++] != bands[prev++]) {
1025
break;
1026
}
1027
num--;
1028
}
1029
if (num == 0) {
1030
// prev == box[4]
1031
bands[box[5] + 1] = bands[prev + 1];
1032
endIndex = prev;
1033
return;
1034
}
1035
}
1036
}
1037
box[5] = box[4];
1038
box[4] = endIndex;
1039
}
1040
1041
private void calcBBox() {
1042
int[] bands = this.bands;
1043
if (endIndex <= 5) {
1044
if (endIndex == 0) {
1045
lox = loy = hix = hiy = 0;
1046
} else {
1047
loy = bands[0];
1048
hiy = bands[1];
1049
lox = bands[3];
1050
hix = bands[4];
1051
endIndex = 0;
1052
}
1053
this.bands = null;
1054
return;
1055
}
1056
int lox = this.hix;
1057
int hix = this.lox;
1058
int hiyindex = 0;
1059
1060
int i = 0;
1061
while (i < endIndex) {
1062
hiyindex = i;
1063
int numbands = bands[i + 2];
1064
i += 3;
1065
if (lox > bands[i]) {
1066
lox = bands[i];
1067
}
1068
i += numbands * 2;
1069
if (hix < bands[i - 1]) {
1070
hix = bands[i - 1];
1071
}
1072
}
1073
1074
this.lox = lox;
1075
this.loy = bands[0];
1076
this.hix = hix;
1077
this.hiy = bands[hiyindex + 1];
1078
}
1079
1080
/**
1081
* Returns the lowest X coordinate in the Region.
1082
*/
1083
public final int getLoX() {
1084
return lox;
1085
}
1086
1087
/**
1088
* Returns the lowest Y coordinate in the Region.
1089
*/
1090
public final int getLoY() {
1091
return loy;
1092
}
1093
1094
/**
1095
* Returns the highest X coordinate in the Region.
1096
*/
1097
public final int getHiX() {
1098
return hix;
1099
}
1100
1101
/**
1102
* Returns the highest Y coordinate in the Region.
1103
*/
1104
public final int getHiY() {
1105
return hiy;
1106
}
1107
1108
/**
1109
* Returns the width of this Region clipped to the range (0 - MAX_INT).
1110
*/
1111
public final int getWidth() {
1112
if (hix < lox) return 0;
1113
int w;
1114
if ((w = hix - lox) < 0) {
1115
w = Integer.MAX_VALUE;
1116
}
1117
return w;
1118
}
1119
1120
/**
1121
* Returns the height of this Region clipped to the range (0 - MAX_INT).
1122
*/
1123
public final int getHeight() {
1124
if (hiy < loy) return 0;
1125
int h;
1126
if ((h = hiy - loy) < 0) {
1127
h = Integer.MAX_VALUE;
1128
}
1129
return h;
1130
}
1131
1132
/**
1133
* Returns true iff this Region encloses no area.
1134
*/
1135
public boolean isEmpty() {
1136
return (hix <= lox || hiy <= loy);
1137
}
1138
1139
/**
1140
* Returns true iff this Region represents a single simple
1141
* rectangular area.
1142
*/
1143
public boolean isRectangular() {
1144
return (bands == null);
1145
}
1146
1147
/**
1148
* Returns true iff this Region contains the specified coordinate.
1149
*/
1150
public boolean contains(int x, int y) {
1151
if (x < lox || x >= hix || y < loy || y >= hiy) return false;
1152
if (bands == null) return true;
1153
int i = 0;
1154
while (i < endIndex) {
1155
if (y < bands[i++]) {
1156
return false;
1157
}
1158
if (y >= bands[i++]) {
1159
int numspans = bands[i++];
1160
i += numspans * 2;
1161
} else {
1162
int end = bands[i++];
1163
end = i + end * 2;
1164
while (i < end) {
1165
if (x < bands[i++]) return false;
1166
if (x < bands[i++]) return true;
1167
}
1168
return false;
1169
}
1170
}
1171
return false;
1172
}
1173
1174
/**
1175
* Returns true iff this Region lies inside the indicated
1176
* rectangular area specified in x, y, width, height format
1177
* with appropriate clipping performed as per the dimAdd method.
1178
*/
1179
public boolean isInsideXYWH(int x, int y, int w, int h) {
1180
return isInsideXYXY(x, y, dimAdd(x, w), dimAdd(y, h));
1181
}
1182
1183
/**
1184
* Returns true iff this Region lies inside the indicated
1185
* rectangular area specified in lox, loy, hix, hiy format.
1186
*/
1187
public boolean isInsideXYXY(int lox, int loy, int hix, int hiy) {
1188
return (this.lox >= lox && this.loy >= loy &&
1189
this.hix <= hix && this.hiy <= hiy);
1190
1191
}
1192
1193
/**
1194
* Quickly checks if this Region lies inside the specified
1195
* Region object.
1196
* <p>
1197
* This method will return false if the specified Region
1198
* object is not a simple rectangle.
1199
*/
1200
public boolean isInsideQuickCheck(Region r) {
1201
return (r.bands == null &&
1202
r.lox <= this.lox && r.loy <= this.loy &&
1203
r.hix >= this.hix && r.hiy >= this.hiy);
1204
}
1205
1206
/**
1207
* Quickly checks if this Region intersects the specified
1208
* rectangular area specified in lox, loy, hix, hiy format.
1209
* <p>
1210
* This method tests only against the bounds of this region
1211
* and does not bother to test if the rectangular region
1212
* actually intersects any bands.
1213
*/
1214
public boolean intersectsQuickCheckXYXY(int lox, int loy,
1215
int hix, int hiy)
1216
{
1217
return (hix > this.lox && lox < this.hix &&
1218
hiy > this.loy && loy < this.hiy);
1219
}
1220
1221
/**
1222
* Quickly checks if this Region intersects the specified
1223
* Region object.
1224
* <p>
1225
* This method tests only against the bounds of this region
1226
* and does not bother to test if the rectangular region
1227
* actually intersects any bands.
1228
*/
1229
public boolean intersectsQuickCheck(Region r) {
1230
return (r.hix > this.lox && r.lox < this.hix &&
1231
r.hiy > this.loy && r.loy < this.hiy);
1232
}
1233
1234
/**
1235
* Quickly checks if this Region surrounds the specified
1236
* Region object.
1237
* <p>
1238
* This method will return false if this Region object is
1239
* not a simple rectangle.
1240
*/
1241
public boolean encompasses(Region r) {
1242
return (this.bands == null &&
1243
this.lox <= r.lox && this.loy <= r.loy &&
1244
this.hix >= r.hix && this.hiy >= r.hiy);
1245
}
1246
1247
/**
1248
* Quickly checks if this Region surrounds the specified
1249
* rectangular area specified in x, y, width, height format.
1250
* <p>
1251
* This method will return false if this Region object is
1252
* not a simple rectangle.
1253
*/
1254
public boolean encompassesXYWH(int x, int y, int w, int h) {
1255
return encompassesXYXY(x, y, dimAdd(x, w), dimAdd(y, h));
1256
}
1257
1258
/**
1259
* Quickly checks if this Region surrounds the specified
1260
* rectangular area specified in lox, loy, hix, hiy format.
1261
* <p>
1262
* This method will return false if this Region object is
1263
* not a simple rectangle.
1264
*/
1265
public boolean encompassesXYXY(int lox, int loy, int hix, int hiy) {
1266
return (this.bands == null &&
1267
this.lox <= lox && this.loy <= loy &&
1268
this.hix >= hix && this.hiy >= hiy);
1269
}
1270
1271
/**
1272
* Gets the bbox of the available spans, clipped to the OutputArea.
1273
*/
1274
public void getBounds(int pathbox[]) {
1275
pathbox[0] = lox;
1276
pathbox[1] = loy;
1277
pathbox[2] = hix;
1278
pathbox[3] = hiy;
1279
}
1280
1281
/**
1282
* Clips the indicated bbox array to the bounds of this Region.
1283
*/
1284
public void clipBoxToBounds(int bbox[]) {
1285
if (bbox[0] < lox) bbox[0] = lox;
1286
if (bbox[1] < loy) bbox[1] = loy;
1287
if (bbox[2] > hix) bbox[2] = hix;
1288
if (bbox[3] > hiy) bbox[3] = hiy;
1289
}
1290
1291
/**
1292
* Gets an iterator object to iterate over the spans in this region.
1293
*/
1294
public RegionIterator getIterator() {
1295
return new RegionIterator(this);
1296
}
1297
1298
/**
1299
* Gets a span iterator object that iterates over the spans in this region
1300
*/
1301
public SpanIterator getSpanIterator() {
1302
return new RegionSpanIterator(this);
1303
}
1304
1305
/**
1306
* Gets a span iterator object that iterates over the spans in this region
1307
* but clipped to the bounds given in the argument (xlo, ylo, xhi, yhi).
1308
*/
1309
public SpanIterator getSpanIterator(int bbox[]) {
1310
SpanIterator result = getSpanIterator();
1311
result.intersectClipBox(bbox[0], bbox[1], bbox[2], bbox[3]);
1312
return result;
1313
}
1314
1315
/**
1316
* Returns a SpanIterator that is the argument iterator filtered by
1317
* this region.
1318
*/
1319
public SpanIterator filter(SpanIterator si) {
1320
if (bands == null) {
1321
si.intersectClipBox(lox, loy, hix, hiy);
1322
} else {
1323
si = new RegionClipSpanIterator(this, si);
1324
}
1325
return si;
1326
}
1327
1328
public String toString() {
1329
StringBuffer sb = new StringBuffer();
1330
sb.append("Region[[");
1331
sb.append(lox);
1332
sb.append(", ");
1333
sb.append(loy);
1334
sb.append(" => ");
1335
sb.append(hix);
1336
sb.append(", ");
1337
sb.append(hiy);
1338
sb.append("]");
1339
if (bands != null) {
1340
int col = 0;
1341
while (col < endIndex) {
1342
sb.append("y{");
1343
sb.append(bands[col++]);
1344
sb.append(",");
1345
sb.append(bands[col++]);
1346
sb.append("}[");
1347
int end = bands[col++];
1348
end = col + end * 2;
1349
while (col < end) {
1350
sb.append("x(");
1351
sb.append(bands[col++]);
1352
sb.append(", ");
1353
sb.append(bands[col++]);
1354
sb.append(")");
1355
}
1356
sb.append("]");
1357
}
1358
}
1359
sb.append("]");
1360
return sb.toString();
1361
}
1362
1363
public int hashCode() {
1364
return (isEmpty() ? 0 : (lox * 3 + loy * 5 + hix * 7 + hiy * 9));
1365
}
1366
1367
public boolean equals(Object o) {
1368
if (!(o instanceof Region)) {
1369
return false;
1370
}
1371
Region r = (Region) o;
1372
if (this.isEmpty()) {
1373
return r.isEmpty();
1374
} else if (r.isEmpty()) {
1375
return false;
1376
}
1377
if (r.lox != this.lox || r.loy != this.loy ||
1378
r.hix != this.hix || r.hiy != this.hiy)
1379
{
1380
return false;
1381
}
1382
if (this.bands == null) {
1383
return (r.bands == null);
1384
} else if (r.bands == null) {
1385
return false;
1386
}
1387
if (this.endIndex != r.endIndex) {
1388
return false;
1389
}
1390
int abands[] = this.bands;
1391
int bbands[] = r.bands;
1392
for (int i = 0; i < endIndex; i++) {
1393
if (abands[i] != bbands[i]) {
1394
return false;
1395
}
1396
}
1397
return true;
1398
}
1399
}
1400
1401