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 
  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      */
 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 }