View Javadoc
1   /*
2    * Copyright (c) 1999, 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.geom.PathIterator;
29  import java.awt.Rectangle;
30  
31  /**
32   * This class clips a SpanIterator to a Region and outputs the
33   * resulting spans as another SpanIterator.
34   *
35   * Spans are output in the usual y/x order, unless the input span
36   * iterator doesn't conform to this order, or the iterator's span
37   * straddle more than one band of the Region used for clipping.
38   *
39   * Principle of operation:
40   *
41   * The iterator maintains a several cursors onto the RegionIterator
42   * in order to avoid having to buffer spans from the SpanIterator.
43   * They are:
44   *  resetState    The initial state of the RegionIterator
45   *  lwm             Low Water Mark, a running start point for
46   *                  processing each band. Usually goes down, but
47   *                  can be reset to resetState if a span has a lower
48   *                  start coordinate than the previous one.
49   *  row             The start of the current band of the RegionIterator
50   *  box             The current span of the current row
51   *
52   * The main nextSpan() loop implements a coroutine like structure, with
53   * three producers to get the next span, row and box calling each other
54   * to iterate through the span iterator and region.
55   *
56   * REMIND: Needs a native implementation!
57   */
58  public class RegionClipSpanIterator implements SpanIterator {
59  
60      // The inputs to the filter
61      Region rgn;
62      SpanIterator spanIter;
63  
64      // The cursors that track the progress through the region
65      RegionIterator resetState;
66      RegionIterator lwm;
67      RegionIterator row;
68      RegionIterator box;
69  
70      // The bounds of the current span iterator span
71      int spanlox, spanhix, spanloy, spanhiy;
72  
73      // The extent of the region band marking the low water mark
74      int lwmloy, lwmhiy;
75  
76      // The bounds of the current region box
77      int rgnlox, rgnloy, rgnhix, rgnhiy;
78  
79      // The bounding box of the input Region. Used for click
80      // rejection of iterator spans
81      int rgnbndslox, rgnbndsloy, rgnbndshix, rgnbndshiy;
82  
83      // The array used to hold coordinates from the region iterator
84      int rgnbox[] = new int[4];
85  
86      // The array used to hold coordinates from the span iterator
87      int spanbox[] = new int[4];
88  
89      // True if the next iterator span should be read on the next
90      // iteration of the main nextSpan() loop
91      boolean doNextSpan;
92  
93      // True if the next region box should be read on the next
94      // iteration of the main nextSpan() loop
95      boolean doNextBox;
96  
97      // True if there are no more spans or the Region is empty
98      boolean done = false;
99  
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 ||
125             rgnbndsloy >= rgnbndshiy) {
126             done = true;
127             return;
128         }
129 
130         this.rgn = rgn;
131 
132 
133         row = lwm.createCopy();
134         box = row.createCopy();
135         doNextSpan = true;
136         doNextBox = false;
137     }
138 
139     /*
140      * Gets the bbox of the available path segments, clipped to the
141      * Region.
142      */
143     public void getPathBox(int pathbox[]) {
144         int[] rgnbox = new int[4];
145         rgn.getBounds(rgnbox);
146         spanIter.getPathBox(pathbox);
147 
148         if (pathbox[0] < rgnbox[0]) {
149             pathbox[0] = rgnbox[0];
150         }
151 
152         if (pathbox[1] < rgnbox[1]) {
153             pathbox[1] = rgnbox[1];
154         }
155 
156         if (pathbox[2] > rgnbox[2]) {
157             pathbox[2] = rgnbox[2];
158         }
159 
160         if (pathbox[3] > rgnbox[3]) {
161             pathbox[3] = rgnbox[3];
162         }
163 }
164 
165     /*
166      * Intersects the path box with the given bbox.
167      * Returned spans are clipped to this region, or discarded
168      * altogether if they lie outside it.
169      */
170     public void intersectClipBox(int lox, int loy, int hix, int hiy) {
171         spanIter.intersectClipBox(lox, loy, hix, hiy);
172     }
173 
174 
175     /*
176      * Fetches the next span that needs to be operated on.
177      * If the return value is false then there are no more spans.
178      */
179     public boolean nextSpan(int resultbox[]) {
180         if (done) {
181             return false;
182         }
183 
184         int resultlox, resultloy, resulthix, resulthiy;
185         boolean doNextRow = false;
186 
187         // REMIND: Cache the coordinate inst vars used in this loop
188         // in locals vars.
189         while (true) {
190             // We've exhausted the current span so get the next one
191             if (doNextSpan) {
192                 if (!spanIter.nextSpan(spanbox)) {
193                     done = true;
194                     return false;
195                 } else {
196                     spanlox = spanbox[0];
197                     // Clip out spans that lie outside of the rgn's bounds
198                     if (spanlox >= rgnbndshix) {
199                         continue;
200                     }
201 
202                     spanloy = spanbox[1];
203                     if (spanloy >= rgnbndshiy) {
204                         continue;
205                     }
206 
207                     spanhix = spanbox[2];
208                     if (spanhix <= rgnbndslox) {
209                         continue;
210                     }
211 
212                     spanhiy = spanbox[3];
213                     if (spanhiy <= rgnbndsloy) {
214                         continue;
215                     }
216                 }
217                 // If the span starts higher up than the low-water mark,
218                 // reset the lwm. This can only happen if spans aren't
219                 // returned in strict y/x order, or the first time through.
220                 if (lwmloy > spanloy) {
221                     lwm.copyStateFrom(resetState);
222                     lwm.nextYRange(rgnbox);
223                     lwmloy = rgnbox[1];
224                     lwmhiy = rgnbox[3];
225                 }
226                 // Skip to the first rgn row whose bottom edge is
227                 // below the top of the current span. This will only
228                 // execute >0 times when the current span starts in a
229                 // lower region row than the previous one, or possibly the
230                 // first time through.
231                 while (lwmhiy <= spanloy) {
232                     if (!lwm.nextYRange(rgnbox))
233                         break;
234                     lwmloy = rgnbox[1];
235                     lwmhiy = rgnbox[3];
236                 }
237                 // If the row overlaps the span, process it, otherwise
238                 // fetch another span
239                 if (lwmhiy > spanloy && lwmloy < spanhiy) {
240                     // Update the current row if it's different from the
241                     // new lwm
242                     if (rgnloy != lwmloy) {
243                         row.copyStateFrom(lwm);
244                         rgnloy = lwmloy;
245                         rgnhiy = lwmhiy;
246                     }
247                     box.copyStateFrom(row);
248                     doNextBox = true;
249                     doNextSpan = false;
250                 }
251                 continue;
252             }
253 
254             // The current row's spans are exhausted, do the next one
255             if (doNextRow) {
256                 // Next time we either do the next span or the next box
257                 doNextRow = false;
258                 // Get the next row
259                 boolean ok = row.nextYRange(rgnbox);
260                 // If there was one, update the bounds
261                 if (ok) {
262                     rgnloy = rgnbox[1];
263                     rgnhiy = rgnbox[3];
264                 }
265                 if (!ok || rgnloy >= spanhiy) {
266                     // If we've exhausted the rows or this one is below the span,
267                     // go onto the next span
268                     doNextSpan = true;
269                 }
270                 else {
271                     // Otherwise get the first box on this row
272                     box.copyStateFrom(row);
273                     doNextBox = true;
274                 }
275                 continue;
276             }
277 
278             // Process the next box in the current row
279             if (doNextBox) {
280                 boolean ok = box.nextXBand(rgnbox);
281                 if (ok) {
282                     rgnlox = rgnbox[0];
283                     rgnhix = rgnbox[2];
284                 }
285                 if (!ok || rgnlox >= spanhix) {
286                     // If there was no next rgn span or it's beyond the
287                     // source span, go onto the next row or span
288                     doNextBox = false;
289                     if (rgnhiy >= spanhiy) {
290                         // If the current row totally overlaps the span,
291                         // go onto the next span
292                         doNextSpan = true;
293                     } else {
294                         // otherwise go onto the next rgn row
295                         doNextRow = true;
296                     }
297                 } else {
298                     // Otherwise, if the new rgn span overlaps the
299                     // spanbox, no need to get another box
300                     doNextBox = rgnhix <= spanlox;
301                 }
302                 continue;
303             }
304 
305             // Prepare to do the next box either on this call or
306             // or the subsequent one
307             doNextBox = true;
308 
309             // Clip the current span against the current box
310             if (spanlox > rgnlox) {
311                 resultlox = spanlox;
312             }
313             else {
314                 resultlox = rgnlox;
315             }
316 
317             if (spanloy > rgnloy) {
318                 resultloy = spanloy;
319             }
320             else {
321                 resultloy = rgnloy;
322             }
323 
324             if (spanhix < rgnhix) {
325                 resulthix = spanhix;
326             }
327             else {
328                 resulthix = rgnhix;
329             }
330 
331             if (spanhiy < rgnhiy) {
332                 resulthiy = spanhiy;
333             }
334             else {
335                 resulthiy = rgnhiy;
336             }
337 
338             // If the result is empty, try then next box
339             // otherwise return the box.
340             // REMIND: I think by definition it's non-empty
341             // if we're here. Need to think about this some more.
342             if (resultlox >= resulthix ||
343                 resultloy >= resulthiy) {
344                     continue;
345             }
346             else {
347                     break;
348             }
349         }
350 
351         resultbox[0] = resultlox;
352         resultbox[1] = resultloy;
353         resultbox[2] = resulthix;
354         resultbox[3] = resulthiy;
355         return true;
356 
357     }
358 
359 
360     /**
361      * This method tells the iterator that it may skip all spans
362      * whose Y range is completely above the indicated Y coordinate.
363      */
364     public void skipDownTo(int y) {
365         spanIter.skipDownTo(y);
366     }
367 
368     /**
369      * This method returns a native pointer to a function block that
370      * can be used by a native method to perform the same iteration
371      * cycle that the above methods provide while avoiding upcalls to
372      * the Java object.
373      * The definition of the structure whose pointer is returned by
374      * this method is defined in:
375      * <pre>
376      *     src/share/native/sun/java2d/pipe/SpanIterator.h
377      * </pre>
378      */
379     public long getNativeIterator() {
380         return 0;
381     }
382 
383     /*
384      * Cleans out all internal data structures.
385      */
386     //public native void dispose();
387 
388     protected void finalize() {
389         //dispose();
390     }
391 
392 }