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;
 | 
						|
      }
 | 
						|
  }
 | 
						|
}
 |