All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.adobe.fontengine.font.QReducer Maven / Gradle / Ivy

There is a newer version: 2024.11.18598.20241113T125352Z-241000
Show newest version
/*
 * File: QReducer.java
 * 
 *	ADOBE CONFIDENTIAL
 *	___________________
 *
 *	Copyright 2006 Adobe Systems Incorporated
 *	All Rights Reserved.
 *
 *	NOTICE: All information contained herein is, and remains the property of
 *	Adobe Systems Incorporated and its suppliers, if any. The intellectual
 *	and technical concepts contained herein are proprietary to Adobe Systems
 *	Incorporated and its suppliers and may be covered by U.S. and Foreign
 *	Patents, patents in process, and are protected by trade secret or
 *	copyright law. Dissemination of this information or reproduction of this
 *	material is strictly forbidden unless prior written permission is
 *      obtained from Adobe Systems Incorporated.
 *
 */

package com.adobe.fontengine.font;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;


/** Scan conversion by overscanning.
 * 
 * This scan converter turns a pixel on as soon as the outline
 * traverses that pixel.
 */

public class QReducer implements ScanConverter {
  
  EdgeBuilder edgeBuilder;
  
  public QReducer () {
    edgeBuilder = new EdgeBuilder ();
    edgeBuilder.setFlatness (1.0d);
  }
 
  public void setScanType (int scanType) {
    // nothing
  }
  
  public OutlineConsumer2 getOutlineConsumer2 () {
    return edgeBuilder;
  }
  
  public void getBitmap (BitmapConsumer bitmapConsumer) {
    finish (bitmapConsumer);
  }
  
  private class EdgeBuilder extends OutlineConsumer2BaseImpl {
  
    private double THRESHOLD = 127.0;
    private double epsilon;
    
    public void setFlatness (double flatness) {
       epsilon = Math.max (1.0d/8192, 1.5d * flatness / 4);
    }

    //--------------------------------------------------------------------------
    public void setScanType (int scanType) {
      // we don't care for that
    }
    
    public void startOutline () {
    }
    
    public void startContour () {
    }
    
    public void line (double x1, double y1, double x2, double y2) {
      if (y1 != y2 || ! isIntegral (y1)) {
        insertEdge (new Edge (x1, y1, x2, y2)); }
    }
    
    public void quadraticCurve (double x1, double y1, double x2, double y2, double x3, double y3) {
      if (   (   (x1 <= x2 && x2 <= x3)
              || (x3 <= x2 && x2 <= x1))
          && (Math.abs (x3 - x1) < THRESHOLD)
          && (Math.abs ((x3 - x1) - 2 * (x2 - x1)) <= epsilon)
          && (   (y1 <= y2 && y2 <= y3)
              || (y3 <= y2 && y2 <= y1))
          && (Math.abs (y3 - y1) < THRESHOLD)
          && (Math.abs ((y3 - y1) - 2 * (y2 - y1)) <= epsilon)) {
    
        line (x1, y1, x3, y3); }
      
      else {
        double x12 = (x2 + x1) / 2.0;     double y12 = (y2 + y1) / 2.0;
        double x23 = (x3 + x2) / 2.0;     double y23 = (y3 + y2) / 2.0;
        double x1223 = (x12 + x23) / 2.0; double y1223 = (y12 + y23) / 2.0;
        
        quadraticCurve (x1, y1, x12, y12, x1223, y1223);
        quadraticCurve (x1223, y1223, x23, y23, x3, y3); }
    }
    
    public void cubicCurve (double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) {
      if (   (   (x1 <= x2 && x2 <= x3 && x3 <= x4)
              || (x4 <= x3 && x3 <= x2 && x2 <= x1))
          && (Math.abs (x4 - x1) < THRESHOLD)
          && (Math.abs ((x4 - x1) - 3 * (x2 - x1)) <= epsilon)
          && (Math.abs ((x4 - x1) - 3 * (x4 - x3)) <= epsilon)
          && (   (y1 <= y2 && y2 <= y3 && y3 <= y4)
              || (y4 <= y3 && y3 <= y2 && y2 <= y1))
          && (Math.abs (y4 - y1) < THRESHOLD)
          && (Math.abs ((y4 - y1) - 3 * (y2 - y1)) <= epsilon)
          && (Math.abs ((y4 - y1) - 3 * (y4 - y3)) <= epsilon)) {
        
        line (x1, y1, x4, y4); }
      
      else {
        double x12 = (x2 + x1) / 2.0; double y12 = (y2 + y1) / 2.0;
        double x23 = (x3 + x2) / 2.0; double y23 = (y3 + y2) / 2.0;
        double x34 = (x4 + x3) / 2.0; double y34 = (y4 + y3) / 2.0;
        
        double x1223 = (x23 + x12) / 2.0; double y1223 = (y23 + y12) / 2.0;
        double x2334 = (x34 + x23) / 2.0; double y2334 = (y34 + y23) / 2.0;
        
        double xx = (x1223 + x2334) / 2.0; double yy = (y1223 + y2334) / 2.0;
        
        cubicCurve (x1, y1, x12, y12, x1223, y1223, xx, yy);       
        cubicCurve (xx, yy, x2334, y2334, x34, y34, x4, y4); }
    }
    
    public void endContour () {
    }
    
    public void endOutline () {
    }
  }
  
 
  private static class Edge implements Comparable {
    final private boolean reversed; // the original line segment goes from smallest y to largest y  
    final private boolean endsAtHorzEdge; // largest y is exactly at pixel boundary
    final private boolean startsAtHorzEdge; // smallest y is exactly at pixel boundary

    final private static int LEFT = -1;
    final private static int SINGLE_VERTICAL = 0;
    final private static int RIGHT = 1;
    final private static int SINGLE_HORIZONTAL = 2;
    final private int lineType;
    
    final private double dx, dy;
    
    final private int endPixelX;
    private double g;
    
    final public int bottomScanLine;
    final public int topScanLine;
    final public boolean atVertEdge; // vertical, and on pixel boundary    
    public int leftPixelInCurrentScanLine, rightPixelInCurrentScanline;
    
    
    public int getCntDelta (int currentScanline) {
      // Returns:
      // 2 if the outline crosses the bottom of the scanline and the 
      //   top
      // 1 if it crosses the bottom or the top only
      // 0 if it is entirely in the scanline
      // the sign is positive if the outline goes up, negative otherwise
      int cntDelta = 2;
      if (bottomScanLine == currentScanline && ! startsAtHorzEdge) {
        cntDelta--; }
      if (topScanLine == currentScanline && ! endsAtHorzEdge) {
        cntDelta--; }
      
      if (! reversed) {
        cntDelta = - cntDelta; }
      
      return cntDelta;
    }
    
    public int pixel (double v) {
      int p = (int) Math.floor (v);
      if (p == v) {
        p--; }
      return p;
    }

    public Edge (double x1, double y1, double x2, double y2) {
      reversed = y1 > y2;
      if (reversed) {
        double tmp = y1; y1 = y2; y2 = tmp;
        tmp = x1; x1 = x2; x2 = tmp; }
      
      dx = x2 - x1;
      dy = y2 - y1;
      
      int startPixelX;
      if (dx < 0.0d) {
        atVertEdge = false;
        startPixelX = pixel (x1);
        endPixelX = (int) Math.floor (x2); }
      else if (dx == 0.0d) {
        atVertEdge = isIntegral (x1);
        startPixelX = pixel (x1);
        endPixelX = startPixelX; }
      else { /* dx > 0 */
        atVertEdge = false;
        endPixelX = pixel (x2);
        startPixelX = (int) Math.floor (x1); }

      bottomScanLine = (int) Math.floor (y1);
      startsAtHorzEdge = (isIntegral (y1));

      topScanLine = pixel (y2);
      endsAtHorzEdge = (isIntegral (y2));

      if (bottomScanLine == topScanLine) {
        lineType = SINGLE_HORIZONTAL;
        leftPixelInCurrentScanLine = Math.min (startPixelX, endPixelX);
        rightPixelInCurrentScanline = Math.max (startPixelX, endPixelX); }
      else if (startPixelX == endPixelX) {
        lineType = SINGLE_VERTICAL;
        leftPixelInCurrentScanLine = startPixelX;
        rightPixelInCurrentScanline = startPixelX; }
      else if (startPixelX < endPixelX) {
        lineType = RIGHT; 
        rightPixelInCurrentScanline = startPixelX;
        leftPixelInCurrentScanLine = startPixelX;       
        g = (x1 - startPixelX - 1.0) - (y1 - bottomScanLine - 1.0) * dx / dy;
        while (g >= 0) {
          rightPixelInCurrentScanline++;
          g -= 1; }}
      else {
        lineType = LEFT; 
        rightPixelInCurrentScanline = startPixelX;
        leftPixelInCurrentScanLine = startPixelX;
        g = (startPixelX - x1) + (y1 - bottomScanLine - 1.0) * dx / dy;
        while (g > 0) { 
          leftPixelInCurrentScanLine--; 
          g -= 1; }}
    }
    
    public Edge moveToNextScanLine (int nextScanline) {
      if (nextScanline > topScanLine) {
        return null; }
      
      if (lineType == SINGLE_VERTICAL) {
        return this; }
      
      if (topScanLine == nextScanline) {
        if (lineType == RIGHT) {
          leftPixelInCurrentScanLine = rightPixelInCurrentScanline; 
          rightPixelInCurrentScanline = endPixelX; }
        else {
          rightPixelInCurrentScanline = leftPixelInCurrentScanLine; 
          leftPixelInCurrentScanLine = endPixelX; }}
      
      else { /* do Bresenham */
        if (lineType == RIGHT) {
          leftPixelInCurrentScanLine = rightPixelInCurrentScanline;
          g += dx / dy;
          while (g >= 0) {
            rightPixelInCurrentScanline++; 
            g -= 1; }
          rightPixelInCurrentScanline = Math.min (rightPixelInCurrentScanline, endPixelX); } 
        else {
          rightPixelInCurrentScanline = leftPixelInCurrentScanLine; 
          g -= dx / dy;
          while (g > 0) {
            leftPixelInCurrentScanLine--; 
            g -= 1;}
          leftPixelInCurrentScanLine = Math.max (leftPixelInCurrentScanLine, endPixelX); }}
      return this;
    }
          
    public int compareTo (Object arg0) {
      Edge e = (Edge) arg0;
      if (e.leftPixelInCurrentScanLine < leftPixelInCurrentScanLine) {
        return +1; }
      else if (e.leftPixelInCurrentScanLine == leftPixelInCurrentScanLine) {
        return 0; }
      else {
        return -1; }
    }    
  }
    

  private static boolean isIntegral (double x) {
    return x == Math.floor (x);
  }
  

  Map/**/ scanLines = new HashMap ();
  int firstScanline = Integer.MAX_VALUE;
  int lastScanline = Integer.MIN_VALUE;
  
  private void insertEdge (Edge  edge) {   
    firstScanline = Math.min (firstScanline, edge.bottomScanLine);
    lastScanline = Math.max (lastScanline, edge.topScanLine);
    
    Integer Y = new Integer (edge.bottomScanLine); 
    List/**/ l = (List/**/) scanLines.get (Y);
    if (l == null) {
      l = new ArrayList/**/ ();
      scanLines.put (Y, l);}
    l.add (edge);
  } 

  static boolean eofill = false;
  
  private Edge[] mergeAndSort (List/**/ l1, Edge[] l2) {
    int ll = (l1 == null) ? 0 : l1.size ();
    for (int i = 0; i < l2.length; i++) {
      if (l2[i] != null) {
        ll++; }}
    Edge[] result = new Edge [ll];
    
    ll = 0;
    
    for (int i = 0; i < l2.length; i++) {
      if (l2 [i] != null) {
        result [ll++] = l2 [i]; }}
       
    if (l1 != null) {
      for (Iterator it = l1.iterator(); it.hasNext(); ) {
        result [ll++] = (Edge) it.next(); }}
    
    Arrays.sort (result);
    
    return result;
  }
  
  private void finish (BitmapConsumer consumer) {
    Edge[] edges = new Edge [0];
    
    for (int y = firstScanline; y <= lastScanline; y++) {
      // add the edges that have y as their min.
      edges = mergeAndSort ((List/**/) scanLines.get (new Integer (y)), edges);
      
      boolean inScan = false;
      double runStart = 0;
      int cnt = 0;
      int e = 0;
      while (e < edges.length) {
        if (!inScan) {
          runStart = edges [e].leftPixelInCurrentScanLine + 1;
          inScan = true; }
        
        double runEnd = edges [e].leftPixelInCurrentScanLine;
        
        while (e < edges.length && edges [e].leftPixelInCurrentScanLine <= runEnd) {
          Edge edge = edges [e];
          if (!(edge.atVertEdge)) {
            runStart = Math.min (runStart, edge.leftPixelInCurrentScanLine); }
          
          runEnd = Math.max (runEnd, edge.rightPixelInCurrentScanline);
          cnt += edge.getCntDelta (y);          
          edges [e] = edge.moveToNextScanLine (y + 1); 
          e++; }
        
        if ((eofill ? cnt & 3 : cnt) == 0) {
          inScan = false; 
          consumer.addRun (runStart, runEnd + 1, y); }}}
  }
  
  //----------------------------------------------------------------------------
//  private ScalerDebugger debugger;
  public void setDebugger (ScalerDebugger outlineDebugger) {
//    this.outlineDebugger = debugger;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy