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