This implements the edge detection for the local feature analysis in the
TTF autohinter. This also links the autohinter into the TrueType
implementation so that it could in theory really hint outlines right
away. There's still some feature detection stuff missing though.

2006-12-14  Roman Kennke  <[EMAIL PROTECTED]>

        * gnu/java/awt/font/autofit/AutoHinter.java
        (hints): New field.
        (applyHints): New method. Implements the actual hinting.
        * gnu/java/awt/font/autofit/AxisHints.java
        (edges): New field.
        (AxisHints): Initialize edges field.
        (newEdge): New method. Records a new edge and sorts it into the
        existing list.
        * gnu/java/awt/font/autofit/Edge.java: New class.
        * gnu/java/awt/font/autofit/GlyphHints.java
        (GlyphHints): Initialize the scales with 1.
        (doHorizontal): New method.
        (doVertical): New method.
        * gnu/java/awt/font/autofit/Latin.java
        (alignEdgePoints): New stub method.
        (alignStrongPoints): New stub method.
        (alignWeakPoints): New stub method.
        (applyHints): Take outline as argument. Implemented skeleton.
        (computeBlueEdges): New stub method.
        (computeEdges): New method. Detects edges on a glyph outline.
        (detectFeatures): New methods. Performs local feature analysis.
        (hintEdges): New stub method.
        (initBlues): Remove debug output.
        * gnu/java/awt/font/autofit/LatinAxis.java
        (edgeDistanceThreshold): Changed to be an int
        (as fixed-point decimal).
        * gnu/java/awt/font/autofit/Script.java
        (applyHints): Include the outline in the method call.
        * gnu/java/awt/font/autofit/Segment.java
        (FLAG_EDGE_NORMAL): Set value to 0.
        (FLAG_EDGE_SERIF): New constant.
        (FLAG_EDGE_DONE): New constant.
        (edge): New field.
        (edgeNext): New field.
        * gnu/java/awt/font/opentype/Hinter.java
        (applyHints): New method. Applies the hints to the specified outline.
        * gnu/java/awt/font/opentype/OpenTypeFont.java
        (getGlyphOutline): Check the hinter and call the scaler with the
        hinter.
        * gnu/java/awt/font/opentype/Scaler.java
        (getOutline): Also pass a Hinter.
        * gnu/java/awt/font/opentype/truetype/GlyphLoader.java
        (loadCompoundGlyph): Also accept a hinter argument.
        (loadGlyph): Also accept a hinter argument.
        (loadSimpleGlyph): Also accept a hinter argument. Hint the
        resulting outline.
        (loadSubGlyph): Also accept a hinter argument.
        * gnu/java/awt/font/opentype/truetype/TrueTypeScaler.java
        (getOutline): Accept hinter and pass it to the loader.
        (getRawOutline): Pass null hinter to the loader.

/Roman

Index: gnu/java/awt/font/autofit/AutoHinter.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/awt/font/autofit/AutoHinter.java,v
retrieving revision 1.1
diff -u -1 -5 -r1.1 AutoHinter.java
--- gnu/java/awt/font/autofit/AutoHinter.java	13 Dec 2006 22:59:43 -0000	1.1
+++ gnu/java/awt/font/autofit/AutoHinter.java	14 Dec 2006 20:32:00 -0000
@@ -28,33 +28,42 @@
 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.font.autofit;
 
 import gnu.java.awt.font.opentype.Hinter;
 import gnu.java.awt.font.opentype.OpenTypeFont;
+import gnu.java.awt.font.opentype.truetype.Zone;
 
 /**
  * The public interface to the automatic gridfitter.
  */
 public class AutoHinter
   implements Hinter
 {
   Latin latinScript;
   LatinMetrics metrics;
+  GlyphHints hints;
 
   public void init(OpenTypeFont font)
   {
     // TODO: Should support other scripts too.
     latinScript = new Latin();
     metrics = new LatinMetrics(font);
     latinScript.initMetrics(metrics, font);
   }
+
+  public void applyHints(Zone outline)
+  {
+    if (hints == null)
+      hints = new GlyphHints();
+    latinScript.applyHints(hints, outline, metrics);
+  }
 }
Index: gnu/java/awt/font/autofit/AxisHints.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/awt/font/autofit/AxisHints.java,v
retrieving revision 1.2
diff -u -1 -5 -r1.2 AxisHints.java
--- gnu/java/awt/font/autofit/AxisHints.java	13 Dec 2006 22:59:43 -0000	1.2
+++ gnu/java/awt/font/autofit/AxisHints.java	14 Dec 2006 20:32:00 -0000
@@ -33,38 +33,64 @@
 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.font.autofit;
 
 class AxisHints
 {
 
   Segment[] segments;
   int majorDir;
   int numSegments;
   int numEdges;
+  Edge[] edges;
 
   AxisHints()
   {
     segments = new Segment[4];
+    edges = new Edge[4];
   }
 
   Segment newSegment()
   {
     if (numSegments >= segments.length)
       {
         // Grow array.
         int newMax = segments.length;
         newMax += (newMax >> 2) + 4; // From FreeType.
         Segment[] newSegs = new Segment[newMax];
         System.arraycopy(segments, 0, newSegs, 0, numSegments);
         segments = newSegs;
       }
     Segment seg = new Segment();
     segments[numSegments] = seg;
     numSegments++;
     return seg;
   }
+
+  public Edge newEdge(int pos)
+  {
+    if (numEdges >= edges.length)
+      {
+        // Grow array.
+        int newMax = edges.length;
+        newMax += (newMax >> 2) + 4; // From FreeType.
+        Edge[] newEdges = new Edge[newMax];
+        System.arraycopy(edges, 0, newEdges, 0, numEdges);
+        edges = newEdges;
+      }
+    int edgeIndex = numEdges;
+    Edge edge = edges[edgeIndex] = new Edge();
+    while (edgeIndex > 0 && edges[edgeIndex].fpos > pos)
+      {
+        edges[edgeIndex] = edges[edgeIndex - 1];
+        edgeIndex--;
+      }
+    edges[edgeIndex] = edge;
+    numEdges++;
+    edge.fpos = pos;
+    return edge;
+  }
 }
Index: gnu/java/awt/font/autofit/Edge.java
===================================================================
RCS file: gnu/java/awt/font/autofit/Edge.java
diff -N gnu/java/awt/font/autofit/Edge.java
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gnu/java/awt/font/autofit/Edge.java	14 Dec 2006 20:32:00 -0000
@@ -0,0 +1,70 @@
+/* Edge.java -- An edge of segments
+   Copyright (C) 2006 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.font.autofit;
+
+class Edge
+{
+  int fpos;
+  Segment first;
+  Segment last;
+  int opos;
+  Edge link;
+  Edge serif;
+  int flags;
+  int dir;
+
+  public String toString()
+  {
+    StringBuilder s = new StringBuilder();
+    s.append("[Edge] id");
+    s.append(hashCode());
+    s.append(", fpos: ");
+    s.append(fpos);
+    s.append(", opos: ");
+    s.append(opos);
+    s.append(", dir: ");
+    s.append(dir);
+    s.append(", serif: ");
+    s.append(serif != null ? serif.hashCode() : "null");
+    s.append(", link: ");
+    s.append(link != null ? link.hashCode() : "null");
+    s.append(", flags: " + flags);
+    return s.toString();
+  }
+}
Index: gnu/java/awt/font/autofit/GlyphHints.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/awt/font/autofit/GlyphHints.java,v
retrieving revision 1.2
diff -u -1 -5 -r1.2 GlyphHints.java
--- gnu/java/awt/font/autofit/GlyphHints.java	13 Dec 2006 22:59:43 -0000	1.2
+++ gnu/java/awt/font/autofit/GlyphHints.java	14 Dec 2006 20:32:00 -0000
@@ -26,30 +26,31 @@
 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.font.autofit;
 
+import gnu.java.awt.font.opentype.truetype.Fixed;
 import gnu.java.awt.font.opentype.truetype.Point;
 import gnu.java.awt.font.opentype.truetype.Zone;
 
 /**
  * The data and methods used for the actual hinting process.
  */
 class GlyphHints
   implements Constants
 {
 
   int xScale;
   int xDelta;
   int yScale;
   int yDelta;
 
@@ -59,30 +60,33 @@
   int numPoints;
   int maxPoints;
 
   Point[] contours;
   int numContours;
   int maxContours;
 
   ScriptMetrics metrics;
 
   
   GlyphHints()
   {
     axis = new AxisHints[Constants.DIMENSION_MAX];
     axis[Constants.DIMENSION_VERT] = new AxisHints();
     axis[Constants.DIMENSION_HORZ] = new AxisHints();
+
+    xScale = Fixed.ONE;
+    yScale = Fixed.ONE;
   }
 
   void rescale(ScriptMetrics m)
   {
     metrics = m;
     // TODO: Copy scalerFlags.
   }
   
   void reload(Zone outline)
   {
     numPoints = 0;
     numContours = 0;
     axis[0].numSegments = 0;
     axis[0].numEdges = 0;
     axis[1].numSegments = 0;
@@ -264,16 +268,25 @@
                 do
                   {
                     start.addFlags(Point.FLAG_INFLECTION);
                     start = start.getNext();
                   } while (start != end);
                 start.addFlags(Point.FLAG_INFLECTION);
               }
             start = end;
             end = after;
             angleSeg = angleOut;
             diffIn = diffOut;
           } while (! finished);
       }
   }
 
+  boolean doHorizontal()
+  {
+    return true; // Check scaler flags here.
+  }
+
+  boolean doVertical()
+  {
+    return true; // Check scaler flags here.
+  }
 }
Index: gnu/java/awt/font/autofit/Latin.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/awt/font/autofit/Latin.java,v
retrieving revision 1.3
diff -u -1 -5 -r1.3 Latin.java
--- gnu/java/awt/font/autofit/Latin.java	14 Dec 2006 15:14:12 -0000	1.3
+++ gnu/java/awt/font/autofit/Latin.java	14 Dec 2006 20:32:01 -0000
@@ -30,30 +30,31 @@
 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.font.autofit;
 
 import java.awt.geom.AffineTransform;
 import java.util.HashSet;
 
 import gnu.java.awt.font.opentype.OpenTypeFont;
+import gnu.java.awt.font.opentype.truetype.Fixed;
 import gnu.java.awt.font.opentype.truetype.Point;
 import gnu.java.awt.font.opentype.truetype.Zone;
 
 /**
  * Implements Latin specific glyph handling.
  */
 class Latin
   implements Script, Constants
 {
 
   static final int MAX_WIDTHS = 16;
 
   private final static int MAX_TEST_CHARS = 12;
 
   /**
@@ -64,34 +65,82 @@
   private static final int SMALL_F_TOP = 2;
   private static final int SMALL_TOP = 3;
   private static final int SMALL_BOTTOM = 4;
   private static final int SMALL_MINOR = 5;
   static final int BLUE_MAX = 6;
 
   /**
    * The test chars for the blue zones.
    *
    * @see #initBlues(LatinMetrics, OpenTypeFont)
    */
   private static final String[] TEST_CHARS =
     new String[]{"THEZOCQS", "HEZLOCUS", "fijkdbh",
                  "xzroesc", "xzroesc", "pqgjy"};
 
-  public void applyHints(GlyphHints hints, ScriptMetrics metrics)
+  public void applyHints(GlyphHints hints, Zone outline, ScriptMetrics metrics)
+  {
+    hints.reload(outline);
+    hints.rescale(metrics);
+    if (hints.doHorizontal())
+      {
+        detectFeatures(hints, DIMENSION_HORZ);
+      }
+    if (hints.doVertical())
+      {
+        detectFeatures(hints, DIMENSION_VERT);
+        computeBlueEdges(hints, (LatinMetrics) metrics);
+      }
+    // Grid-fit the outline.
+    for (int dim = 0; dim < DIMENSION_MAX; dim++)
+      {
+        if (dim == DIMENSION_HORZ && hints.doHorizontal()
+            || dim == DIMENSION_VERT && hints.doVertical())
+          {
+            hintEdges(hints, dim);
+            alignEdgePoints(hints, dim);
+            alignStrongPoints(hints, dim);
+            alignWeakPoints(hints, dim);
+            
+         }
+      }
+    // FreeType does a save call here. I guess that's not needed as we operate
+    // on the live glyph data anyway.
+  }
+
+  private void alignWeakPoints(GlyphHints hints, int dim)
   {
     // TODO Auto-generated method stub
+    
+  }
 
+  private void alignStrongPoints(GlyphHints hints, int dim)
+  {
+    // TODO Auto-generated method stub
+    
+  }
+
+  private void alignEdgePoints(GlyphHints hints, int dim)
+  {
+    // TODO Auto-generated method stub
+    
+  }
+
+  private void hintEdges(GlyphHints hints, int dim)
+  {
+    // TODO Auto-generated method stub
+    
   }
 
   public void doneMetrics(ScriptMetrics metrics)
   {
     // TODO Auto-generated method stub
 
   }
 
   /**
    * Initializes the <code>hints</code> object.
    *
    * @param hints the hints to initialize
    * @param metrics the metrics to use
    */
   public void initHints(GlyphHints hints, ScriptMetrics metrics)
@@ -414,31 +463,31 @@
               {
                 blue.shoot = blue.ref = new Width((shoot + ref) / 2);
               }
           }
         blue.flags = 0;
         if (isTopBlue(bb))
           blue.flags |= LatinBlue.FLAG_TOP;
         // The following flag is used later to adjust y and x scales in 
         // order to optimize the pixel grid alignment of the top small
         // letters.
         if (bb == SMALL_TOP)
           {
             blue.flags |= LatinBlue.FLAG_ADJUSTMENT;
           }
         // Debug: print out the blue zones.
-        System.err.println("blue zone #" + bb + ": " + blue);
+        // System.err.println("blue zone #" + bb + ": " + blue);
       }
   }
 
   private static final AffineTransform IDENTITY = new AffineTransform();
 
   private int constant(LatinMetrics metrics, int c)
   {
     return c * (metrics.unitsPerEm / 2048);
   }
 
   private void computeSegments(GlyphHints hints, int dim)
   {
     Point[] points = hints.points;
     if (dim == DIMENSION_HORZ)
       {
@@ -543,16 +592,194 @@
                 segment.score = 32000;
                 segment.len = 0;
                 segment.link = null;
                 onEdge = true;
               }
             point = point.getNext();
           }
       }
     
   }
 
   private boolean isTopBlue(int b)
   {
     return b == CAPITAL_TOP || b == SMALL_F_TOP || b == SMALL_TOP;
   }
+
+  private void detectFeatures(GlyphHints hints, int dim)
+  {
+    computeSegments(hints, dim);
+    linkSegments(hints, dim);
+    computeEdges(hints, dim);
+  }
+
+  private void computeEdges(GlyphHints hints, int dim)
+  {
+    AxisHints axis = hints.axis[dim];
+    LatinAxis laxis = ((LatinMetrics) hints.metrics).axis[dim];
+    Segment[] segments = axis.segments;
+    int numSegments = axis.numSegments;
+    Segment seg;
+    int upDir;
+    int scale;
+    int edgeDistanceThreshold;
+    axis.numEdges = 0;
+    scale = dim == DIMENSION_HORZ ? hints.xScale : hints.yScale;
+    upDir = dim == DIMENSION_HORZ ? DIR_UP : DIR_RIGHT;
+
+    // We will begin by generating a sorted table of edges for the
+    // current direction. To do so, we simply scan each segment and try
+    // to find an edge in our table that corresponds to its position.
+    //
+    // If no edge is found, we create one and insert a new edge in the
+    // sorted table. Otherwise, we simply add the segment to the egde's
+    // list which will be processed in the second step to compute the
+    // edge's properties.
+    //
+    // Note that the edge table is sorted along the segment/edge
+    // position.
+
+    edgeDistanceThreshold = Fixed.mul(laxis.edgeDistanceTreshold, scale);
+    if (edgeDistanceThreshold > 64 / 4)
+      edgeDistanceThreshold = 64 / 4;
+    edgeDistanceThreshold = Fixed.div(edgeDistanceThreshold, scale);
+
+    for (int i = 0; i < numSegments; i++)
+      {
+        seg = segments[i];
+        Edge found = null;
+        for (int ee = 0; ee < axis.numEdges; ee++)
+          {
+            Edge edge = axis.edges[ee];
+            int dist = seg.pos - edge.fpos;
+            if (dist < 0)
+              dist = -dist;
+            if (dist < edgeDistanceThreshold)
+              {
+                found = edge;
+                break;
+              }
+          }
+        if (found == null)
+          {
+            // Insert new edge in the list and sort according to
+            // the position.
+            Edge edge = axis.newEdge(seg.pos);
+            edge.first = seg;
+            edge.last = seg;
+            edge.fpos = seg.pos;
+            edge.opos = Fixed.mul(seg.pos, scale);
+            seg.edgeNext = seg;
+            seg.edge = edge;
+          }
+        else
+          {
+            seg.edgeNext = found.first;
+            found.last.edgeNext = seg;
+            found.last = seg;
+            seg.edge = found;
+          }
+      }
+    // Good. We will now compute each edge's properties according to
+    // segments found on its position. Basically these are:
+    // - Edge's main direction.
+    // - Stem edge, serif edge, or both (which defaults to stem edge).
+    // - Rounded edge, straight or both (which defaults to straight).
+    // - Link for edge.
+
+    // Now, compute each edge properties.
+    for (int e = 0; e < axis.numEdges; e++)
+      {
+        Edge edge = axis.edges[e];
+        // Does it contain round segments?
+        int isRound = 0;
+        // Does it contain straight segments?
+        int isStraight = 0;
+        // Number of upward segments.
+        int ups = 0;
+        // Number of downward segments.
+        int downs = 0;
+
+        seg = edge.first;
+        do
+          {
+            // Check for roundness of segment.
+            if ((seg.flags & Segment.FLAG_EDGE_ROUND) != 0)
+              isRound++;
+            else
+              isStraight++;
+
+            // Check for segment direction.
+            if (seg.dir == upDir)
+              ups += seg.maxPos - seg.minPos;
+            else
+              downs += seg.maxPos - seg.minPos;
+
+            // Check for links. If seg.serif is set, then seg.link must
+            // be ignored.
+            boolean isSerif = seg.serif != null && seg.serif.edge != edge;
+            if (seg.link != null || isSerif)
+              {
+                Edge edge2 = edge.link;
+                Segment seg2 = seg.link;
+                if (isSerif)
+                  {
+                    seg2 = seg.serif;
+                    edge2 = edge.serif;
+                  }
+                if (edge2 != null)
+                  {
+                    int edgeDelta = edge.fpos - edge2.fpos;
+                    if (edgeDelta < 0)
+                      edgeDelta = -edgeDelta;
+                    int segDelta = seg.pos - seg2.pos;
+                    if (segDelta < 0)
+                      segDelta = -segDelta;
+                    if (segDelta < edgeDelta)
+                      edge2 = seg2.edge;
+                  }
+                else
+                  {
+                    edge2 = seg2.edge;
+                  }
+                if (isSerif)
+                  {
+                    edge.serif = edge2;
+                    edge2.flags |= Segment.FLAG_EDGE_SERIF;
+                  }
+                else
+                  {
+                    edge.link = edge2;
+                  }
+              }
+            seg = seg.edgeNext;
+          } while (seg != edge.first);
+        edge.flags = Segment.FLAG_EDGE_NORMAL;
+        if (isRound > 0 && isRound > isStraight)
+          edge.flags |= Segment.FLAG_EDGE_ROUND;
+
+        // Set the edge's main direction.
+        edge.dir = DIR_NONE;
+        if (ups > downs)
+          edge.dir = upDir;
+        else if (ups < downs)
+          edge.dir = -upDir;
+        else if (ups == downs)
+          edge.dir = 0;
+
+        // Gets rid of serif if link is set. This gets rid of many
+        // unpleasant artifacts.
+        if (edge.serif != null && edge.link != null)
+          {
+            edge.serif = null;
+          }
+
+        // Debug: Print out all edges.
+        // System.err.println("edge# " + e + ": " + edge);
+      }        
+  }
+
+  private void computeBlueEdges(GlyphHints hints, LatinMetrics metrics)
+  {
+    // TODO: Implement.
+  }
 }
Index: gnu/java/awt/font/autofit/LatinAxis.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/awt/font/autofit/LatinAxis.java,v
retrieving revision 1.3
diff -u -1 -5 -r1.3 LatinAxis.java
--- gnu/java/awt/font/autofit/LatinAxis.java	14 Dec 2006 15:14:12 -0000	1.3
+++ gnu/java/awt/font/autofit/LatinAxis.java	14 Dec 2006 20:32:01 -0000
@@ -37,24 +37,24 @@
 
 
 package gnu.java.awt.font.autofit;
 
 /**
  * Some axis specific data.
  */
 class LatinAxis
 {
 
   int scale;
   int delta;
 
   int widthCount;
   Width[] widths;
-  float edgeDistanceTreshold;
+  int edgeDistanceTreshold;
   LatinBlue[] blues;
   int blueCount;
   LatinAxis()
   {
     widths = new Width[Latin.MAX_WIDTHS];
     blues = new LatinBlue[Latin.BLUE_MAX];
   }
 }
Index: gnu/java/awt/font/autofit/Script.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/awt/font/autofit/Script.java,v
retrieving revision 1.1
diff -u -1 -5 -r1.1 Script.java
--- gnu/java/awt/font/autofit/Script.java	14 Nov 2006 15:18:09 -0000	1.1
+++ gnu/java/awt/font/autofit/Script.java	14 Dec 2006 20:32:01 -0000
@@ -27,36 +27,36 @@
 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.font.autofit;
 
 import gnu.java.awt.font.opentype.OpenTypeFont;
+import gnu.java.awt.font.opentype.truetype.Zone;
 
 /**
  * Defines script specific methods for the auto fitter.
  */
 interface Script
 {
 
   /**
    * Initializes the metrics.
    */
   void initMetrics(ScriptMetrics metrics, OpenTypeFont face);
 
   void scaleMetrics(ScriptMetrics metrics/* , scaler, map this */);
 
   void doneMetrics(ScriptMetrics metrics);
 
   void initHints(GlyphHints hints, ScriptMetrics metrics);
 
-  void applyHints(GlyphHints hints, /* some outline object, */
-                  ScriptMetrics metrics);
+  void applyHints(GlyphHints hints, Zone outline, ScriptMetrics metrics);
 }
Index: gnu/java/awt/font/autofit/Segment.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/awt/font/autofit/Segment.java,v
retrieving revision 1.2
diff -u -1 -5 -r1.2 Segment.java
--- gnu/java/awt/font/autofit/Segment.java	13 Dec 2006 22:59:43 -0000	1.2
+++ gnu/java/awt/font/autofit/Segment.java	14 Dec 2006 20:32:01 -0000
@@ -31,45 +31,49 @@
 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.font.autofit;
 
 import gnu.java.awt.font.opentype.truetype.Point;
 
 class Segment
 {
 
+  static final int FLAG_EDGE_NORMAL = 0;
   static final int FLAG_EDGE_ROUND = 1;
-  static final int FLAG_EDGE_NORMAL = 2;
+  static final int FLAG_EDGE_SERIF = 2;
+  static final int FLAG_EDGE_DONE = 4;
   int dir;
   int flags;
   Segment link;
   Segment serif;
   int numLinked;
   int pos;
   Point first;
   Point last;
   Point contour;
   int minPos;
   int maxPos;
   int score;
   int len;
+  Segment edgeNext;
+  Edge edge;
 
   public String toString()
   {
     StringBuilder s = new StringBuilder();
     s.append("[Segment] id: ");
     s.append(hashCode());
     s.append(", len:");
     s.append(len);
     s.append(", round: ");
     s.append(((flags & FLAG_EDGE_ROUND) != 0));
     s.append(", dir: ");
     s.append(dir);
     s.append(", pos: ");
     s.append(pos);
     s.append(", minPos: ");
Index: gnu/java/awt/font/opentype/Hinter.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/awt/font/opentype/Hinter.java,v
retrieving revision 1.1
diff -u -1 -5 -r1.1 Hinter.java
--- gnu/java/awt/font/opentype/Hinter.java	13 Dec 2006 22:59:43 -0000	1.1
+++ gnu/java/awt/font/opentype/Hinter.java	14 Dec 2006 20:32:01 -0000
@@ -26,27 +26,36 @@
 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.font.opentype;
 
+import gnu.java.awt.font.opentype.truetype.Zone;
+
 /**
  * The interface to a hinting implementation.
  */
 public interface Hinter
 {
   /**
    * Initializes the hinter.
    *
    * @param face the font for which the hinter should be used
    */
   void init(OpenTypeFont face);
+
+  /**
+   * Hints the specified outline.
+   *
+   * @param outline the outline to hint
+   */
+  void applyHints(Zone outline);
 }
Index: gnu/java/awt/font/opentype/OpenTypeFont.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/awt/font/opentype/OpenTypeFont.java,v
retrieving revision 1.3
diff -u -1 -5 -r1.3 OpenTypeFont.java
--- gnu/java/awt/font/opentype/OpenTypeFont.java	13 Dec 2006 22:59:43 -0000	1.3
+++ gnu/java/awt/font/opentype/OpenTypeFont.java	14 Dec 2006 20:32:01 -0000
@@ -687,32 +687,33 @@
    * metrics, <code>false</code> for rounding the result to a pixel
    * boundary.
    *
    * @return the scaled and grid-fitted outline of the specified
    * glyph, or <code>null</code> for bitmap fonts.
    */
   public synchronized GeneralPath getGlyphOutline(int glyph,
                                                   float pointSize,
                                                   AffineTransform transform,
                                                   boolean antialias,
                                                   boolean fractionalMetrics)
   {
     /* The synchronization is needed because the scaler is not
      * synchronized.
      */
+    checkHinter();
     return scaler.getOutline(glyph, pointSize, transform,
-                             antialias, fractionalMetrics);
+                             antialias, fractionalMetrics, hinter);
   }
 
   /**
    * Fetches the raw glyph outline for the specified glyph index. This is used
    * for the autofitter only ATM and is otherwise not usable for outside code.
    *
    * @param glyph the glyph index to fetch
    * @param transform the transform to apply
    *
    * @return the raw outline of that glyph
    */
   public synchronized Zone getRawGlyphOutline(int glyph,
                                               AffineTransform transform)
   {
     return scaler.getRawOutline(glyph, transform);
Index: gnu/java/awt/font/opentype/Scaler.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/awt/font/opentype/Scaler.java,v
retrieving revision 1.2
diff -u -1 -5 -r1.2 Scaler.java
--- gnu/java/awt/font/opentype/Scaler.java	14 Nov 2006 15:18:09 -0000	1.2
+++ gnu/java/awt/font/opentype/Scaler.java	14 Dec 2006 20:32:01 -0000
@@ -78,31 +78,32 @@
    * glyphs.
    *
    * @param antialias whether or not the rasterizer will perform
    * anti-aliasing on the returned path.
    *
    * @param fractionalMetrics <code>false</code> for adjusting glyph
    * positions to the raster grid of device space.
    *
    * @return the scaled and grid-fitted outline of the specified
    * glyph, or <code>null</code> for bitmap fonts.
    */
   public abstract GeneralPath getOutline(int glyph,
                                          float pointSize,
                                          AffineTransform transform,
                                          boolean antialias,
-                                         boolean fractionalMetrics);
+                                         boolean fractionalMetrics,
+                                         Hinter hinter);
 
 
   /**
    * Determines the advance width and height for a glyph.
    *
    * @param glyphIndex the glyph whose advance width is to be
    * determined.
    *
    * @param pointSize the point size of the font.
    *
    * @param transform a transform that is applied in addition to
    * scaling to the specified point size. This is often used for
    * scaling according to the device resolution. Those who lack any
    * aesthetic sense may also use the transform to slant or stretch
    * glyphs.
Index: gnu/java/awt/font/opentype/truetype/GlyphLoader.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/awt/font/opentype/truetype/GlyphLoader.java,v
retrieving revision 1.2
diff -u -1 -5 -r1.2 GlyphLoader.java
--- gnu/java/awt/font/opentype/truetype/GlyphLoader.java	14 Nov 2006 15:18:09 -0000	1.2
+++ gnu/java/awt/font/opentype/truetype/GlyphLoader.java	14 Dec 2006 20:32:01 -0000
@@ -26,30 +26,32 @@
 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.font.opentype.truetype;
 
+import gnu.java.awt.font.opentype.Hinter;
+
 import java.awt.geom.AffineTransform;
 import java.nio.ByteBuffer;
 
 
 /**
  * A class for loading scaled and hinted glyph outlines.
  *
  * <p><b>Lack of Thread Safety:</b> Glyph loaders are intentionally
  * <i>not</i> safe to access from multiple concurrent
  * threads. Synchronization needs to be performed externally. Usually,
  * the font has already obtained a lock before calling the scaler,
  * which in turn calls the GlyphLoader. It would thus be wasteful to
  * acquire additional locks for the GlyphLoader.
  *
  * @author Sascha Brawer ([EMAIL PROTECTED])
@@ -100,148 +102,151 @@
     this.vm = vm;
 
     contourEndPoints = new int[maxContours];
     pointFlags = new byte[maxPoints];
   }
 
 
   /**
    * @param glyphIndex the number of the glyph whose outlines are to be
    * retrieved.
    */
   public void loadGlyph(int glyphIndex,
                         double pointSize,
                         AffineTransform transform,
                         boolean antialias,
-                        Zone glyphZone)
+                        Zone glyphZone, Hinter hinter)
   {
     glyphZone.setNumPoints(4);
     loadSubGlyph(glyphIndex, pointSize, transform, antialias, glyphZone,
-                 0, 0);
+                 0, 0, hinter);
   }
 
   public void loadGlyph(int glyphIndex, AffineTransform transform,
-                        Zone glyphZone)
+                        Zone glyphZone, Hinter hinter)
   {
-    loadGlyph(glyphIndex, unitsPerEm, transform, false, glyphZone);
+    loadGlyph(glyphIndex, unitsPerEm, transform, false, glyphZone, hinter);
   }
 
   private void loadSubGlyph(int glyphIndex,
                             double pointSize,
                             AffineTransform transform,
                             boolean antialias,
                             Zone glyphZone,
                             int preTranslateX,
-                            int preTranslateY)
+                            int preTranslateY,
+                            Hinter hinter)
   {
     ByteBuffer glyph;
     int numContours;
     int xMin, yMin, xMax, yMax;
     byte flag;
 
     glyph = glyphLocator.getGlyphData(glyphIndex);
 
     if (glyph == null)
     {
       glyphZone.setNumPoints(4);
       setPhantomPoints(glyphIndex, 0, glyphZone);
       glyphZone.transform(pointSize, transform, unitsPerEm,
                           preTranslateX, preTranslateY);
       return;
     }
 
     numContours = glyph.getShort();
     xMin = glyph.getChar();
     yMin = glyph.getChar();
     xMax = glyph.getChar();
     yMax = glyph.getChar();
     
 
     if (numContours >= 0)
       loadSimpleGlyph(glyphIndex, pointSize, transform, antialias,
                       numContours, glyph, glyphZone,
-                      preTranslateX, preTranslateY);
+                      preTranslateX, preTranslateY, hinter);
     else
       loadCompoundGlyph(glyphIndex, pointSize, transform, antialias,
                         glyph, glyphZone,
-                        preTranslateX, preTranslateY);
+                        preTranslateX, preTranslateY, hinter);
   }
 
 
   private void loadSimpleGlyph(int glyphIndex,
                                double pointSize, AffineTransform transform,
                                boolean antialias,
                                int numContours, ByteBuffer glyph,
                                Zone glyphZone,
-                               int preTranslateX, int preTranslateY)
+                               int preTranslateX, int preTranslateY,
+                               Hinter hinter)
   {
     int numPoints;
     int posInstructions, numInstructions;
     boolean execInstructions;
 
     execInstructions = vm.setup(pointSize, transform, antialias);
 
     /* Load the contour end points and determine the number of
      * points.
      */
     for (int i = 0; i < numContours; i++)
       contourEndPoints[i] = glyph.getChar();
     if (numContours > 0)
       numPoints = 1 + contourEndPoints[numContours - 1];
     else
       numPoints = 0;
     glyphZone.setNumPoints(numPoints + 4);
 
     numInstructions = glyph.getChar();
     posInstructions = glyph.position();
     glyph.position(posInstructions + numInstructions);
     loadFlags(numPoints, glyph);
     loadCoordinates(numPoints, glyph, glyphZone);
     for (int i = 0; i < numContours; i++)
       glyphZone.setContourEnd(contourEndPoints[i], true);
 
     setPhantomPoints(glyphIndex, numPoints, glyphZone);
     glyphZone.transform(pointSize, transform, unitsPerEm,
                         preTranslateX, preTranslateY);
 
-    if (execInstructions)
-    {
-      // FIXME: Hint the glyph.
-    }
+    if (execInstructions && hinter != null)
+      {
+        hinter.applyHints(glyphZone);
+      }
   }
 
 
   private static final short ARGS_ARE_WORDS = 1;
   private static final short ARGS_ARE_XY_VALUES = 2;
   private static final short ROUND_XY_TO_GRID = 4;
   private static final short WE_HAVE_A_SCALE = 8;
   private static final short MORE_COMPONENTS = 32;
   private static final short WE_HAVE_AN_X_AND_Y_SCALE = 64;
   private static final short WE_HAVE_A_TWO_BY_TWO = 128;
   private static final short WE_HAVE_INSTRUCTIONS = 256;
   private static final short USE_MY_METRICS = 512;
   private static final short OVERLAP_COMPOUND = 1024;
   private static final short SCALED_COMPONENT_OFFSET = 2048;
   private static final short UNSCALED_COMPONENT_OFFSET = 4096;
 
   private void loadCompoundGlyph(int glyphIndex,
                                  double pointSize,
                                  AffineTransform transform,
                                  boolean antialias,
                                  ByteBuffer glyph,
                                  Zone glyphZone,
-                                 int preTranslateX, int preTranslateY)
+                                 int preTranslateX, int preTranslateY,
+                                 Hinter hinter)
   {
     short flags;
     int subGlyphIndex;
     int metricsGlyphIndex;
     Zone subGlyphZone = new Zone(glyphZone.getCapacity());
     int arg1, arg2;
     double a, b, c, d, e, f;
     AffineTransform componentTransform = new AffineTransform();
 
     /* By default, use the metrics of the compound glyph. The default
      * is overridden if some component glyph has the USE_MY_METRICS
      * flag set.
      */
     metricsGlyphIndex = glyphIndex;
 
@@ -314,31 +319,31 @@
       else
         e = f = 0.0;
 
       componentTransform.setTransform(a, b, c, d, 0.0, 0.0);
       
       // System.out.println("componentTransform = " + componentTransform
       //   + ", e=" + e + ", f=" + f);
       componentTransform.concatenate(transform);
 
       int pos = glyph.position();
       int lim = glyph.limit();
       
       loadSubGlyph(subGlyphIndex, pointSize, componentTransform,
                    antialias, subGlyphZone,
                    Math.round((float) e + preTranslateX),
-                   Math.round(-((float) f + preTranslateY)));
+                   Math.round(-((float) f + preTranslateY)), hinter);
       glyphZone.combineWithSubGlyph(subGlyphZone, 4);
       glyph.limit(lim).position(pos);
     }
     while ((flags & MORE_COMPONENTS) != 0);
 
     setPhantomPoints(metricsGlyphIndex, glyphZone.getSize() - 4, glyphZone);
   }
 
 
   private double getDouble214(ByteBuffer buf)
   {
     return ((double) buf.getShort()) / (1 << 14);
   }
 
 
Index: gnu/java/awt/font/opentype/truetype/TrueTypeScaler.java
===================================================================
RCS file: /cvsroot/classpath/classpath/gnu/java/awt/font/opentype/truetype/TrueTypeScaler.java,v
retrieving revision 1.2
diff -u -1 -5 -r1.2 TrueTypeScaler.java
--- gnu/java/awt/font/opentype/truetype/TrueTypeScaler.java	14 Nov 2006 15:18:09 -0000	1.2
+++ gnu/java/awt/font/opentype/truetype/TrueTypeScaler.java	14 Dec 2006 20:32:01 -0000
@@ -25,30 +25,31 @@
 
 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.font.opentype.truetype;
 
+import gnu.java.awt.font.opentype.Hinter;
 import gnu.java.awt.font.opentype.Scaler;
 
 import java.awt.FontFormatException;
 import java.awt.geom.AffineTransform;
 import java.awt.geom.GeneralPath;
 import java.awt.geom.Point2D;
 import java.nio.ByteBuffer;
 
 
 /**
  * A scaler for fonts whose outlines are described in the TrueType
  * format.
  *
  * <p><b>Lack of Thread Safety:</b> Font scalers are intentionally
  * <i>not</i> safe to access from multiple concurrent threads.
@@ -179,41 +180,41 @@
    *
    * @param pointSize the point size for the glyph.
    *
    * @param deviceTransform an affine transformation for the device.
    *
    * @param antialias whether or not the rasterizer will perform
    * anti-aliasing on the returned path.
    *
    * @param fractionalMetrics <code>false</code> for adjusting glyph
    * positions to the raster grid of device space.
    */
   public GeneralPath getOutline(int glyphIndex,
                                 float pointSize,
                                 AffineTransform deviceTransform,
                                 boolean antialias,
-                                boolean fractionalMetrics)
+                                boolean fractionalMetrics, Hinter hinter)
   {
     glyphLoader.loadGlyph(glyphIndex, pointSize, deviceTransform,
-                          antialias, glyphZone);
+                          antialias, glyphZone, hinter);
     return glyphZone.getPath();
   }
 
   public Zone getRawOutline(int glyphIndex, AffineTransform transform)
   {
     Zone zone = new Zone(glyphZone.getCapacity());
-    glyphLoader.loadGlyph(glyphIndex, transform, zone);
+    glyphLoader.loadGlyph(glyphIndex, transform, zone, null);
     return zone;
   }
 
   /**
    * Determines the advance width and height for a glyph.
    *
    * @param glyphIndex the glyph whose advance width and height is to
    * be determined.
    *
    * @param pointSize the point size of the font.
    *
    * @param transform a transform that is applied in addition to
    * scaling to the specified point size. This is often used for
    * scaling according to the device resolution. Those who lack any
    * aesthetic sense may also use the transform to slant or stretch

Reply via email to