mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			452 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			452 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			Java
		
	
	
	
| /* ScanlineConverter.java -- Rasterizes Shapes
 | |
|    Copyright (C) 2006, 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;
 | |
| 
 | |
| import gnu.java.math.Fixed;
 | |
| 
 | |
| import java.awt.RenderingHints;
 | |
| import java.awt.Shape;
 | |
| import java.awt.geom.AffineTransform;
 | |
| import java.awt.geom.PathIterator;
 | |
| 
 | |
| /**
 | |
|  * Rasterizes {@link Shape} objects on an AbstractGraphics2D.
 | |
|  */
 | |
| public final class ScanlineConverter
 | |
| {
 | |
| 
 | |
|   /**
 | |
|    * The number of digits to use for fixed point arithmetics.
 | |
|    */
 | |
|   private static int FIXED_DIGITS = 6;
 | |
| 
 | |
|   /**
 | |
|    * The fixed point constant for the number one.
 | |
|    */
 | |
|   private static int ONE = Fixed.fixedValue(FIXED_DIGITS, 1);
 | |
| 
 | |
|   /**
 | |
|    * The actual number of scanlines.
 | |
|    */
 | |
|   private int numScanlines;
 | |
| 
 | |
|   /**
 | |
|    * The number of scanlines. This can contain more elements than we have
 | |
|    * scanlines. The real number of scanlines is stored in
 | |
|    * {@link #numScanlines}. This can also contain null values for empty
 | |
|    * scanlines.
 | |
|    */
 | |
|   private Scanline[] scanlines;
 | |
| 
 | |
|   /**
 | |
|    * The upper bounds which correspond to the index 0 in the scanline array.
 | |
|    *
 | |
|    * This is a fixed point value.
 | |
|    */
 | |
|   private int upperBounds;
 | |
| 
 | |
|   /**
 | |
|    * The resolution of the scanline converter.
 | |
|    *
 | |
|    * This is a fixed point value.
 | |
|    */
 | |
|   private int resolution;
 | |
| 
 | |
|   /**
 | |
|    * The number of significant bits for the 'Y' resolution.
 | |
|    */
 | |
|   private int yResolution;
 | |
| 
 | |
|   /**
 | |
|    * One half step according to the resolution. This is stored to avoid
 | |
|    * unnecessary operations during rendering.
 | |
|    */
 | |
|   private int halfStep;
 | |
| 
 | |
|   /**
 | |
|    * This is used in {@link #addShape(PathIterator, boolean)} to
 | |
|    * receive the coordinates of the path.
 | |
|    */
 | |
|   private float[] coords;
 | |
| 
 | |
|   /**
 | |
|    * The active edges.
 | |
|    */
 | |
|   private ActiveEdges activeEdges;
 | |
| 
 | |
|   private PolyEdge edgePool;
 | |
|   private PolyEdge edgePoolLast;
 | |
| 
 | |
|   private int minY;
 | |
|   private int maxY;
 | |
|   private int minX;
 | |
|   private int maxX;
 | |
| 
 | |
|   /**
 | |
|    * Holds and manages information about the pixel coverage.
 | |
|    */
 | |
|   private ScanlineCoverage scanlineCoverage;
 | |
| 
 | |
|   /**
 | |
|    * Create a new ScanlineConverter.
 | |
|    */
 | |
|   ScanlineConverter()
 | |
|   {
 | |
|     scanlines = new Scanline[10];
 | |
|     coords = new float[6];
 | |
|     activeEdges = new ActiveEdges();
 | |
|     edgePool = new PolyEdge();
 | |
|     edgePoolLast = edgePool;
 | |
|     scanlineCoverage = new ScanlineCoverage();
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Renders the specified shape using the specified clip and transform.
 | |
|    *
 | |
|    * @param p the pixelizer that receives the coverage information
 | |
|    * @param shape the shape to render
 | |
|    * @param clip the clip
 | |
|    * @param trans the transform
 | |
|    */
 | |
|   public void renderShape(Pixelizer p, Shape shape, Shape clip,
 | |
|                           AffineTransform trans, int res, int yRes,
 | |
|                           RenderingHints hints)
 | |
|   {
 | |
|     // TODO: Do something useful with the rendering hints. Like, adjusting
 | |
|     // the resolution.
 | |
| 
 | |
|     // Prepare resolution and upper bounds.
 | |
|     clear();
 | |
|     setResolution(res, yRes);
 | |
| 
 | |
|     boolean haveClip = clip != null;
 | |
| 
 | |
|     // Add shapes.
 | |
|     float flatness = Fixed.floatValue(FIXED_DIGITS, resolution / 2);
 | |
|     PathIterator path = shape.getPathIterator(trans, flatness);
 | |
|     addShape(path, false);
 | |
|     if (haveClip)
 | |
|       {
 | |
|         path= clip.getPathIterator(trans, flatness);
 | |
|         addShape(path, true);
 | |
|       }
 | |
| 
 | |
|     setUpperBounds(minY);
 | |
| 
 | |
|     PolyEdge edge = edgePool;
 | |
|     while (edge != edgePoolLast)
 | |
|       {
 | |
|         addEdge(edge);
 | |
|         edge = edge.poolNext;
 | |
|       }
 | |
| 
 | |
|     int y = upperBounds;
 | |
|     int index;
 | |
|     activeEdges.clear();
 | |
|     // The render loop...
 | |
|     Scanline scanline = null;
 | |
|     int lastRealY = Fixed.intValue(FIXED_DIGITS, y);
 | |
|     while (y <= maxY)
 | |
|       {
 | |
|         // First we put together our list of active edges.
 | |
|         index = scanlineIndex(y);
 | |
|         // If we go outside the scanline array we still need to render the
 | |
|         // remaining edges until they end.
 | |
|         scanline = index < scanlines.length ? scanlines[index] : null;
 | |
|         if (scanline != null)
 | |
|           {
 | |
|             edge = scanline.getEdges();
 | |
|             while (edge != null)
 | |
|               {
 | |
|                 activeEdges.add(edge);
 | |
|                 edge = edge.scanlineNext;
 | |
|               }
 | |
|           }
 | |
| 
 | |
|         // Then we intersect all active edges with the current scanline
 | |
|         // and sort them according to their intersection points.
 | |
|         activeEdges.intersectSortAndPack(FIXED_DIGITS, y + halfStep);
 | |
| 
 | |
|         // Ok, now we can perform the actual scanlining.
 | |
|         int realY = Fixed.intValue(FIXED_DIGITS, y + resolution);
 | |
|         boolean push = lastRealY != realY;
 | |
| 
 | |
|         doScanline(p, y, push, haveClip);
 | |
| 
 | |
|         // Remove obsolete active edges.
 | |
|         //activeEdges.remove(y + halfStep);
 | |
|         // Go on with the next line...
 | |
|         y += resolution;
 | |
|         lastRealY = realY;
 | |
| 
 | |
|       }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Clears all scanlines.
 | |
|    */
 | |
|   private void clear()
 | |
|   {
 | |
|     // Reset edge pool.
 | |
|     edgePoolLast = edgePool;
 | |
| 
 | |
|     // Reset scanlines.
 | |
|     for (int i = scanlines.length - 1; i >= 0 ; i--)
 | |
|       {
 | |
|         Scanline sl = scanlines[i];
 | |
|         if (sl != null)
 | |
|           sl.clear();
 | |
|       }
 | |
| 
 | |
|     // Reset scanline coverage.
 | |
|     scanlineCoverage.clear();
 | |
| 
 | |
|     // Reset bounds.
 | |
|     minY = Integer.MAX_VALUE;
 | |
|     maxY = Integer.MIN_VALUE;
 | |
|     minX = Integer.MAX_VALUE;
 | |
|     maxX = Integer.MIN_VALUE;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Performs the scanlining on the current set of active edges.
 | |
|    *
 | |
|    * @param p the pixelizer to receive the pixel coverage data
 | |
|    * @param y the Y coordinate
 | |
|    * @param push true when the scanline is ready to be pushed to the
 | |
|    *        pixelizer
 | |
|    * @param haveClip true when there's a clip, false otherwise
 | |
|    */
 | |
|   private void doScanline(Pixelizer p, int y, boolean push,
 | |
|                           boolean haveClip)
 | |
|   {
 | |
|     // First, rewind the scanline coverage.
 | |
|     scanlineCoverage.rewind();
 | |
| 
 | |
|     // We begin outside the clip and outside the shape. We only draw when
 | |
|     // we are inside the clip AND inside the shape.
 | |
|     boolean inClip = ! haveClip;
 | |
|     boolean inShape = false;
 | |
|     PolyEdge lastEdge = null;
 | |
|     int numEdges = activeEdges.getNumActiveEdges();
 | |
|     for (int i = 0; i < numEdges; i++)
 | |
|       {
 | |
|         PolyEdge edge = activeEdges.getActiveEdge(i);
 | |
|         if (inClip && inShape)
 | |
|           {
 | |
|             assert lastEdge != null;
 | |
|             int x0 = lastEdge.xIntersection;
 | |
|             int x1 = edge.xIntersection;
 | |
|             assert x0 <= x1;
 | |
| 
 | |
|             int pix0 = Fixed.intValue(FIXED_DIGITS, x0);
 | |
|             int pix1 = Fixed.intValue(FIXED_DIGITS, x1);
 | |
|             int frac0 = ONE - Fixed.trunc(FIXED_DIGITS, x0);
 | |
|             int frac1 = ONE - Fixed.trunc(FIXED_DIGITS, x1);
 | |
|             // Only keep the first 4 digits after the point.
 | |
|             frac0 = frac0 >> (FIXED_DIGITS - yResolution);
 | |
|             frac1 = frac1 >> (FIXED_DIGITS - yResolution);
 | |
|             scanlineCoverage.add(pix0, 1 * (1 << yResolution), frac0);
 | |
|             scanlineCoverage.add(pix1, -1 * (1 << yResolution), -frac1);
 | |
|           }
 | |
|         if (edge.isClip)
 | |
|           inClip = ! inClip;
 | |
|         else
 | |
|           inShape = ! inShape;
 | |
| 
 | |
|         lastEdge = edge;
 | |
|       }
 | |
| 
 | |
|     // Push out the whole scanline to the pixelizer.
 | |
|     if (push && ! scanlineCoverage.isEmpty())
 | |
|       {
 | |
|         p.renderScanline(Fixed.intValue(FIXED_DIGITS, y), scanlineCoverage);
 | |
|         scanlineCoverage.clear();
 | |
|       }
 | |
|   }
 | |
| 
 | |
| 
 | |
|   /**
 | |
|    * Sets the resolution. A value of 0 rasterizes the shape normally without
 | |
|    * anti-aliasing. Greater values renders with a resolution of 2 ^ res.
 | |
|    *
 | |
|    * @param res the resolution
 | |
|    */
 | |
|   private void setResolution(int res, int yRes)
 | |
|   {
 | |
|     int scanlinesPerPixel = 1 << res;
 | |
|     int one = Fixed.fixedValue(FIXED_DIGITS, 1);
 | |
|     resolution = one / (scanlinesPerPixel);
 | |
|     halfStep = resolution / 2;
 | |
| 
 | |
|     scanlineCoverage.setMaxCoverage(scanlinesPerPixel << yResolution);
 | |
| 
 | |
|     yResolution = yRes;
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Sets the vertical bounds of that shape that is beeing rendered.
 | |
|    *
 | |
|    * @param y0 the upper bounds
 | |
|    */
 | |
|   private void setUpperBounds(int y0)
 | |
|   {
 | |
|     upperBounds = fit(y0);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Add a shape to the scanline converter.
 | |
|    *
 | |
|    * @param path
 | |
|    * @param clip
 | |
|    */
 | |
|   private void addShape(PathIterator path, boolean clip)
 | |
|   {
 | |
|     int startX = 0;
 | |
|     int startY = 0;
 | |
|     int lastX = 0;
 | |
|     int lastY = 0;
 | |
|     while (! path.isDone())
 | |
|       {
 | |
|         int type = path.currentSegment(coords);
 | |
|         switch (type)
 | |
|           {
 | |
|             case PathIterator.SEG_MOVETO:
 | |
|               startX = lastX = Fixed.fixedValue(FIXED_DIGITS, coords[0]);
 | |
|               startY = lastY = Fixed.fixedValue(FIXED_DIGITS, coords[1]);
 | |
|               minY = Math.min(startY, minY);
 | |
|               maxY = Math.max(startY, maxY);
 | |
|               minX = Math.min(startX, minX);
 | |
|               maxX = Math.max(startX, maxX);
 | |
|               break;
 | |
|             case PathIterator.SEG_LINETO:
 | |
|               int x = Fixed.fixedValue(FIXED_DIGITS, coords[0]);
 | |
|               int y = Fixed.fixedValue(FIXED_DIGITS, coords[1]);
 | |
|               edgePoolAdd(lastX, lastY, x, y, clip);
 | |
|               lastX = x;
 | |
|               lastY = y;
 | |
|               minY = Math.min(lastY, minY);
 | |
|               maxY = Math.max(lastY, maxY);
 | |
|               minX = Math.min(lastX, minX);
 | |
|               maxX = Math.max(lastX, maxX);
 | |
|               break;
 | |
|             case PathIterator.SEG_CLOSE:
 | |
|               edgePoolAdd(lastX, lastY, startX, startY, clip);
 | |
|               lastX = startX;
 | |
|               lastY = startY;
 | |
|               break;
 | |
|             case PathIterator.SEG_CUBICTO:
 | |
|             case PathIterator.SEG_QUADTO:
 | |
|             default:
 | |
|               assert false;
 | |
|           }
 | |
|         path.next();
 | |
|       }
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Adds an edge into the scanline array.
 | |
|    */
 | |
|   private void addEdge(PolyEdge edge)
 | |
|   {
 | |
|     // Determine index.
 | |
|     int upper = Math.min(edge.y0, edge.y1);
 | |
|     // Fit to raster.
 | |
|     int index = scanlineIndex(upper);
 | |
|     // Grow array when necessary.
 | |
|     if (index >= scanlines.length)
 | |
|       {
 | |
|         int oldSize = scanlines.length;
 | |
|         int newSize = Math.max(oldSize + oldSize / 2 + 1, index + 10);
 | |
|         Scanline[] newScanlines = new Scanline[newSize];
 | |
|         System.arraycopy(scanlines, 0, newScanlines, 0, oldSize);
 | |
|         scanlines = newScanlines;
 | |
|       }
 | |
| 
 | |
|     // Add edge.
 | |
|     if (scanlines[index] == null)
 | |
|       {
 | |
|         scanlines[index] = new Scanline();
 | |
|       }
 | |
|     scanlines[index].addEdge(edge);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Fits an Y coordinate to the grid.
 | |
|    *
 | |
|    * @param y the Y coordinate to fit
 | |
|    *
 | |
|    * @return the fitted Y coordinate
 | |
|    */
 | |
|   private int fit(int y)
 | |
|   {
 | |
|     int val1 = Fixed.div(FIXED_DIGITS, y, resolution);
 | |
|     int rounded = Fixed.round(FIXED_DIGITS, val1);
 | |
|     return Fixed.mul(FIXED_DIGITS, rounded, resolution);
 | |
|   }
 | |
| 
 | |
|   /**
 | |
|    * Calculates the scanline index for the specified y coordinate.
 | |
|    *
 | |
|    * @param y the y coordinate as fixed point value
 | |
|    *
 | |
|    * @return the scanline index
 | |
|    */
 | |
|   private int scanlineIndex(int y)
 | |
|   {
 | |
|     int fitted = fit(y);
 | |
|     // Cleverly skip the fixed point conversions here.
 | |
|     return (fitted - upperBounds)/ resolution;
 | |
|   }
 | |
| 
 | |
|   private void edgePoolAdd(int x0, int y0, int x1, int y1, boolean clip)
 | |
|   {
 | |
|     // Don't need no horizontal edges.
 | |
|     if (y0 != y1)
 | |
|       {
 | |
|         edgePoolLast.init(FIXED_DIGITS, x0, y0, x1, y1, clip);
 | |
|         if (edgePoolLast.poolNext == null)
 | |
|           {
 | |
|             edgePoolLast.poolNext = new PolyEdge();
 | |
|           }
 | |
|         edgePoolLast = edgePoolLast.poolNext;
 | |
|       }
 | |
|   }
 | |
| }
 |