| /* |
| * Copyright (c) 1999, 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; |
| |
| import java.awt.geom.PathIterator; |
| import java.awt.Rectangle; |
| |
| /** |
| * 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; |
| } |
| |
| /* |
| * Cleans out all internal data structures. |
| */ |
| //public native void dispose(); |
| |
| protected void finalize() { |
| //dispose(); |
| } |
| |
| } |