1/*
2 * Copyright (c) 1999, 2017, 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
26package 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 */
56public 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     */
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     */
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    /*
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     */
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     */
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     */
377    public long getNativeIterator() {
378        return 0;
379    }
380
381    /*
382     * Cleans out all internal data structures.
383     */
384    //public native void dispose();
385
386    @SuppressWarnings("deprecation")
387    protected void finalize() {
388        //dispose();
389    }
390
391}
392