PageRenderTime 57ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/java-1.7.0-openjdk/openjdk/jdk/src/share/classes/sun/java2d/pipe/Region.java

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