| /* | |
| * Copyright (c) 1999, 2020, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. Oracle designates this | |
| * particular file as subject to the "Classpath" exception as provided | |
| * by Oracle in the LICENSE file that accompanied this code. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| */ | |
| package sun.java2d.pipe; | |
| /** | |
| * This class clips a SpanIterator to a Region and outputs the | |
| * resulting spans as another SpanIterator. | |
| * | |
| * Spans are output in the usual y/x order, unless the input span | |
| * iterator doesn't conform to this order, or the iterator's span | |
| * straddle more than one band of the Region used for clipping. | |
| * | |
| * Principle of operation: | |
| * | |
| * The iterator maintains a several cursors onto the RegionIterator | |
| * in order to avoid having to buffer spans from the SpanIterator. | |
| * They are: | |
| * resetState The initial state of the RegionIterator | |
| * lwm Low Water Mark, a running start point for | |
| * processing each band. Usually goes down, but | |
| * can be reset to resetState if a span has a lower | |
| * start coordinate than the previous one. | |
| * row The start of the current band of the RegionIterator | |
| * box The current span of the current row | |
| * | |
| * The main nextSpan() loop implements a coroutine like structure, with | |
| * three producers to get the next span, row and box calling each other | |
| * to iterate through the span iterator and region. | |
| * | |
| * REMIND: Needs a native implementation! | |
| */ | |
| public class RegionClipSpanIterator implements SpanIterator { | |
| // The inputs to the filter | |
| Region rgn; | |
| SpanIterator spanIter; | |
| // The cursors that track the progress through the region | |
| RegionIterator resetState; | |
| RegionIterator lwm; | |
| RegionIterator row; | |
| RegionIterator box; | |
| // The bounds of the current span iterator span | |
| int spanlox, spanhix, spanloy, spanhiy; | |
| // The extent of the region band marking the low water mark | |
| int lwmloy, lwmhiy; | |
| // The bounds of the current region box | |
| int rgnlox, rgnloy, rgnhix, rgnhiy; | |
| // The bounding box of the input Region. Used for click | |
| // rejection of iterator spans | |
| int rgnbndslox, rgnbndsloy, rgnbndshix, rgnbndshiy; | |
| // The array used to hold coordinates from the region iterator | |
| int[] rgnbox = new int[4]; | |
| // The array used to hold coordinates from the span iterator | |
| int[] spanbox = new int[4]; | |
| // True if the next iterator span should be read on the next | |
| // iteration of the main nextSpan() loop | |
| boolean doNextSpan; | |
| // True if the next region box should be read on the next | |
| // iteration of the main nextSpan() loop | |
| boolean doNextBox; | |
| // True if there are no more spans or the Region is empty | |
| boolean done = false; | |
| /* | |
| * Creates an instance that filters the spans generated by | |
| * spanIter through the region described by rgn. | |
| */ | |
| public RegionClipSpanIterator(Region rgn, SpanIterator spanIter) { | |
| this.spanIter = spanIter; | |
| resetState = rgn.getIterator(); | |
| lwm = resetState.createCopy(); | |
| if (!lwm.nextYRange(rgnbox)) { | |
| done = true; | |
| return; | |
| } | |
| rgnloy = lwmloy = rgnbox[1]; | |
| rgnhiy = lwmhiy = rgnbox[3]; | |
| rgn.getBounds(rgnbox); | |
| rgnbndslox = rgnbox[0]; | |
| rgnbndsloy = rgnbox[1]; | |
| rgnbndshix = rgnbox[2]; | |
| rgnbndshiy = rgnbox[3]; | |
| if (rgnbndslox >= rgnbndshix || | |
| rgnbndsloy >= rgnbndshiy) { | |
| done = true; | |
| return; | |
| } | |
| this.rgn = rgn; | |
| row = lwm.createCopy(); | |
| box = row.createCopy(); | |
| doNextSpan = true; | |
| doNextBox = false; | |
| } | |
| /* | |
| * Gets the bbox of the available path segments, clipped to the | |
| * Region. | |
| */ | |
| public void getPathBox(int[] pathbox) { | |
| int[] rgnbox = new int[4]; | |
| rgn.getBounds(rgnbox); | |
| spanIter.getPathBox(pathbox); | |
| if (pathbox[0] < rgnbox[0]) { | |
| pathbox[0] = rgnbox[0]; | |
| } | |
| if (pathbox[1] < rgnbox[1]) { | |
| pathbox[1] = rgnbox[1]; | |
| } | |
| if (pathbox[2] > rgnbox[2]) { | |
| pathbox[2] = rgnbox[2]; | |
| } | |
| if (pathbox[3] > rgnbox[3]) { | |
| pathbox[3] = rgnbox[3]; | |
| } | |
| } | |
| /* | |
| * Intersects the path box with the given bbox. | |
| * Returned spans are clipped to this region, or discarded | |
| * altogether if they lie outside it. | |
| */ | |
| public void intersectClipBox(int lox, int loy, int hix, int hiy) { | |
| spanIter.intersectClipBox(lox, loy, hix, hiy); | |
| } | |
| /* | |
| * Fetches the next span that needs to be operated on. | |
| * If the return value is false then there are no more spans. | |
| */ | |
| public boolean nextSpan(int[] resultbox) { | |
| if (done) { | |
| return false; | |
| } | |
| int resultlox, resultloy, resulthix, resulthiy; | |
| boolean doNextRow = false; | |
| // REMIND: Cache the coordinate inst vars used in this loop | |
| // in locals vars. | |
| while (true) { | |
| // We've exhausted the current span so get the next one | |
| if (doNextSpan) { | |
| if (!spanIter.nextSpan(spanbox)) { | |
| done = true; | |
| return false; | |
| } else { | |
| spanlox = spanbox[0]; | |
| // Clip out spans that lie outside of the rgn's bounds | |
| if (spanlox >= rgnbndshix) { | |
| continue; | |
| } | |
| spanloy = spanbox[1]; | |
| if (spanloy >= rgnbndshiy) { | |
| continue; | |
| } | |
| spanhix = spanbox[2]; | |
| if (spanhix <= rgnbndslox) { | |
| continue; | |
| } | |
| spanhiy = spanbox[3]; | |
| if (spanhiy <= rgnbndsloy) { | |
| continue; | |
| } | |
| } | |
| // If the span starts higher up than the low-water mark, | |
| // reset the lwm. This can only happen if spans aren't | |
| // returned in strict y/x order, or the first time through. | |
| if (lwmloy > spanloy) { | |
| lwm.copyStateFrom(resetState); | |
| lwm.nextYRange(rgnbox); | |
| lwmloy = rgnbox[1]; | |
| lwmhiy = rgnbox[3]; | |
| } | |
| // Skip to the first rgn row whose bottom edge is | |
| // below the top of the current span. This will only | |
| // execute >0 times when the current span starts in a | |
| // lower region row than the previous one, or possibly the | |
| // first time through. | |
| while (lwmhiy <= spanloy) { | |
| if (!lwm.nextYRange(rgnbox)) | |
| break; | |
| lwmloy = rgnbox[1]; | |
| lwmhiy = rgnbox[3]; | |
| } | |
| // If the row overlaps the span, process it, otherwise | |
| // fetch another span | |
| if (lwmhiy > spanloy && lwmloy < spanhiy) { | |
| // Update the current row if it's different from the | |
| // new lwm | |
| if (rgnloy != lwmloy) { | |
| row.copyStateFrom(lwm); | |
| rgnloy = lwmloy; | |
| rgnhiy = lwmhiy; | |
| } | |
| box.copyStateFrom(row); | |
| doNextBox = true; | |
| doNextSpan = false; | |
| } | |
| continue; | |
| } | |
| // The current row's spans are exhausted, do the next one | |
| if (doNextRow) { | |
| // Next time we either do the next span or the next box | |
| doNextRow = false; | |
| // Get the next row | |
| boolean ok = row.nextYRange(rgnbox); | |
| // If there was one, update the bounds | |
| if (ok) { | |
| rgnloy = rgnbox[1]; | |
| rgnhiy = rgnbox[3]; | |
| } | |
| if (!ok || rgnloy >= spanhiy) { | |
| // If we've exhausted the rows or this one is below the span, | |
| // go onto the next span | |
| doNextSpan = true; | |
| } | |
| else { | |
| // Otherwise get the first box on this row | |
| box.copyStateFrom(row); | |
| doNextBox = true; | |
| } | |
| continue; | |
| } | |
| // Process the next box in the current row | |
| if (doNextBox) { | |
| boolean ok = box.nextXBand(rgnbox); | |
| if (ok) { | |
| rgnlox = rgnbox[0]; | |
| rgnhix = rgnbox[2]; | |
| } | |
| if (!ok || rgnlox >= spanhix) { | |
| // If there was no next rgn span or it's beyond the | |
| // source span, go onto the next row or span | |
| doNextBox = false; | |
| if (rgnhiy >= spanhiy) { | |
| // If the current row totally overlaps the span, | |
| // go onto the next span | |
| doNextSpan = true; | |
| } else { | |
| // otherwise go onto the next rgn row | |
| doNextRow = true; | |
| } | |
| } else { | |
| // Otherwise, if the new rgn span overlaps the | |
| // spanbox, no need to get another box | |
| doNextBox = rgnhix <= spanlox; | |
| } | |
| continue; | |
| } | |
| // Prepare to do the next box either on this call or | |
| // or the subsequent one | |
| doNextBox = true; | |
| // Clip the current span against the current box | |
| if (spanlox > rgnlox) { | |
| resultlox = spanlox; | |
| } | |
| else { | |
| resultlox = rgnlox; | |
| } | |
| if (spanloy > rgnloy) { | |
| resultloy = spanloy; | |
| } | |
| else { | |
| resultloy = rgnloy; | |
| } | |
| if (spanhix < rgnhix) { | |
| resulthix = spanhix; | |
| } | |
| else { | |
| resulthix = rgnhix; | |
| } | |
| if (spanhiy < rgnhiy) { | |
| resulthiy = spanhiy; | |
| } | |
| else { | |
| resulthiy = rgnhiy; | |
| } | |
| // If the result is empty, try then next box | |
| // otherwise return the box. | |
| // REMIND: I think by definition it's non-empty | |
| // if we're here. Need to think about this some more. | |
| if (resultlox >= resulthix || | |
| resultloy >= resulthiy) { | |
| continue; | |
| } | |
| else { | |
| break; | |
| } | |
| } | |
| resultbox[0] = resultlox; | |
| resultbox[1] = resultloy; | |
| resultbox[2] = resulthix; | |
| resultbox[3] = resulthiy; | |
| return true; | |
| } | |
| /** | |
| * This method tells the iterator that it may skip all spans | |
| * whose Y range is completely above the indicated Y coordinate. | |
| */ | |
| public void skipDownTo(int y) { | |
| spanIter.skipDownTo(y); | |
| } | |
| /** | |
| * This method returns a native pointer to a function block that | |
| * can be used by a native method to perform the same iteration | |
| * cycle that the above methods provide while avoiding upcalls to | |
| * the Java object. | |
| * The definition of the structure whose pointer is returned by | |
| * this method is defined in: | |
| * <pre> | |
| * src/share/native/sun/java2d/pipe/SpanIterator.h | |
| * </pre> | |
| */ | |
| public long getNativeIterator() { | |
| return 0; | |
| } | |
| } |