| /* ScanlineCoverage.java -- Manages coverage information for a scanline | |
| Copyright (C) 2007 Free Software Foundation, Inc. | |
| This file is part of GNU Classpath. | |
| GNU Classpath is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 2, or (at your option) | |
| any later version. | |
| GNU Classpath 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 for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with GNU Classpath; see the file COPYING. If not, write to the | |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
| 02110-1301 USA. | |
| Linking this library statically or dynamically with other modules is | |
| making a combined work based on this library. Thus, the terms and | |
| conditions of the GNU General Public License cover the whole | |
| combination. | |
| As a special exception, the copyright holders of this library give you | |
| permission to link this library with independent modules to produce an | |
| executable, regardless of the license terms of these independent | |
| modules, and to copy and distribute the resulting executable under | |
| terms of your choice, provided that you also meet, for each linked | |
| independent module, the terms and conditions of the license of that | |
| module. An independent module is a module which is not derived from | |
| or based on this library. If you modify this library, you may extend | |
| this exception to your version of the library, but you are not | |
| obligated to do so. If you do not wish to do so, delete this | |
| exception statement from your version. */ | |
| package gnu.java.awt.java2d; | |
| /** | |
| * Stores and handles the pixel converage for a scanline. The pixel coverage | |
| * is stored as sorted list of {@linke Covergage} entries, each of which holds | |
| * information about the coverage for the X and Y axis. This is utilized to | |
| * compute the actual coverage for each pixel on the scanline and finding | |
| * chunks of pixels with equal coverage quickly. | |
| */ | |
| public final class ScanlineCoverage | |
| { | |
| /** | |
| * Iterates over the coverage list and calculates the actual coverage | |
| * ranges on a scanline. | |
| */ | |
| public final class Iterator | |
| { | |
| /** | |
| * This instance is reused in the iteration. | |
| */ | |
| private Range range; | |
| /** | |
| * The pointer to the current item in the iteration. | |
| */ | |
| private Coverage currentItem; | |
| /** | |
| * The current coverage value. | |
| */ | |
| private int currentCoverage; | |
| /** | |
| * True when the current pixel coverage has already been handled, false | |
| * otherwise. | |
| */ | |
| private boolean handledPixelCoverage; | |
| /** | |
| * Creates a new CoverageIterator. | |
| */ | |
| Iterator() | |
| { | |
| range = new Range(); | |
| } | |
| /** | |
| * Returns the next coverage range on the scanline. The returned object | |
| * will always be the same object, but with different values. Keep that | |
| * in mind when dealing with this object. | |
| * | |
| * @return the next coverage range on the scanline | |
| */ | |
| public Range next() | |
| { | |
| // TODO: Lump together the single-pixel coverage and the | |
| // between-pixel coverage when the pixel coverage delta is 0. | |
| if (handledPixelCoverage == false) | |
| { | |
| // Handle single pixel coverage. | |
| range.setXPos(currentItem.xPos); | |
| range.setLength(1); | |
| range.setCoverage(currentCoverage + currentItem.pixelCoverage); | |
| handledPixelCoverage = true; | |
| } | |
| else | |
| { | |
| // Handle pixel span coverage. | |
| currentCoverage += currentItem.covDelta; | |
| range.setCoverage(currentCoverage); | |
| range.setXPos(currentItem.xPos + 1); | |
| currentItem = currentItem.next; | |
| range.setLength(currentItem.xPos - range.xPos); | |
| handledPixelCoverage = false; | |
| } | |
| return range; | |
| } | |
| /** | |
| * Returns {@ true} when there are more coverage ranges to iterate, | |
| * {@ false} otherwise. | |
| * | |
| * @return {@ true} when there are more coverage ranges to iterate, | |
| * {@ false} otherwise | |
| */ | |
| public boolean hasNext() | |
| { | |
| boolean hasNext; | |
| if (currentItem != null && handledPixelCoverage == false) | |
| { | |
| // We have at least one more coverage item when there's a pixel | |
| // coverage piece left. | |
| hasNext = true; | |
| } | |
| else if (currentItem == null || currentItem.next == null | |
| || currentItem.next == last) | |
| { | |
| hasNext = false; | |
| } | |
| else | |
| { | |
| hasNext = true; | |
| } | |
| return hasNext; | |
| } | |
| /** | |
| * Resets this iterator to the start of the list. | |
| */ | |
| void reset() | |
| { | |
| currentItem = head; | |
| currentCoverage = 0; | |
| handledPixelCoverage = false; | |
| } | |
| } | |
| /** | |
| * A data object that carries information about pixel coverage on a scanline. | |
| * The data consists of a starting X position on the scanline, the | |
| * length of the range in pixels and the actual coverage value. | |
| **/ | |
| public static final class Range | |
| { | |
| /** | |
| * The X position on the scanline, in pixels. | |
| */ | |
| private int xPos; | |
| /** | |
| * The length of the range, in pixels. | |
| */ | |
| private int length; | |
| /** | |
| * The actual coverage. The relation depends on | |
| * {@link ScanlineCoverage#maxCoverage}. | |
| */ | |
| private int coverage; | |
| /** | |
| * Creates a new CoverageRange object. | |
| */ | |
| Range() | |
| { | |
| // Nothing to do. The values get initialized in the corresponding | |
| // setters. | |
| } | |
| /** | |
| * Sets the X start position (left) on the scanline. This value is | |
| * considered to be in pixels and device space. | |
| * | |
| * @param x the x position | |
| */ | |
| void setXPos(int x) | |
| { | |
| xPos = x; | |
| } | |
| /** | |
| * Returns the X start position (left) on the scanline. This value | |
| * is considered to be in pixels and device space. | |
| * | |
| * @return the X position on the scanline | |
| */ | |
| public int getXPos() | |
| { | |
| return xPos; | |
| } | |
| /** | |
| * Sets the length of the pixel range. This is in pixel units. | |
| * | |
| * @param l the length of the range | |
| */ | |
| void setLength(int l) | |
| { | |
| length = l; | |
| } | |
| /** | |
| * Returns the length of the range in pixel units. | |
| * | |
| * @return the length of the range in pixel units | |
| */ | |
| public int getLength() | |
| { | |
| return length; | |
| } | |
| /** | |
| * Returns the first X position after the range. | |
| * | |
| * @return the first X position after the range | |
| */ | |
| public int getXPosEnd() | |
| { | |
| return xPos + length; | |
| } | |
| /** | |
| * Sets the coverage of the pixel range. The relation of that value | |
| * depends on {@link ScanlineCoverage#maxCoverage}. | |
| * | |
| * @param cov the coverage value for the pixel range | |
| */ | |
| void setCoverage(int cov) | |
| { | |
| coverage = cov; | |
| } | |
| /** | |
| * Returns the coverage of the pixel range. The relation of this value | |
| * depends on {@link ScanlineCoverage#getMaxCoverage()}. | |
| * | |
| * @return the coverage of the pixel range | |
| */ | |
| public int getCoverage() | |
| { | |
| return coverage; | |
| } | |
| /** | |
| * Returns a string representation. | |
| */ | |
| public String toString() | |
| { | |
| return "Coverage range: xPos=" + xPos + ", length=" + length | |
| + ", coverage: " + coverage; | |
| } | |
| } | |
| /** | |
| * One bucket in the list. | |
| */ | |
| private static final class Coverage | |
| { | |
| /** | |
| * The X coordinate on the scanline to which this bucket belongs. | |
| */ | |
| int xPos; | |
| /** | |
| * The coverage delta from the pixel at xPos to xPos + 1. | |
| */ | |
| int covDelta; | |
| /** | |
| * The delta for the pixel at xPos. This is added to the pixel at xPos, | |
| * but not to the following pixel. | |
| */ | |
| int pixelCoverage; | |
| /** | |
| * Implements a linked list. This points to the next element of the list. | |
| */ | |
| Coverage next; | |
| /** | |
| * Returns the X coordinate for this entry. | |
| * | |
| * @return the X coordinate for this entry | |
| */ | |
| public int getXPos() | |
| { | |
| return xPos; | |
| } | |
| /** | |
| * Returns the coverage delta for this entry. | |
| * | |
| * @return the coverage delta for this entry | |
| */ | |
| public int getCoverageDelta() | |
| { | |
| return covDelta; | |
| } | |
| /** | |
| * Returns a string representation. | |
| * | |
| * @return a string representation | |
| */ | |
| public String toString() | |
| { | |
| return "Coverage: xPos: " + xPos + ", covDelta: " + covDelta; | |
| } | |
| /** | |
| * Returns a string representation of this entry and all the following | |
| * in the linked list. | |
| * | |
| * @return a string representation of this entry and all the following | |
| * in the linked list | |
| */ | |
| public String list() | |
| { | |
| String str = toString(); | |
| if (next != null) | |
| str = str + " --> " + next.list(); | |
| return str; | |
| } | |
| } | |
| /** | |
| * The head of the sorted list of buckets. | |
| */ | |
| private Coverage head; | |
| /** | |
| * The current bucket. We make use of the fact that the scanline converter | |
| * always scans the scanline (and thus this list) from left to right to | |
| * quickly find buckets or insertion points. | |
| */ | |
| private Coverage current; | |
| /** | |
| * The item that is before current in the list. | |
| */ | |
| private Coverage currentPrev; | |
| /** | |
| * The bucket after the last valid bucket. Unused buckets are not thrown | |
| * away and garbage collected. Instead, we keep them at the tail of the list | |
| * and reuse them when necessary. | |
| */ | |
| private Coverage last; | |
| /** | |
| * The last valid entry. | |
| */ | |
| private Coverage lastPrev; | |
| /** | |
| * The minimum X coordinate of this scanline. | |
| */ | |
| private int minX; | |
| /** | |
| * The maximum X coordinate of this scanline. | |
| */ | |
| private int maxX; | |
| /** | |
| * The maximum coverage value. | |
| */ | |
| private int maxCoverage; | |
| /** | |
| * The iterator over the ranges of this scanline. | |
| */ | |
| private Iterator iterator; | |
| /** | |
| * Creates a new ScanlineCoverage instance. | |
| */ | |
| public ScanlineCoverage() | |
| { | |
| iterator = new Iterator(); | |
| } | |
| /** | |
| * Indicates the the next scan of the scanline begins and that the next | |
| * request will be at the beginning of this list. This makes searching and | |
| * sorting of this list very quick. | |
| */ | |
| public void rewind() | |
| { | |
| current = head; | |
| currentPrev = null; | |
| } | |
| /** | |
| * Clears the list. This does not throw away the old buckets but only | |
| * resets the end-pointer of the list to the first element. All buckets are | |
| * then unused and are reused when the list is filled again. | |
| */ | |
| public void clear() | |
| { | |
| last = head; | |
| lastPrev = null; | |
| current = head; | |
| currentPrev = null; | |
| minX = Integer.MAX_VALUE; | |
| maxX = Integer.MIN_VALUE; | |
| } | |
| /** | |
| * This adds the specified coverage to the pixel at the specified | |
| * X position. | |
| * | |
| * @param x the X position | |
| * @param xc the x coverage | |
| * @param yc the y coverage | |
| */ | |
| public void add(int x, int xc, int yc) | |
| { | |
| Coverage bucket = findOrInsert(x); | |
| bucket.covDelta += xc; | |
| bucket.pixelCoverage += yc; | |
| minX = Math.min(minX, x); | |
| maxX = Math.max(maxX, x); | |
| } | |
| /** | |
| * Returns the maximum coverage value for the scanline. | |
| * | |
| * @return the maximum coverage value for the scanline | |
| */ | |
| public int getMaxCoverage() | |
| { | |
| return maxCoverage; | |
| } | |
| /** | |
| * Sets the maximum coverage value for the scanline. | |
| * | |
| * @param maxCov the maximum coverage value for the scanline | |
| */ | |
| void setMaxCoverage(int maxCov) | |
| { | |
| maxCoverage = maxCov; | |
| } | |
| /** | |
| * Returns the maximum X coordinate of the current scanline. | |
| * | |
| * @return the maximum X coordinate of the current scanline | |
| */ | |
| public int getMaxX() | |
| { | |
| return maxX; | |
| } | |
| /** | |
| * Returns the minimum X coordinate of the current scanline. | |
| * | |
| * @return the minimum X coordinate of the current scanline | |
| */ | |
| public int getMinX() | |
| { | |
| return minX; | |
| } | |
| /** | |
| * Finds the bucket in the list with the specified X coordinate. | |
| * If no such bucket is found, then a new one is fetched (either a cached | |
| * bucket from the end of the list or a newly allocated one) inserted at the | |
| * correct position and returned. | |
| * | |
| * @param x the X coordinate | |
| * | |
| * @return a bucket to hold the coverage data | |
| */ | |
| private Coverage findOrInsert(int x) | |
| { | |
| // First search for a matching bucket. | |
| if (head == null) | |
| { | |
| // Special case: the list is still empty. | |
| // Testpoint 1. | |
| head = new Coverage(); | |
| head.xPos = x; | |
| current = head; | |
| currentPrev = null; | |
| return head; | |
| } | |
| // This performs a linear search, starting from the current bucket. | |
| // This is reasonably efficient because access to this list is always done | |
| // in a linear fashion and we are usually not more then 1 or 2 buckets away | |
| // from the one we're looking for. | |
| Coverage match = current; | |
| Coverage prev = currentPrev; | |
| while (match != last && match.xPos < x) | |
| { | |
| prev = match; | |
| match = match.next; | |
| } | |
| // At this point we have either found an entry with xPos >= x, or reached | |
| // the end of the list (match == last || match == null). | |
| if (match == null) | |
| { | |
| // End of the list. No cached items to reuse. | |
| // Testpoint 2. | |
| match = new Coverage(); | |
| match.xPos = x; | |
| if (prev != null) | |
| prev.next = match; | |
| current = match; | |
| currentPrev = prev; | |
| return match; | |
| } | |
| else if (match == last) | |
| { | |
| // End of the list. Reuse this item. Expand list. | |
| // Testpoint 3. | |
| last = match.next; | |
| lastPrev = match; | |
| match.xPos = x; | |
| match.covDelta = 0; | |
| match.pixelCoverage = 0; | |
| // Keep link to last element or null, indicating the end of the list. | |
| current = match; | |
| currentPrev = prev; | |
| return match; | |
| } | |
| if (x == match.xPos) | |
| { | |
| // Special case: We have another coverage entry at the same location | |
| // as an already existing entry. Return this. | |
| // Testpoint 4. | |
| current = match; | |
| currentPrev = prev; | |
| return match; | |
| } | |
| else // x <= match.xPos | |
| { | |
| assert (x <= match.xPos); | |
| assert (prev == null ||x > prev.xPos); | |
| // Create new entry, or reuse existing one. | |
| Coverage cov; | |
| if (last != null) | |
| { | |
| // Testpoint 5. | |
| cov = last; | |
| last = cov.next; | |
| lastPrev.next = last; | |
| } | |
| else | |
| { | |
| // Testpoint 6. | |
| cov = new Coverage(); | |
| } | |
| cov.xPos = x; | |
| cov.covDelta = 0; | |
| cov.pixelCoverage = 0; | |
| // Insert this item in the list. | |
| if (prev != null) | |
| { | |
| // Testpoint 5 & 6. | |
| prev.next = cov; | |
| cov.next = match; | |
| current = cov; | |
| currentPrev = prev; | |
| } | |
| else | |
| { | |
| // Testpoint 7. | |
| assert (match == head); | |
| // Insert at head. | |
| head = cov; | |
| head.next = match; | |
| current = head; | |
| currentPrev = null; | |
| } | |
| return cov; | |
| } | |
| } | |
| /** | |
| * (Re-)Starts iterating the coverage values for the scanline. | |
| * Use the returned iterator to get the consecutive coverage ranges. | |
| * | |
| * @return the iterator | |
| */ | |
| public Iterator iterate() | |
| { | |
| iterator.reset(); | |
| return iterator; | |
| } | |
| /** | |
| * Returns {@ true} if this object has no entries for the current scanline, | |
| * {@ false} otherwise. | |
| * | |
| * @return {@ true} if this object has no entries for the current scanline, | |
| * {@ false} otherwise | |
| */ | |
| public boolean isEmpty() | |
| { | |
| return head == null || head == last | |
| || head.next == null || head.next == last; | |
| } | |
| } |