001: /*
002: * Copyright 1999 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.java2d.pipe;
027:
028: import java.awt.geom.PathIterator;
029: import java.awt.Rectangle;
030:
031: /**
032: * This class clips a SpanIterator to a Region and outputs the
033: * resulting spans as another SpanIterator.
034: *
035: * Spans are output in the usual y/x order, unless the input span
036: * iterator doesn't conform to this order, or the iterator's span
037: * straddle more than one band of the Region used for clipping.
038: *
039: * Principle of operation:
040: *
041: * The iterator maintains a several cursors onto the RegionIterator
042: * in order to avoid having to buffer spans from the SpanIterator.
043: * They are:
044: * resetState The initial state of the RegionIterator
045: * lwm Low Water Mark, a running start point for
046: * processing each band. Usually goes down, but
047: * can be reset to resetState if a span has a lower
048: * start coordinate than the previous one.
049: * row The start of the current band of the RegionIterator
050: * box The current span of the current row
051: *
052: * The main nextSpan() loop implements a coroutine like structure, with
053: * three producers to get the next span, row and box calling each other
054: * to iterate through the span iterator and region.
055: *
056: * REMIND: Needs a native implementation!
057: */
058: public class RegionClipSpanIterator implements SpanIterator {
059:
060: // The inputs to the filter
061: Region rgn;
062: SpanIterator spanIter;
063:
064: // The cursors that track the progress through the region
065: RegionIterator resetState;
066: RegionIterator lwm;
067: RegionIterator row;
068: RegionIterator box;
069:
070: // The bounds of the current span iterator span
071: int spanlox, spanhix, spanloy, spanhiy;
072:
073: // The extent of the region band marking the low water mark
074: int lwmloy, lwmhiy;
075:
076: // The bounds of the current region box
077: int rgnlox, rgnloy, rgnhix, rgnhiy;
078:
079: // The bounding box of the input Region. Used for click
080: // rejection of iterator spans
081: int rgnbndslox, rgnbndsloy, rgnbndshix, rgnbndshiy;
082:
083: // The array used to hold coordinates from the region iterator
084: int rgnbox[] = new int[4];
085:
086: // The array used to hold coordinates from the span iterator
087: int spanbox[] = new int[4];
088:
089: // True if the next iterator span should be read on the next
090: // iteration of the main nextSpan() loop
091: boolean doNextSpan;
092:
093: // True if the next region box should be read on the next
094: // iteration of the main nextSpan() loop
095: boolean doNextBox;
096:
097: // True if there are no more spans or the Region is empty
098: boolean done = false;
099:
100: /*
101: * Creates an instance that filters the spans generated by
102: * spanIter through the region described by rgn.
103: */
104: public RegionClipSpanIterator(Region rgn, SpanIterator spanIter) {
105:
106: this .spanIter = spanIter;
107:
108: resetState = rgn.getIterator();
109: lwm = resetState.createCopy();
110:
111: if (!lwm.nextYRange(rgnbox)) {
112: done = true;
113: return;
114: }
115:
116: rgnloy = lwmloy = rgnbox[1];
117: rgnhiy = lwmhiy = rgnbox[3];
118:
119: rgn.getBounds(rgnbox);
120: rgnbndslox = rgnbox[0];
121: rgnbndsloy = rgnbox[1];
122: rgnbndshix = rgnbox[2];
123: rgnbndshiy = rgnbox[3];
124: if (rgnbndslox >= rgnbndshix || rgnbndsloy >= rgnbndshiy) {
125: done = true;
126: return;
127: }
128:
129: this .rgn = rgn;
130:
131: row = lwm.createCopy();
132: box = row.createCopy();
133: doNextSpan = true;
134: doNextBox = false;
135: }
136:
137: /*
138: * Gets the bbox of the available path segments, clipped to the
139: * Region.
140: */
141: public void getPathBox(int pathbox[]) {
142: int[] rgnbox = new int[4];
143: rgn.getBounds(rgnbox);
144: spanIter.getPathBox(pathbox);
145:
146: if (pathbox[0] < rgnbox[0]) {
147: pathbox[0] = rgnbox[0];
148: }
149:
150: if (pathbox[1] < rgnbox[1]) {
151: pathbox[1] = rgnbox[1];
152: }
153:
154: if (pathbox[2] > rgnbox[2]) {
155: pathbox[2] = rgnbox[2];
156: }
157:
158: if (pathbox[3] > rgnbox[3]) {
159: pathbox[3] = rgnbox[3];
160: }
161: }
162:
163: /*
164: * Intersects the path box with the given bbox.
165: * Returned spans are clipped to this region, or discarded
166: * altogether if they lie outside it.
167: */
168: public void intersectClipBox(int lox, int loy, int hix, int hiy) {
169: spanIter.intersectClipBox(lox, loy, hix, hiy);
170: }
171:
172: /*
173: * Fetches the next span that needs to be operated on.
174: * If the return value is false then there are no more spans.
175: */
176: public boolean nextSpan(int resultbox[]) {
177: if (done) {
178: return false;
179: }
180:
181: int resultlox, resultloy, resulthix, resulthiy;
182: boolean doNextRow = false;
183:
184: // REMIND: Cache the coordinate inst vars used in this loop
185: // in locals vars.
186: while (true) {
187: // We've exhausted the current span so get the next one
188: if (doNextSpan) {
189: if (!spanIter.nextSpan(spanbox)) {
190: done = true;
191: return false;
192: } else {
193: spanlox = spanbox[0];
194: // Clip out spans that lie outside of the rgn's bounds
195: if (spanlox >= rgnbndshix) {
196: continue;
197: }
198:
199: spanloy = spanbox[1];
200: if (spanloy >= rgnbndshiy) {
201: continue;
202: }
203:
204: spanhix = spanbox[2];
205: if (spanhix <= rgnbndslox) {
206: continue;
207: }
208:
209: spanhiy = spanbox[3];
210: if (spanhiy <= rgnbndsloy) {
211: continue;
212: }
213: }
214: // If the span starts higher up than the low-water mark,
215: // reset the lwm. This can only happen if spans aren't
216: // returned in strict y/x order, or the first time through.
217: if (lwmloy > spanloy) {
218: lwm.copyStateFrom(resetState);
219: lwm.nextYRange(rgnbox);
220: lwmloy = rgnbox[1];
221: lwmhiy = rgnbox[3];
222: }
223: // Skip to the first rgn row whose bottom edge is
224: // below the top of the current span. This will only
225: // execute >0 times when the current span starts in a
226: // lower region row than the previous one, or possibly the
227: // first time through.
228: while (lwmhiy <= spanloy) {
229: if (!lwm.nextYRange(rgnbox))
230: break;
231: lwmloy = rgnbox[1];
232: lwmhiy = rgnbox[3];
233: }
234: // If the row overlaps the span, process it, otherwise
235: // fetch another span
236: if (lwmhiy > spanloy && lwmloy < spanhiy) {
237: // Update the current row if it's different from the
238: // new lwm
239: if (rgnloy != lwmloy) {
240: row.copyStateFrom(lwm);
241: rgnloy = lwmloy;
242: rgnhiy = lwmhiy;
243: }
244: box.copyStateFrom(row);
245: doNextBox = true;
246: doNextSpan = false;
247: }
248: continue;
249: }
250:
251: // The current row's spans are exhausted, do the next one
252: if (doNextRow) {
253: // Next time we either do the next span or the next box
254: doNextRow = false;
255: // Get the next row
256: boolean ok = row.nextYRange(rgnbox);
257: // If there was one, update the bounds
258: if (ok) {
259: rgnloy = rgnbox[1];
260: rgnhiy = rgnbox[3];
261: }
262: if (!ok || rgnloy >= spanhiy) {
263: // If we've exhausted the rows or this one is below the span,
264: // go onto the next span
265: doNextSpan = true;
266: } else {
267: // Otherwise get the first box on this row
268: box.copyStateFrom(row);
269: doNextBox = true;
270: }
271: continue;
272: }
273:
274: // Process the next box in the current row
275: if (doNextBox) {
276: boolean ok = box.nextXBand(rgnbox);
277: if (ok) {
278: rgnlox = rgnbox[0];
279: rgnhix = rgnbox[2];
280: }
281: if (!ok || rgnlox >= spanhix) {
282: // If there was no next rgn span or it's beyond the
283: // source span, go onto the next row or span
284: doNextBox = false;
285: if (rgnhiy >= spanhiy) {
286: // If the current row totally overlaps the span,
287: // go onto the next span
288: doNextSpan = true;
289: } else {
290: // otherwise go onto the next rgn row
291: doNextRow = true;
292: }
293: } else {
294: // Otherwise, if the new rgn span overlaps the
295: // spanbox, no need to get another box
296: doNextBox = rgnhix <= spanlox;
297: }
298: continue;
299: }
300:
301: // Prepare to do the next box either on this call or
302: // or the subsequent one
303: doNextBox = true;
304:
305: // Clip the current span against the current box
306: if (spanlox > rgnlox) {
307: resultlox = spanlox;
308: } else {
309: resultlox = rgnlox;
310: }
311:
312: if (spanloy > rgnloy) {
313: resultloy = spanloy;
314: } else {
315: resultloy = rgnloy;
316: }
317:
318: if (spanhix < rgnhix) {
319: resulthix = spanhix;
320: } else {
321: resulthix = rgnhix;
322: }
323:
324: if (spanhiy < rgnhiy) {
325: resulthiy = spanhiy;
326: } else {
327: resulthiy = rgnhiy;
328: }
329:
330: // If the result is empty, try then next box
331: // otherwise return the box.
332: // REMIND: I think by definition it's non-empty
333: // if we're here. Need to think about this some more.
334: if (resultlox >= resulthix || resultloy >= resulthiy) {
335: continue;
336: } else {
337: break;
338: }
339: }
340:
341: resultbox[0] = resultlox;
342: resultbox[1] = resultloy;
343: resultbox[2] = resulthix;
344: resultbox[3] = resulthiy;
345: return true;
346:
347: }
348:
349: /**
350: * This method tells the iterator that it may skip all spans
351: * whose Y range is completely above the indicated Y coordinate.
352: */
353: public void skipDownTo(int y) {
354: spanIter.skipDownTo(y);
355: }
356:
357: /**
358: * This method returns a native pointer to a function block that
359: * can be used by a native method to perform the same iteration
360: * cycle that the above methods provide while avoiding upcalls to
361: * the Java object.
362: * The definition of the structure whose pointer is returned by
363: * this method is defined in:
364: * <pre>
365: * src/share/native/sun/java2d/pipe/SpanIterator.h
366: * </pre>
367: */
368: public long getNativeIterator() {
369: return 0;
370: }
371:
372: /*
373: * Cleans out all internal data structures.
374: */
375: //public native void dispose();
376: protected void finalize() {
377: //dispose();
378: }
379:
380: }
|