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