1 /* ScanlineCoverage.java -- Manages coverage information for a scanline 2 Copyright (C) 2007 Free Software Foundation, Inc. 3 4 This file is part of GNU Classpath. 5 6 GNU Classpath is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2, or (at your option) 9 any later version. 10 11 GNU Classpath is distributed in the hope that it will be useful, but 12 WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with GNU Classpath; see the file COPYING. If not, write to the 18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 19 02110-1301 USA. 20 21 Linking this library statically or dynamically with other modules is 22 making a combined work based on this library. Thus, the terms and 23 conditions of the GNU General Public License cover the whole 24 combination. 25 26 As a special exception, the copyright holders of this library give you 27 permission to link this library with independent modules to produce an 28 executable, regardless of the license terms of these independent 29 modules, and to copy and distribute the resulting executable under 30 terms of your choice, provided that you also meet, for each linked 31 independent module, the terms and conditions of the license of that 32 module. An independent module is a module which is not derived from 33 or based on this library. If you modify this library, you may extend 34 this exception to your version of the library, but you are not 35 obligated to do so. If you do not wish to do so, delete this 36 exception statement from your version. */ 37 38 package gnu.java.awt.java2d; 39 40 /** 41 * Stores and handles the pixel converage for a scanline. The pixel coverage 42 * is stored as sorted list of {@linke Covergage} entries, each of which holds 43 * information about the coverage for the X and Y axis. This is utilized to 44 * compute the actual coverage for each pixel on the scanline and finding 45 * chunks of pixels with equal coverage quickly. 46 */ 47 public final class ScanlineCoverage 48 { 49 50 /** 51 * Iterates over the coverage list and calculates the actual coverage 52 * ranges on a scanline. 53 */ 54 public final class Iterator 55 { 56 /** 57 * This instance is reused in the iteration. 58 */ 59 private Range range; 60 61 /** 62 * The pointer to the current item in the iteration. 63 */ 64 private Coverage currentItem; 65 66 /** 67 * The current coverage value. 68 */ 69 private int currentCoverage; 70 71 /** 72 * True when the current pixel coverage has already been handled, false 73 * otherwise. 74 */ 75 private boolean handledPixelCoverage; 76 77 /** 78 * Creates a new CoverageIterator. 79 */ Iterator()80 Iterator() 81 { 82 range = new Range(); 83 } 84 85 /** 86 * Returns the next coverage range on the scanline. The returned object 87 * will always be the same object, but with different values. Keep that 88 * in mind when dealing with this object. 89 * 90 * @return the next coverage range on the scanline 91 */ next()92 public Range next() 93 { 94 // TODO: Lump together the single-pixel coverage and the 95 // between-pixel coverage when the pixel coverage delta is 0. 96 if (handledPixelCoverage == false) 97 { 98 // Handle single pixel coverage. 99 range.setXPos(currentItem.xPos); 100 range.setLength(1); 101 range.setCoverage(currentCoverage + currentItem.pixelCoverage); 102 handledPixelCoverage = true; 103 } 104 else 105 { 106 // Handle pixel span coverage. 107 currentCoverage += currentItem.covDelta; 108 range.setCoverage(currentCoverage); 109 range.setXPos(currentItem.xPos + 1); 110 currentItem = currentItem.next; 111 range.setLength(currentItem.xPos - range.xPos); 112 handledPixelCoverage = false; 113 } 114 return range; 115 } 116 117 /** 118 * Returns {@ true} when there are more coverage ranges to iterate, 119 * {@ false} otherwise. 120 * 121 * @return {@ true} when there are more coverage ranges to iterate, 122 * {@ false} otherwise 123 */ hasNext()124 public boolean hasNext() 125 { 126 boolean hasNext; 127 if (currentItem != null && handledPixelCoverage == false) 128 { 129 // We have at least one more coverage item when there's a pixel 130 // coverage piece left. 131 hasNext = true; 132 } 133 else if (currentItem == null || currentItem.next == null 134 || currentItem.next == last) 135 { 136 hasNext = false; 137 } 138 else 139 { 140 hasNext = true; 141 } 142 return hasNext; 143 } 144 145 /** 146 * Resets this iterator to the start of the list. 147 */ reset()148 void reset() 149 { 150 currentItem = head; 151 currentCoverage = 0; 152 handledPixelCoverage = false; 153 } 154 } 155 156 /** 157 * A data object that carries information about pixel coverage on a scanline. 158 * The data consists of a starting X position on the scanline, the 159 * length of the range in pixels and the actual coverage value. 160 **/ 161 public static final class Range 162 { 163 /** 164 * The X position on the scanline, in pixels. 165 */ 166 private int xPos; 167 168 /** 169 * The length of the range, in pixels. 170 */ 171 private int length; 172 173 /** 174 * The actual coverage. The relation depends on 175 * {@link ScanlineCoverage#maxCoverage}. 176 */ 177 private int coverage; 178 179 /** 180 * Creates a new CoverageRange object. 181 */ Range()182 Range() 183 { 184 // Nothing to do. The values get initialized in the corresponding 185 // setters. 186 } 187 188 /** 189 * Sets the X start position (left) on the scanline. This value is 190 * considered to be in pixels and device space. 191 * 192 * @param x the x position 193 */ setXPos(int x)194 void setXPos(int x) 195 { 196 xPos = x; 197 } 198 199 /** 200 * Returns the X start position (left) on the scanline. This value 201 * is considered to be in pixels and device space. 202 * 203 * @return the X position on the scanline 204 */ getXPos()205 public int getXPos() 206 { 207 return xPos; 208 } 209 210 /** 211 * Sets the length of the pixel range. This is in pixel units. 212 * 213 * @param l the length of the range 214 */ setLength(int l)215 void setLength(int l) 216 { 217 length = l; 218 } 219 220 /** 221 * Returns the length of the range in pixel units. 222 * 223 * @return the length of the range in pixel units 224 */ getLength()225 public int getLength() 226 { 227 return length; 228 } 229 230 /** 231 * Returns the first X position after the range. 232 * 233 * @return the first X position after the range 234 */ getXPosEnd()235 public int getXPosEnd() 236 { 237 return xPos + length; 238 } 239 240 /** 241 * Sets the coverage of the pixel range. The relation of that value 242 * depends on {@link ScanlineCoverage#maxCoverage}. 243 * 244 * @param cov the coverage value for the pixel range 245 */ setCoverage(int cov)246 void setCoverage(int cov) 247 { 248 coverage = cov; 249 } 250 251 /** 252 * Returns the coverage of the pixel range. The relation of this value 253 * depends on {@link ScanlineCoverage#getMaxCoverage()}. 254 * 255 * @return the coverage of the pixel range 256 */ getCoverage()257 public int getCoverage() 258 { 259 return coverage; 260 } 261 262 /** 263 * Returns a string representation. 264 */ toString()265 public String toString() 266 { 267 return "Coverage range: xPos=" + xPos + ", length=" + length 268 + ", coverage: " + coverage; 269 } 270 } 271 272 /** 273 * One bucket in the list. 274 */ 275 private static final class Coverage 276 { 277 /** 278 * The X coordinate on the scanline to which this bucket belongs. 279 */ 280 int xPos; 281 282 /** 283 * The coverage delta from the pixel at xPos to xPos + 1. 284 */ 285 int covDelta; 286 287 /** 288 * The delta for the pixel at xPos. This is added to the pixel at xPos, 289 * but not to the following pixel. 290 */ 291 int pixelCoverage; 292 293 /** 294 * Implements a linked list. This points to the next element of the list. 295 */ 296 Coverage next; 297 298 /** 299 * Returns the X coordinate for this entry. 300 * 301 * @return the X coordinate for this entry 302 */ getXPos()303 public int getXPos() 304 { 305 return xPos; 306 } 307 308 /** 309 * Returns the coverage delta for this entry. 310 * 311 * @return the coverage delta for this entry 312 */ getCoverageDelta()313 public int getCoverageDelta() 314 { 315 return covDelta; 316 } 317 318 /** 319 * Returns a string representation. 320 * 321 * @return a string representation 322 */ toString()323 public String toString() 324 { 325 return "Coverage: xPos: " + xPos + ", covDelta: " + covDelta; 326 } 327 328 /** 329 * Returns a string representation of this entry and all the following 330 * in the linked list. 331 * 332 * @return a string representation of this entry and all the following 333 * in the linked list 334 */ list()335 public String list() 336 { 337 String str = toString(); 338 if (next != null) 339 str = str + " --> " + next.list(); 340 return str; 341 } 342 } 343 344 /** 345 * The head of the sorted list of buckets. 346 */ 347 private Coverage head; 348 349 /** 350 * The current bucket. We make use of the fact that the scanline converter 351 * always scans the scanline (and thus this list) from left to right to 352 * quickly find buckets or insertion points. 353 */ 354 private Coverage current; 355 356 /** 357 * The item that is before current in the list. 358 */ 359 private Coverage currentPrev; 360 361 /** 362 * The bucket after the last valid bucket. Unused buckets are not thrown 363 * away and garbage collected. Instead, we keep them at the tail of the list 364 * and reuse them when necessary. 365 */ 366 private Coverage last; 367 368 /** 369 * The last valid entry. 370 */ 371 private Coverage lastPrev; 372 373 /** 374 * The minimum X coordinate of this scanline. 375 */ 376 private int minX; 377 378 /** 379 * The maximum X coordinate of this scanline. 380 */ 381 private int maxX; 382 383 /** 384 * The maximum coverage value. 385 */ 386 private int maxCoverage; 387 388 /** 389 * The iterator over the ranges of this scanline. 390 */ 391 private Iterator iterator; 392 393 /** 394 * Creates a new ScanlineCoverage instance. 395 */ ScanlineCoverage()396 public ScanlineCoverage() 397 { 398 iterator = new Iterator(); 399 } 400 401 /** 402 * Indicates the the next scan of the scanline begins and that the next 403 * request will be at the beginning of this list. This makes searching and 404 * sorting of this list very quick. 405 */ rewind()406 public void rewind() 407 { 408 current = head; 409 currentPrev = null; 410 } 411 412 /** 413 * Clears the list. This does not throw away the old buckets but only 414 * resets the end-pointer of the list to the first element. All buckets are 415 * then unused and are reused when the list is filled again. 416 */ clear()417 public void clear() 418 { 419 last = head; 420 lastPrev = null; 421 current = head; 422 currentPrev = null; 423 minX = Integer.MAX_VALUE; 424 maxX = Integer.MIN_VALUE; 425 } 426 427 /** 428 * This adds the specified coverage to the pixel at the specified 429 * X position. 430 * 431 * @param x the X position 432 * @param xc the x coverage 433 * @param yc the y coverage 434 */ add(int x, int xc, int yc)435 public void add(int x, int xc, int yc) 436 { 437 Coverage bucket = findOrInsert(x); 438 bucket.covDelta += xc; 439 bucket.pixelCoverage += yc; 440 minX = Math.min(minX, x); 441 maxX = Math.max(maxX, x); 442 } 443 444 /** 445 * Returns the maximum coverage value for the scanline. 446 * 447 * @return the maximum coverage value for the scanline 448 */ getMaxCoverage()449 public int getMaxCoverage() 450 { 451 return maxCoverage; 452 } 453 454 /** 455 * Sets the maximum coverage value for the scanline. 456 * 457 * @param maxCov the maximum coverage value for the scanline 458 */ setMaxCoverage(int maxCov)459 void setMaxCoverage(int maxCov) 460 { 461 maxCoverage = maxCov; 462 } 463 464 /** 465 * Returns the maximum X coordinate of the current scanline. 466 * 467 * @return the maximum X coordinate of the current scanline 468 */ getMaxX()469 public int getMaxX() 470 { 471 return maxX; 472 } 473 474 /** 475 * Returns the minimum X coordinate of the current scanline. 476 * 477 * @return the minimum X coordinate of the current scanline 478 */ getMinX()479 public int getMinX() 480 { 481 return minX; 482 } 483 484 /** 485 * Finds the bucket in the list with the specified X coordinate. 486 * If no such bucket is found, then a new one is fetched (either a cached 487 * bucket from the end of the list or a newly allocated one) inserted at the 488 * correct position and returned. 489 * 490 * @param x the X coordinate 491 * 492 * @return a bucket to hold the coverage data 493 */ findOrInsert(int x)494 private Coverage findOrInsert(int x) 495 { 496 // First search for a matching bucket. 497 if (head == null) 498 { 499 // Special case: the list is still empty. 500 // Testpoint 1. 501 head = new Coverage(); 502 head.xPos = x; 503 current = head; 504 currentPrev = null; 505 return head; 506 } 507 508 // This performs a linear search, starting from the current bucket. 509 // This is reasonably efficient because access to this list is always done 510 // in a linear fashion and we are usually not more then 1 or 2 buckets away 511 // from the one we're looking for. 512 Coverage match = current; 513 Coverage prev = currentPrev; 514 while (match != last && match.xPos < x) 515 { 516 prev = match; 517 match = match.next; 518 } 519 520 // At this point we have either found an entry with xPos >= x, or reached 521 // the end of the list (match == last || match == null). 522 if (match == null) 523 { 524 // End of the list. No cached items to reuse. 525 // Testpoint 2. 526 match = new Coverage(); 527 match.xPos = x; 528 if (prev != null) 529 prev.next = match; 530 current = match; 531 currentPrev = prev; 532 return match; 533 } 534 else if (match == last) 535 { 536 // End of the list. Reuse this item. Expand list. 537 // Testpoint 3. 538 last = match.next; 539 lastPrev = match; 540 match.xPos = x; 541 match.covDelta = 0; 542 match.pixelCoverage = 0; 543 // Keep link to last element or null, indicating the end of the list. 544 current = match; 545 currentPrev = prev; 546 return match; 547 } 548 549 if (x == match.xPos) 550 { 551 // Special case: We have another coverage entry at the same location 552 // as an already existing entry. Return this. 553 // Testpoint 4. 554 current = match; 555 currentPrev = prev; 556 return match; 557 } 558 else // x <= match.xPos 559 { 560 assert (x <= match.xPos); 561 assert (prev == null ||x > prev.xPos); 562 563 // Create new entry, or reuse existing one. 564 Coverage cov; 565 if (last != null) 566 { 567 // Testpoint 5. 568 cov = last; 569 last = cov.next; 570 lastPrev.next = last; 571 } 572 else 573 { 574 // Testpoint 6. 575 cov = new Coverage(); 576 } 577 578 cov.xPos = x; 579 cov.covDelta = 0; 580 cov.pixelCoverage = 0; 581 582 // Insert this item in the list. 583 if (prev != null) 584 { 585 // Testpoint 5 & 6. 586 prev.next = cov; 587 cov.next = match; 588 current = cov; 589 currentPrev = prev; 590 } 591 else 592 { 593 // Testpoint 7. 594 assert (match == head); 595 // Insert at head. 596 head = cov; 597 head.next = match; 598 current = head; 599 currentPrev = null; 600 } 601 return cov; 602 } 603 } 604 605 /** 606 * (Re-)Starts iterating the coverage values for the scanline. 607 * Use the returned iterator to get the consecutive coverage ranges. 608 * 609 * @return the iterator 610 */ iterate()611 public Iterator iterate() 612 { 613 iterator.reset(); 614 return iterator; 615 } 616 617 /** 618 * Returns {@ true} if this object has no entries for the current scanline, 619 * {@ false} otherwise. 620 * 621 * @return {@ true} if this object has no entries for the current scanline, 622 * {@ false} otherwise 623 */ isEmpty()624 public boolean isEmpty() 625 { 626 return head == null || head == last 627 || head.next == null || head.next == last; 628 } 629 630 } 631