|
From: <ls...@us...> - 2007-01-07 05:35:57
|
Revision: 2989
http://jnode.svn.sourceforge.net/jnode/?rev=2989&view=rev
Author: lsantha
Date: 2007-01-06 21:35:55 -0800 (Sat, 06 Jan 2007)
Log Message:
-----------
Classpath patches.
Modified Paths:
--------------
trunk/core/src/classpath/gnu/gnu/java/awt/font/FontDelegate.java
trunk/core/src/classpath/gnu/gnu/java/awt/font/GNUGlyphVector.java
trunk/core/src/classpath/gnu/gnu/java/awt/font/autofit/AxisHints.java
trunk/core/src/classpath/gnu/gnu/java/awt/font/autofit/Constants.java
trunk/core/src/classpath/gnu/gnu/java/awt/font/autofit/GlyphHints.java
trunk/core/src/classpath/gnu/gnu/java/awt/font/autofit/Latin.java
trunk/core/src/classpath/gnu/gnu/java/awt/font/autofit/LatinAxis.java
trunk/core/src/classpath/gnu/gnu/java/awt/font/autofit/LatinMetrics.java
trunk/core/src/classpath/gnu/gnu/java/awt/font/autofit/Script.java
trunk/core/src/classpath/gnu/gnu/java/awt/font/autofit/ScriptMetrics.java
trunk/core/src/classpath/gnu/gnu/java/awt/font/autofit/Segment.java
trunk/core/src/classpath/gnu/gnu/java/awt/font/autofit/Width.java
trunk/core/src/classpath/gnu/gnu/java/awt/font/opentype/OpenTypeFont.java
trunk/core/src/classpath/gnu/gnu/java/awt/font/opentype/Scaler.java
trunk/core/src/classpath/gnu/gnu/java/awt/font/opentype/truetype/Fixed.java
trunk/core/src/classpath/gnu/gnu/java/awt/font/opentype/truetype/GlyphLoader.java
trunk/core/src/classpath/gnu/gnu/java/awt/font/opentype/truetype/TrueTypeScaler.java
trunk/core/src/classpath/gnu/gnu/java/awt/font/opentype/truetype/Zone.java
trunk/core/src/classpath/gnu/gnu/java/awt/font/opentype/truetype/ZonePathIterator.java
Added Paths:
-----------
trunk/core/src/classpath/gnu/gnu/java/awt/font/autofit/AutoHinter.java
trunk/core/src/classpath/gnu/gnu/java/awt/font/autofit/Edge.java
trunk/core/src/classpath/gnu/gnu/java/awt/font/autofit/HintScaler.java
trunk/core/src/classpath/gnu/gnu/java/awt/font/autofit/LatinBlue.java
trunk/core/src/classpath/gnu/gnu/java/awt/font/autofit/Utils.java
trunk/core/src/classpath/gnu/gnu/java/awt/font/opentype/Hinter.java
trunk/core/src/classpath/gnu/gnu/java/awt/font/opentype/truetype/Point.java
Removed Paths:
-------------
trunk/core/src/classpath/gnu/gnu/java/awt/font/autofit/Scaler.java
Modified: trunk/core/src/classpath/gnu/gnu/java/awt/font/FontDelegate.java
===================================================================
--- trunk/core/src/classpath/gnu/gnu/java/awt/font/FontDelegate.java 2007-01-05 20:55:34 UTC (rev 2988)
+++ trunk/core/src/classpath/gnu/gnu/java/awt/font/FontDelegate.java 2007-01-07 05:35:55 UTC (rev 2989)
@@ -61,6 +61,13 @@
*/
public interface FontDelegate
{
+ public static final int FLAG_FITTED = 1 << 0;
+ public static final int FLAG_NO_HINT_HORIZONTAL = 1 << 1;
+ public static final int FLAG_NO_HINT_VERTICAL = 1 << 2;
+ public static final int FLAG_NO_HINT_EDGE_POINTS = 1 << 3;
+ public static final int FLAG_NO_HINT_STRONG_POINTS = 1 << 4;
+ public static final int FLAG_NO_HINT_WEAK_POINTS = 1 << 5;
+
/**
* Returns the full name of this font face in the specified
* locale, for example <i>“Univers Light”</i>.
@@ -221,7 +228,8 @@
float pointSize,
AffineTransform transform,
boolean antialias,
- boolean fractionalMetrics);
+ boolean fractionalMetrics,
+ int type);
/**
Modified: trunk/core/src/classpath/gnu/gnu/java/awt/font/GNUGlyphVector.java
===================================================================
--- trunk/core/src/classpath/gnu/gnu/java/awt/font/GNUGlyphVector.java 2007-01-05 20:55:34 UTC (rev 2988)
+++ trunk/core/src/classpath/gnu/gnu/java/awt/font/GNUGlyphVector.java 2007-01-07 05:35:55 UTC (rev 2989)
@@ -164,7 +164,9 @@
renderContext.usesFractionalMetrics(),
/* horizontal */ true,
advance);
- pos[p] = x += advance.x;
+ // FIXME: We shouldn't round here, but instead hint the metrics
+ // correctly.
+ pos[p] = x += Math.round(advance.x);
pos[p + 1] = y += advance.y;
}
valid = true;
@@ -284,7 +286,23 @@
return outline;
}
+ public Shape getOutline(float x, float y, int type)
+ {
+ validate();
+ GeneralPath outline = new GeneralPath();
+ int len = glyphs.length;
+ for (int i = 0; i < len; i++)
+ {
+ GeneralPath p = new GeneralPath(getGlyphOutline(i, type));
+ outline.append(p, false);
+ }
+ AffineTransform t = new AffineTransform();
+ t.translate(x, y);
+ outline.transform(t);
+ return outline;
+ }
+
/**
* Determines the shape of the specified glyph.
*
@@ -309,7 +327,8 @@
path = fontDelegate.getGlyphOutline(glyphs[glyphIndex], fontSize, tx,
renderContext.isAntiAliased(),
- renderContext.usesFractionalMetrics());
+ renderContext.usesFractionalMetrics(),
+ FontDelegate.FLAG_FITTED);
tx = new AffineTransform();
tx.translate(pos[glyphIndex * 2], pos[glyphIndex * 2 + 1]);
@@ -317,7 +336,33 @@
return path;
}
+ public Shape getGlyphOutline(int glyphIndex, int type)
+ {
+ AffineTransform tx, glyphTx;
+ GeneralPath path;
+ validate();
+
+ if ((transforms != null)
+ && ((glyphTx = transforms[glyphIndex]) != null))
+ {
+ tx = new AffineTransform(transform);
+ tx.concatenate(glyphTx);
+ }
+ else
+ tx = transform;
+
+ path = fontDelegate.getGlyphOutline(glyphs[glyphIndex], fontSize, tx,
+ renderContext.isAntiAliased(),
+ renderContext.usesFractionalMetrics(),
+ type);
+
+ tx = new AffineTransform();
+ tx.translate(pos[glyphIndex * 2], pos[glyphIndex * 2 + 1]);
+ path.transform(tx);
+ return path;
+ }
+
/**
* Determines the position of the specified glyph, or the
* total advance width and height of the vector.
Added: trunk/core/src/classpath/gnu/gnu/java/awt/font/autofit/AutoHinter.java
===================================================================
--- trunk/core/src/classpath/gnu/gnu/java/awt/font/autofit/AutoHinter.java (rev 0)
+++ trunk/core/src/classpath/gnu/gnu/java/awt/font/autofit/AutoHinter.java 2007-01-07 05:35:55 UTC (rev 2989)
@@ -0,0 +1,83 @@
+/* AutoHinter.java -- The entry point into the hinter implementation.
+ 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;
+
+import gnu.java.awt.font.opentype.Hinter;
+import gnu.java.awt.font.opentype.OpenTypeFont;
+import gnu.java.awt.font.opentype.truetype.Fixed;
+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;
+
+ HintScaler scaler = new HintScaler();
+ public void init(OpenTypeFont font)
+ {
+ // TODO: Should support other scripts too.
+ latinScript = new Latin();
+ metrics = new LatinMetrics(font);
+ latinScript.initMetrics(metrics, font);
+ scaler.face = font;
+ }
+
+ public void applyHints(Zone outline)
+ {
+ if (hints == null)
+ hints = new GlyphHints();
+ scaler.xScale = Fixed.valueOf16(outline.scaleX * 64);
+ scaler.yScale = Fixed.valueOf16(outline.scaleY * 64);
+ latinScript.scaleMetrics(metrics, scaler);
+ latinScript.applyHints(hints, outline, metrics);
+ }
+
+ public void setFlags(int flags)
+ {
+ if (hints == null)
+ hints = new GlyphHints();
+ hints.flags = flags;
+ }
+
+}
Modified: trunk/core/src/classpath/gnu/gnu/java/awt/font/autofit/AxisHints.java
===================================================================
--- trunk/core/src/classpath/gnu/gnu/java/awt/font/autofit/AxisHints.java 2007-01-05 20:55:34 UTC (rev 2988)
+++ trunk/core/src/classpath/gnu/gnu/java/awt/font/autofit/AxisHints.java 2007-01-07 05:35:55 UTC (rev 2989)
@@ -1,4 +1,4 @@
-/* AxisHints.java -- FIXME: briefly describe file purpose
+/* AxisHints.java -- Hints specific to an axis
Copyright (C) 2006 Free Software Foundation, Inc.
This file is part of GNU Classpath.
@@ -42,4 +42,71 @@
{
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 - 1].fpos > pos)
+ {
+ edges[edgeIndex] = edges[edgeIndex - 1];
+ edgeIndex--;
+ }
+ edges[edgeIndex] = edge;
+ numEdges++;
+ edge.fpos = pos;
+
+ return edge;
+
+ }
+
+ int getEdgeIndex(Edge edge2)
+ {
+ int idx = -1;
+ for (int i = 0; i < numEdges; i++)
+ {
+ if (edges[i] == edge2)
+ {
+ idx = i;
+ break;
+ }
+ }
+ return idx;
+ }
}
Modified: trunk/core/src/classpath/gnu/gnu/java/awt/font/autofit/Constants.java
===================================================================
--- trunk/core/src/classpath/gnu/gnu/java/awt/font/autofit/Constants.java 2007-01-05 20:55:34 UTC (rev 2988)
+++ trunk/core/src/classpath/gnu/gnu/java/awt/font/autofit/Constants.java 2007-01-07 05:35:55 UTC (rev 2989)
@@ -58,4 +58,29 @@
* The number of dimensions.
*/
static final int DIMENSION_MAX = 2;
+
+ /**
+ * Indicates a vector with no specific direction.
+ */
+ static final int DIR_NONE = 0;
+
+ /**
+ * Right direction.
+ */
+ static final int DIR_RIGHT = 1;
+
+ /**
+ * Left direction.
+ */
+ static final int DIR_LEFT = -1;
+
+ /**
+ * Up direction.
+ */
+ static final int DIR_UP = 2;
+
+ /**
+ * Down direction.
+ */
+ static final int DIR_DOWN = -2;
}
Added: trunk/core/src/classpath/gnu/gnu/java/awt/font/autofit/Edge.java
===================================================================
--- trunk/core/src/classpath/gnu/gnu/java/awt/font/autofit/Edge.java (rev 0)
+++ trunk/core/src/classpath/gnu/gnu/java/awt/font/autofit/Edge.java 2007-01-07 05:35:55 UTC (rev 2989)
@@ -0,0 +1,80 @@
+/* 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;
+ Width blueEdge;
+ int pos;
+ int scale;
+
+ 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(", pos: ");
+ s.append(pos);
+ 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);
+ s.append(", blue: " + blueEdge);
+ s.append(", first: ");
+ s.append(first == null ? "null" : first.hashCode());
+ s.append(", last: ");
+ s.append(last == null ? "null" : last.hashCode());
+ return s.toString();
+ }
+}
Modified: trunk/core/src/classpath/gnu/gnu/java/awt/font/autofit/GlyphHints.java
===================================================================
--- trunk/core/src/classpath/gnu/gnu/java/awt/font/autofit/GlyphHints.java 2007-01-05 20:55:34 UTC (rev 2988)
+++ trunk/core/src/classpath/gnu/gnu/java/awt/font/autofit/GlyphHints.java 2007-01-07 05:35:55 UTC (rev 2989)
@@ -38,12 +38,16 @@
package gnu.java.awt.font.autofit;
+import gnu.java.awt.font.FontDelegate;
+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;
@@ -53,23 +57,584 @@
AxisHints[] axis;
- void rescale(ScriptMetrics metrics)
+ Point[] points;
+ int numPoints;
+ int maxPoints;
+
+ Point[] contours;
+ int numContours;
+ int maxContours;
+
+ ScriptMetrics metrics;
+
+ int flags;
+
+ GlyphHints()
{
- // TODO: Implement.
+ 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)
{
- // TODO: Implement.
+ numPoints = 0;
+ numContours = 0;
+ axis[0].numSegments = 0;
+ axis[0].numEdges = 0;
+ axis[1].numSegments = 0;
+ axis[1].numEdges = 0;
+
+ // Create/reallocate the contours array.
+ int newMax = outline.getNumContours();
+ if (newMax > maxContours || contours == null)
+ {
+ newMax = (newMax + 3) & ~3; // Taken from afhints.c .
+ Point[] newContours = new Point[newMax];
+ if (contours != null)
+ {
+ System.arraycopy(contours, 0, newContours, 0, maxContours);
+ }
+ contours = newContours;
+ maxContours = newMax;
+ }
+
+ // Create/reallocate the points array.
+ newMax = outline.getSize() + 2;
+ if (newMax > maxPoints || points == null)
+ {
+ newMax = (newMax + 2 + 7) & ~7; // Taken from afhints.c .
+ Point[] newPoints = new Point[newMax];
+ if (points != null)
+ {
+ System.arraycopy(points, 0, newPoints, 0, maxPoints);
+ }
+ points = newPoints;
+ maxPoints = newMax;
+ }
+
+ numPoints = outline.getSize() - 4; // 4 phantom points.
+ numContours = outline.getNumContours();
+
+ // Set major direction. We don't handle Type 1 fonts yet.
+ axis[DIMENSION_HORZ].majorDir = DIR_UP;
+ axis[DIMENSION_VERT].majorDir = DIR_LEFT;
+
+ // TODO: Freetype seems to scale and translate the glyph at that point.
+ // I suppose that this is not really needed.
+ // The scales are scaling from font units to 1/64 device pixels.
+ xScale = Fixed.valueOf16(outline.scaleX * 64);
+ yScale = Fixed.valueOf16(outline.scaleY * 64);
+
+ // FIXME: What is that xDelta and yDelta used for?
+ System.arraycopy(outline.getPoints(), 0, points, 0, numPoints);
+
+ // Setup prev and next and contours array.
+ // TODO: Probably cache this.
+ contours = new Point[numContours];
+ Point currentContour = points[0];
+ for (int i = 0, cIndex = 0; i < numPoints; i++)
+ {
+ // Start new contour when the last point has been a contour end.
+ if (outline.isContourEnd(i))
+ {
+ // Connect the contour end point to the start point.
+ points[i].setNext(currentContour);
+ currentContour.setPrev(points[i]);
+ contours[cIndex] = currentContour;
+ cIndex++;
+ currentContour = i < numPoints - 1 ? points[i + 1] : null;
+ }
+ else
+ {
+ // Connect the current and the previous point.
+ points[i].setNext(points[i + 1]);
+ points[i + 1].setPrev(points[i]);
+ }
+ }
+ // Compute directions of in and out vectors of all points as well
+ // as the weak point flag.
+ for (int i = 0; i < numPoints; i++)
+ {
+ // Compute in and out dir.
+ Point p = points[i];
+ Point prev = p.getPrev();
+ int inX = p.getOrigX() - prev.getOrigX();
+ int inY = p.getOrigY() - prev.getOrigY();
+ p.setInDir(Utils.computeDirection(inX, inY));
+ Point next = p.getNext();
+ int outX = next.getOrigX() - p.getOrigX();
+ int outY = next.getOrigY() - p.getOrigY();
+ p.setOutDir(Utils.computeDirection(outX, outY));
+
+ if (p.isControlPoint())
+ {
+ setWeakPoint(p);
+ }
+ else if (p.getOutDir() == p.getInDir())
+ {
+ if (p.getOutDir() != DIR_NONE)
+ setWeakPoint(p);
+ else
+ {
+ int angleIn = Utils.atan(inY, inX);
+ int angleOut = Utils.atan(outY, outX);
+ int delta = Utils.angleDiff(angleIn, angleOut);
+ if (delta < 2 && delta > -2)
+ setWeakPoint(p);
+ }
+ }
+ else if (p.getInDir() == - p.getOutDir())
+ {
+ setWeakPoint(p);
+ }
+ }
+ computeInflectionPoints();
}
- void computeSegments(int dim)
+ private void setWeakPoint(Point p)
{
- // TODO: Implement.
+ p.setFlags((byte) (p.getFlags() | Point.FLAG_WEAK_INTERPOLATION));
}
- void linkSegments(int dim)
+ /**
+ * Computes the inflection points for a glyph.
+ */
+ private void computeInflectionPoints()
{
- // TODO: Implement.
+ // Do each contour separately.
+ contours : for (int c = 0; c < contours.length; c++)
+ {
+ Point point = contours[c];
+ Point first = point;
+ Point start = point;
+ Point end = point;
+ do
+ {
+ end = end.getNext();
+ if (end == first)
+ continue contours;
+ } while (end.getOrigX() == first.getOrigX()
+ && end.getOrigY() == first.getOrigY());
+
+ // Extend segment start whenever possible.
+ Point before = start;
+ int angleIn;
+ int angleSeg = Utils.atan(end.getOrigX() - start.getOrigX(),
+ end.getOrigY() - start.getOrigY());
+ do
+ {
+ do
+ {
+ start = before;
+ before = before.getPrev();
+ if (before == first)
+ continue contours;
+ } while (before.getOrigX() == start.getOrigX()
+ && before.getOrigY() == start.getOrigY());
+ angleIn = Utils.atan(start.getOrigX() - before.getOrigX(),
+ start.getOrigY() - before.getOrigY());
+ } while (angleIn == angleSeg);
+
+ first = start;
+ int diffIn = Utils.angleDiff(angleIn, angleSeg);
+ // Now, process all segments in the contour.
+ Point after;
+ boolean finished = false;
+ int angleOut, diffOut;
+ do
+ {
+ // First, extend the current segment's end whenever possible.
+ after = end;
+ do
+ {
+ do
+ {
+ end = after;
+ after = after.getNext();
+ if (after == first)
+ finished = true;
+ } while (end.getOrigX() == after.getOrigX()
+ && end.getOrigY() == after.getOrigY());
+ angleOut = Utils.atan(after.getOrigX() - end.getOrigX(),
+ after.getOrigY() - end.getOrigY());
+ } while (angleOut == angleSeg);
+ diffOut = Utils.angleDiff(angleSeg, angleOut);
+ if ((diffIn ^ diffOut) < 0)
+ {
+ // diffIn and diffOut have different signs, we have
+ // inflection points here.
+ 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 (flags & FontDelegate.FLAG_NO_HINT_HORIZONTAL) == 0;
+ }
+
+ boolean doVertical()
+ {
+ return (flags & FontDelegate.FLAG_NO_HINT_VERTICAL) == 0;
+ }
+
+ void alignWeakPoints(int dim)
+ {
+ short touchFlag;
+ Point point;
+ // PASS 1 : Move segments to edge positions.
+ if (dim == DIMENSION_HORZ)
+ {
+ touchFlag = Point.FLAG_DONE_X;
+ for (int p = 0; p < numPoints; p++)
+ {
+ point = points[p];
+ point.setU(point.getX());
+ point.setV(point.getScaledX());
+ }
+ }
+ else
+ {
+ touchFlag = Point.FLAG_DONE_Y;
+ for (int p = 0; p < numPoints; p++)
+ {
+ point = points[p];
+ point.setU(point.getY());
+ point.setV(point.getScaledY());
+ }
+ }
+ point = points[0];
+ for (int c = 0; c < numContours; c++)
+ {
+ point = contours[c];
+ int idx = getPointIndex(point);
+ Point endPoint = point.getPrev();
+ int endIdx = getPointIndex(endPoint);
+ int firstIdx = idx;
+ while (idx <= endIdx
+ && (point.getFlags() & touchFlag) == 0)
+ {
+ idx++;
+ point = points[idx];
+ }
+ if (idx <= endIdx)
+ {
+ int firstTouched = idx;
+ int curTouched = idx;
+ idx++;
+ point = points[idx];
+ while (idx <= endIdx)
+ {
+ if ((point.getFlags() & touchFlag) != 0)
+ {
+ // We found two successive touch points. We interpolate
+ // all contour points between them.
+ iupInterp(curTouched + 1, idx - 1, curTouched, idx);
+ curTouched = idx;
+ }
+ idx++;
+ point = points[idx];
+ }
+ if (curTouched == firstTouched)
+ {
+ // This is a special case: Only one point was touched in the
+ // contour. We thus simply shift the whole contour.
+ iupShift(firstIdx, endIdx, curTouched);
+ }
+ else
+ {
+ // Now interpolate after the last touched point to the end
+ // of the contour.
+ iupInterp(curTouched + 1, endIdx, curTouched, firstTouched);
+ // If the first contour point isn't touched, interpolate
+ // from the contour start to the first touched point.
+ if (firstTouched > 0)
+ {
+ iupInterp(firstIdx, firstTouched - 1, curTouched,
+ firstTouched);
+ }
+ }
+ }
+ }
+ // Now store the values back.
+ if (dim == DIMENSION_HORZ)
+ {
+ for (int p = 0; p < numPoints; p++)
+ {
+ point = points[p];
+ point.setX(point.getU());
+ }
+ }
+ else
+ {
+ for (int p = 0; p < numPoints; p++)
+ {
+ point = points[p];
+ point.setY(point.getU());
+ }
+ }
+ }
+
+ private void iupShift(int p1, int p2, int ref)
+ {
+ int delta = points[ref].getU() - points[ref].getV();
+ for (int p = p1; p < ref; p++)
+ {
+ points[p].setU(points[p].getV() + delta);
+ }
+ for (int p = ref + 1; p <= p2; p++)
+ {
+ points[p].setU(points[p].getV() + delta);
+ }
+ }
+
+ private void iupInterp(int p1, int p2, int ref1, int ref2)
+ {
+ int v1 = points[ref1].getV();
+ int v2 = points[ref2].getV();
+ int d1 = points[ref1].getU() - v1;
+ int d2 = points[ref2].getU() - v2;
+ if (p1 > p2)
+ return;
+ if (v1 == v2)
+ {
+ for (int p = p1; p <= p2; p++)
+ {
+ int u = points[p].getV();
+ if (u <= v1)
+ u += d1;
+ else
+ u += d2;
+ points[p].setU(u);
+ }
+ }
+ else if (v1 < v2)
+ {
+ for (int p = p1; p <= p2; p++)
+ {
+ int u = points[p].getV();
+ if (u <= v1)
+ u += d1;
+ else if (u >= v2)
+ u += d2;
+ else
+ {
+ u = points[ref1].getU() + Utils.mulDiv(u - v1,
+ points[ref2].getU()
+ - points[ref1].getU(),
+ v2 - v1);
+ }
+ points[p].setU(u);
+ }
+ }
+ else
+ {
+ for (int p = p1; p <= p2; p++)
+ {
+ int u = points[p].getV();
+ if (u <= v2)
+ u += d2;
+ else if (u >= v1)
+ u += d1;
+ else
+ {
+ u = points[ref1].getU() + Utils.mulDiv(u - v1,
+ points[ref2].getU()
+ - points[ref1].getU(),
+ v2 - v1);
+ }
+ points[p].setU(u);
+ }
+ }
+ }
+
+ void alignStrongPoints(int dim)
+ {
+ AxisHints ax = axis[dim];
+ Edge[] edges = ax.edges;
+ int numEdges = ax.numEdges;
+ short touchFlag;
+ if (dim == DIMENSION_HORZ)
+ touchFlag = Point.FLAG_DONE_X;
+ else
+ touchFlag = Point.FLAG_DONE_Y;
+
+ if (numEdges > 0)
+ {
+ for (int p = 0; p < numPoints; p++)
+ {
+ Point point = points[p];
+ if ((point.getFlags() & touchFlag) != 0)
+ continue;
+ // If this point is a candidate for weak interpolation, we
+ // interpolate it after all strong points have been processed.
+ if ((point.getFlags() & Point.FLAG_WEAK_INTERPOLATION) != 0
+ && (point.getFlags() & Point.FLAG_INFLECTION) == 0)
+ continue;
+
+ int u, ou, fu, delta;
+ if (dim == DIMENSION_VERT)
+ {
+ u = point.getOrigY();
+ ou = point.getScaledY();
+ }
+ else
+ {
+ u = point.getOrigX();
+ ou = point.getScaledX();
+ }
+ fu = u;
+ // Is the point before the first edge?
+ Edge edge = edges[0];
+ // Inversed vertical dimension.
+ delta = edge.fpos - u;
+ if (delta >= 0)
+ {
+ u = edge.pos - (edge.opos - ou);
+ storePoint(point, u, dim, touchFlag);
+ }
+ else
+ {
+ // Is the point after the last edge?
+ edge = edges[numEdges - 1];
+ delta = u - edge.fpos;
+ if (delta >= 0)
+ {
+ u = edge.pos + (ou - edge.opos);
+ storePoint(point, u, dim, touchFlag);
+ }
+ else
+ {
+ // Find enclosing edges.
+ int min = 0;
+ int max = numEdges;
+ int mid, fpos;
+ boolean found = false;
+ while (min < max)
+ {
+ mid = (max + min) / 2;
+ edge = edges[mid];
+ fpos = edge.fpos;
+ if (u < fpos)
+ max = mid;
+ else if (u > fpos)
+ min = mid + 1;
+ else
+ {
+ // Directly on the edge.
+ u = edge.pos;
+ storePoint(point, u, dim, touchFlag);
+ found = true;
+ break;
+ }
+ }
+ if (! found)
+ {
+ Edge before = edges[min - 1];
+ Edge after = edges[min];
+ if (before.scale == 0)
+ {
+ before.scale = Fixed.div16(after.pos - before.pos,
+ after.fpos - before.fpos);
+ }
+ u = before.pos + Fixed.mul16(fu - before.fpos,
+ before.scale);
+ }
+ storePoint(point, u, dim, touchFlag);
+ }
+ }
+ }
+ }
+ }
+
+ private void storePoint(Point p, int u, int dim, short touchFlag)
+ {
+ if (dim == DIMENSION_HORZ)
+ p.setX(u);
+ else
+ p.setY(u);
+ p.addFlags(touchFlag);
+ }
+
+ void alignEdgePoints(int dim)
+ {
+ AxisHints ax = axis[dim];
+ Edge[] edges = ax.edges;
+ int numEdges = ax.numEdges;
+ for (int e = 0; e < numEdges; e++)
+ {
+ Edge edge = edges[e];
+ Segment seg = edge.first;
+ do
+ {
+ Point point = seg.first;
+ while (true)
+ {
+ if (dim == DIMENSION_HORZ)
+ {
+ point.setX(edge.pos);
+ point.addFlags(Point.FLAG_DONE_X);
+ }
+ else
+ {
+ point.setY(edge.pos);
+ point.addFlags(Point.FLAG_DONE_Y);
+ }
+ if (point == seg.last)
+ break;
+ point = point.getNext();
+ }
+ seg = seg.edgeNext;
+ } while (seg != edge.first);
+ }
+ }
+
+ private int getPointIndex(Point p)
+ {
+ int idx = -1;
+ for (int i = 0; i < numPoints; i++)
+ {
+ if (p == points[i])
+ {
+ idx = i;
+ break;
+ }
+ }
+ return idx;
+ }
+
+ public boolean doAlignEdgePoints()
+ {
+ return (flags & FontDelegate.FLAG_NO_HINT_EDGE_POINTS) == 0;
+ }
+
+ public boolean doAlignStrongPoints()
+ {
+ return (flags & FontDelegate.FLAG_NO_HINT_STRONG_POINTS) == 0;
+ }
+
+ public boolean doAlignWeakPoints()
+ {
+ return (flags & FontDelegate.FLAG_NO_HINT_WEAK_POINTS) == 0;
+ }
}
Added: trunk/core/src/classpath/gnu/gnu/java/awt/font/autofit/HintScaler.java
===================================================================
--- trunk/core/src/classpath/gnu/gnu/java/awt/font/autofit/HintScaler.java (rev 0)
+++ trunk/core/src/classpath/gnu/gnu/java/awt/font/autofit/HintScaler.java 2007-01-07 05:35:55 UTC (rev 2989)
@@ -0,0 +1,53 @@
+/* Scaler.java -- FIXME: briefly describe file purpose
+ 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;
+
+import gnu.java.awt.font.opentype.OpenTypeFont;
+
+class HintScaler
+{
+
+ int xScale;
+ int xDelta;
+ int yScale;
+ int yDelta;
+ OpenTypeFont face;
+ int renderMode;
+
+}
Modified: trunk/core/src/classpath/gnu/gnu/java/awt/font/autofit/Latin.java
===================================================================
--- trunk/core/src/classpath/gnu/gnu/java/awt/font/autofit/Latin.java 2007-01-05 20:55:34 UTC (rev 2988)
+++ trunk/core/src/classpath/gnu/gnu/java/awt/font/autofit/Latin.java 2007-01-07 05:35:55 UTC (rev 2989)
@@ -39,8 +39,11 @@
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;
/**
@@ -50,14 +53,478 @@
implements Script, Constants
{
- private static final int MAX_WIDTHS = 16;
+ static final int MAX_WIDTHS = 16;
- public void applyHints(GlyphHints hints, ScriptMetrics metrics)
+ private final static int MAX_TEST_CHARS = 12;
+
+ /**
+ * The types of the 6 blue zones.
+ */
+ private static final int CAPITAL_TOP = 0;
+ private static final int CAPITAL_BOTTOM = 1;
+ 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, 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);
+ if (hints.doAlignEdgePoints())
+ hints.alignEdgePoints(dim);
+ if (hints.doAlignStrongPoints())
+ hints.alignStrongPoints(dim);
+ if (hints.doAlignWeakPoints())
+ hints.alignWeakPoints(dim);
+
+ }
+ }
+ // FreeType does a save call here. I guess that's not needed as we operate
+ // on the live glyph data anyway.
+ }
+
+ private void hintEdges(GlyphHints hints, int dim)
+ {
+ AxisHints axis = hints.axis[dim];
+ Edge[] edges = axis.edges;
+ int numEdges = axis.numEdges;
+ Edge anchor = null;
+ int hasSerifs = 0;
+
+ // We begin by aligning all stems relative to the blue zone if
+ // needed -- that's only for horizontal edges.
+ if (dim == DIMENSION_VERT)
+ {
+ for (int e = 0; e < numEdges; e++)
+ {
+ Edge edge = edges[e];
+ if ((edge.flags & Segment.FLAG_EDGE_DONE) != 0)
+ continue;
+
+ Width blue = edge.blueEdge;
+ Edge edge1 = null;
+ Edge edge2 = edge.link;
+ if (blue != null)
+ {
+ edge1 = edge;
+ }
+ else if (edge2 != null && edge2.blueEdge != null)
+ {
+ blue = edge2.blueEdge;
+ edge1 = edge2;
+ edge2 = edge;
+ }
+ if (edge1 == null)
+ continue;
+
+ edge1.pos = blue.fit;
+ edge1.flags |= Segment.FLAG_EDGE_DONE;
+
+ if (edge2 != null && edge2.blueEdge == null)
+ {
+ alignLinkedEdge(hints, dim, edge1, edge2);
+ edge2.flags |= Segment.FLAG_EDGE_DONE;
+ }
+ if (anchor == null)
+ anchor = edge;
+ }
+ }
+
+ // Now we will align all stem edges, trying to maintain the
+ // relative order of stems in the glyph.
+ for (int e = 0; e < numEdges; e++)
+ {
+ Edge edge = edges[e];
+ if ((edge.flags & Segment.FLAG_EDGE_DONE) != 0)
+ continue;
+ Edge edge2 = edge.link;
+ if (edge2 == null)
+ {
+ hasSerifs++;
+ continue;
+ }
+ // Now align the stem.
+ // This should not happen, but it's better to be safe.
+ if (edge2.blueEdge != null || axis.getEdgeIndex(edge2) < e)
+ {
+ alignLinkedEdge(hints, dim, edge2, edge);
+ edge.flags |= Segment.FLAG_EDGE_DONE;
+ continue;
+ }
+
+ if (anchor == null)
+ {
+ int orgLen = edge2.opos - edge.opos;
+ int curLen = computeStemWidth(hints, dim, orgLen, edge.flags,
+ edge2.flags);
+ int uOff, dOff, orgCenter, curPos1, error1, error2;
+ if (curLen <= 64) // < 1 Pixel.
+ {
+ uOff = 32;
+ dOff = 32;
+ }
+ else
+ {
+ uOff = 38;
+ dOff = 26;
+ }
+ if (curLen < 96)
+ {
+ orgCenter = edge.opos + (orgLen >> 1);
+ curPos1 = Utils.pixRound(orgCenter);
+ error1 = orgCenter - (curPos1 - uOff);
+ if (error1 < 0)
+ error1 = -error1;
+ error2 = orgCenter - (curPos1 + dOff);
+ if (error2 < 0)
+ error2 = -error2;
+ if (error1 < error2)
+ {
+ curPos1 -= uOff;
+ }
+ else
+ {
+ curPos1 += dOff;
+ }
+ edge.pos = curPos1 - curLen / 2;
+ edge2.pos = curPos1 + curLen / 2;
+ }
+ else
+ {
+ edge.pos = Utils.pixRound(edge.opos);
+ }
+ anchor = edge;
+ edge.flags |= Segment.FLAG_EDGE_DONE;
+ alignLinkedEdge(hints, dim, edge, edge2);
+ }
+ else
+ {
+ int aDiff = edge.opos - anchor.opos;
+ int orgPos = anchor.pos + aDiff;
+ int orgLen = edge2.opos - edge.opos;
+ int orgCenter = orgPos + (orgLen >> 1);
+ int curLen = computeStemWidth(hints, dim, orgLen, edge.flags,
+ edge2.flags);
+ //System.err.println("stem width: " + curLen);
+ if (curLen < 96)
+ {
+ int uOff, dOff;
+ int curPos1 = Utils.pixRound(orgCenter);
+ if (curLen <= 64)
+ {
+ uOff = 32;
+ dOff = 32;
+ }
+ else
+ {
+ uOff = 38;
+ dOff = 26;
+ }
+ int delta1 = orgCenter - (curPos1 - uOff);
+ if (delta1 < 0)
+ delta1 = -delta1;
+ int delta2 = orgCenter - (curPos1 + dOff);
+ if (delta2 < 0)
+ delta2 = -delta2;
+ if (delta1 < delta2)
+ {
+ curPos1 -= uOff;
+ }
+ else
+ {
+ curPos1 += dOff;
+ }
+ edge.pos = curPos1 - curLen / 2;
+ edge2.pos = curPos1 + curLen / 2;
+ }
+ else
+ {
+ orgPos = anchor.pos + (edge.opos - anchor.opos);
+ orgLen = edge2.opos - edge.opos;
+ orgCenter = orgPos + (orgLen >> 1);
+ curLen = computeStemWidth(hints, dim, orgLen, edge.flags,
+ edge2.flags);
+ int curPos1 = Utils.pixRound(orgPos);
+ int delta1 = curPos1 + (curLen >> 1) - orgCenter;
+ if (delta1 < 0)
+ delta1 = -delta1;
+ int curPos2 = Utils.pixRound(orgPos + orgLen) - curLen;
+ int delta2 = curPos2 + (curLen >> 1) - orgCenter;
+ if (delta2 < 0)
+ delta2 = -delta2;
+ edge.pos = (delta1 < delta2) ? curPos1 : curPos2;
+ edge2.pos = edge.pos + curLen;
+ }
+ edge.flags |= Segment.FLAG_EDGE_DONE;
+ edge2.flags |= Segment.FLAG_EDGE_DONE;
+
+ if (e > 0 && edge.pos < edges[e - 1].pos)
+ {
+ edge.pos = edges[e - 1].pos;
+ }
+ }
+ }
+ // TODO: Implement the lowercase m symmetry thing.
+
+ // Now we hint the remaining edges (serifs and singles) in order
+ // to complete our processing.
+ if (hasSerifs > 0 || anchor == null)
+ {
+ for (int e = 0; e < numEdges; e++)
+ {
+ Edge edge = edges[e];
+ if ((edge.flags & Segment.FLAG_EDGE_DONE) != 0)
+ continue;
+ if (edge.serif != null)
+ {
+ alignSerifEdge(hints, edge.serif, edge);
+ }
+ else if (anchor == null)
+ {
+ edge.pos = Utils.pixRound(edge.opos);
+ anchor = edge;
+ }
+ else
+ {
+ edge.pos = anchor.pos
+ + Utils.pixRound(edge.opos - anchor.opos);
+ }
+ edge.flags |= Segment.FLAG_EDGE_DONE;
+
+ if (e > 0 && edge.pos < edges[e - 1].pos)
+ {
+ edge.pos = edges[e - 1].pos;
+ }
+ if (e + 1 < numEdges
+ && (edges[e + 1].flags & Segment.FLAG_EDGE_DONE) != 0
+ && edge.pos > edges[e + 1].pos)
+ {
+ edge.pos = edges[e + 1].pos;
+ }
+ }
+ }
+
+ // Debug: print all hinted edges.
+ // System.err.println("hinted edges: " );
+ // for (int i = 0; i < numEdges; i++)
+ // {
+ // System.err.println("edge#" + i + ": " + edges[i]);
+ // }
+ }
+
+ private void alignSerifEdge(GlyphHints hints, Edge base, Edge serif)
+ {
+ serif.pos = base.pos + (serif.opos - base.opos);
+ }
+
+ private int computeStemWidth(GlyphHints hints, int dim, int width,
+ int baseFlags, int stemFlags)
+ {
+ LatinMetrics metrics = (LatinMetrics) hints.metrics;
+ LatinAxis axis = metrics.axis[dim];
+ int dist = width;
+ int sign = 0;
+ boolean vertical = dim == DIMENSION_VERT;
+ if (! doStemAdjust(hints))
+ return width;
+ if (dist < 0)
+ {
+ dist = -width;
+ sign = 1;
+ }
+ if ((vertical && ! doVertSnap(hints)) || ! vertical && ! doHorzSnap(hints))
+ {
+ // Smooth hinting process. Very lightly quantize the stem width.
+ // Leave the widths of serifs alone.
+ if ((stemFlags & Segment.FLAG_EDGE_SERIF) != 0 && vertical
+ && dist < 3 * 64)
+ {
+ return doneWidth(dist, sign);
+ }
+ else if ((baseFlags & Segment.FLAG_EDGE_ROUND) != 0)
+ {
+ if (dist < 80)
+ dist = 64;
+ }
+ else if (dist < 56)
+ {
+ dist = 56;
+ }
+ if (axis.widthCount > 0)
+ {
+ int delta;
+ if (axis.widthCount > 0)
+ {
+ delta = dist - axis.widths[0].cur;
+ if (delta < 0)
+ {
+ delta = -delta;
+ }
+ if (delta < 40)
+ {
+ dist = axis.widths[0].cur;
+ if (dist < 48)
+ dist = 48;
+ return doneWidth(dist, sign);
+ }
+ }
+ if (dist < 3 * 64) // < 3 pixels.
+ {
+ delta = dist & 63;
+ dist &= -64;
+ if (delta < 10)
+ dist += delta;
+ else if (delta < 32)
+ dist += 10;
+ else if (delta < 54)
+ dist += 54;
+ else
+ dist += delta;
+
+ }
+ else
+ {
+ dist = (dist + 32) & ~63;
+ }
+ }
+ }
+ else
+ {
+ // Strong hinting process: Snap the stem width to integer pixels.
+ dist = snapWidth(axis.widths, axis.widthCount, dist);
+ if (vertical)
+ {
+ // In the case of vertical hinting, always round
+ // the stem heights to integer pixels.
+ if (dist >= 64)
+ dist = (dist + 16) & ~63;
+ else
+ dist = 64;
+ }
+ else
+ {
+ if (doMono(hints))
+ {
+ // Monochrome horizontal hinting: Snap widths to integer pixels
+ // with a different threshold.
+ if (dist < 64)
+ dist = 64;
+ else
+ dist = (dist + 32) & ~63;
+ }
+ else
+ {
+ // For anti-aliased hinting, we adopt a more subtle
+ // approach: We strengthen small stems, round those stems
+ // whose size is between 1 and 2 pixels to an integer,
+ // otherwise nothing.
+ if (dist < 48)
+ dist = (dist + 64) >> 1;
+ else if (dist < 128)
+ dist = (dist + 22) & ~63;
+ else
+ // Round otherwise to prevent color fringes in LCD mode.
+ dist = (dist + 32) & ~63;
+ }
+ }
+ }
+ return doneWidth(dist, sign);
+ }
+
+ private boolean doMono(GlyphHints hints)
+ {
+ return true;
+ }
+
+ private int snapWidth(Width[] widths, int count, int width)
+ {
+ int best = 64 + 32 + 2;
+ int reference = width;
+ for (int n = 0; n < count; n++)
+ {
+ int w = widths[n].cur;
+ int dist = width - w;
+ if (dist < 0)
+ dist = -dist;
+ if (dist < best)
+ {
+ best = dist;
+ reference = w;
+ }
+ }
+ int scaled = Utils.pixRound(reference);
+ if (width >= reference)
+ {
+ if (width < scaled + 48)
+ width = reference;
+ }
+ else
+ {
+ if (width > scaled + 48)
+ width = reference;
+ }
+ return width;
+ }
+
+ private int doneWidth(int w, int s)
+ {
+ if (s == 1)
+ w = -w;
+ return w;
+ }
+
+ private boolean doVertSnap(GlyphHints hints)
+ {
// TODO Auto-generated method stub
+ return true;
+ }
+ private boolean doHorzSnap(GlyphHints hints)
+ {
+ // TODO Auto-generated method stub
+ return true;
}
+ private boolean doStemAdjust(GlyphHints hints)
+ {
+ // TODO Auto-generated method stub
+ return true;
+ }
+
+ private void alignLinkedEdge(GlyphHints hints, int dim, Edge base, Edge stem)
+ {
+ int dist = stem.opos - base.opos;
+ int fitted = computeStemWidth(hints, dim, dist, base.flags, stem.flags);
+ stem.pos = base.pos + fitted;
+ }
+
public void doneMetrics(ScriptMetrics metrics)
{
// TODO Auto-generated method stub
@@ -99,10 +566,119 @@
initBlues(lm, face);
}
- public void scaleMetrics(ScriptMetrics metrics)
+ public void scaleMetrics(ScriptMetrics metrics, HintScaler scaler)
{
- // TODO Auto-generated method stub
+ LatinMetrics lm = (LatinMetrics) metrics;
+ lm.scaler.renderMode = scaler.renderMode;
+ lm.scaler.face = scaler.face;
+ scaleMetricsDim(lm, scaler, DIMENSION_HORZ);
+ scaleMetricsDim(lm, scaler, DIMENSION_VERT);
+ }
+ private void scaleMetricsDim(LatinMetrics lm, HintScaler scaler, int dim)
+ {
+ int scale;
+ int delta;
+ if (dim == DIMENSION_HORZ)
+ {
+ scale = scaler.xScale;
+ delta = scaler.xDelta;
+ }
+ else
+ {
+ scale = scaler.yScale;
+ delta = scaler.yDelta;
+ }
+ LatinAxis axis = lm.axis[dim];
+ if (axis.orgScale == scale && axis.orgDelta == delta)
+ // No change, no need to adjust.
+ return;
+ axis.orgScale = scale;
+ axis.orgDelta = delta;
+
+ // Correct X and Y scale to optimize the alignment of the top small
+ // letters to the pixel grid.
+ LatinAxis axis2 = lm.axis[DIMENSION_VERT];
+ LatinBlue blue = null;
+// for (int nn = 0; nn < axis2.blueCount; nn++)
+// {
+// if ((axis2.blues[nn].flags & LatinBlue.FLAG_ADJUSTMENT) != 0)
+// {
+// blue = axis2.blues[nn];
+// break;
+// }
+// }
+// if (blue != null)
+// {
+// int scaled = Fixed.mul16(blue.shoot.org, scaler.yScale);
+// int fitted = Utils.pixRound(scaled);
+// if (scaled != fitted)
+// {
+// if (dim == DIMENSION_HORZ)
+// {
+// if (fitted < scaled)
+// {
+// scale -= scale / 50;
+// }
+// }
+// else
+// {
+// scale = Utils.mulDiv(scale, fitted, scaled);
+// }
+// }
+// }
+ axis.scale = scale;
+ axis.delta = delta;
+ if (dim == DIMENSION_HORZ)
+ {
+ lm.scaler.xScale = scale;
+ lm.scaler.xDelta = delta;
+ }
+ else
+ {
+ lm.scaler.yScale = scale;
+ lm.scaler.yDelta = delta;
+ }
+ // Scale the standard widths.
+ for (int nn = 0; nn < axis.widthCount; nn++)
+ {
+ Width w = axis.widths[nn];
+ w.cur = Fixed.mul16(w.org, scale);
+ w.fit = w.cur;
+ }
+ // Scale blue zones.
+ if (dim == DIMENSION_VERT)
+ {
+ for (int nn = 0; nn < axis.blueCount; nn++)
+ {
+ blue = axis.blues[nn];
+ blue.ref.cur = Fixed.mul16(blue.ref.org, scale) + delta;
+ blue.ref.fit = blue.ref.cur;
+ blue.shoot.cur = Fixed.mul16(blue.ref.org, scale) + delta;
+ blue.flags &= ~LatinBlue.FLAG_BLUE_ACTIVE;
+ // A blue zone is only active if it is less than 3/4 pixels tall.
+ int dist = Fixed.mul16(blue.ref.org - blue.shoot.org, scale);
+ if (dist <= 48 && dist >= -48)
+ {
+ int delta1 = blue.shoot.org - blue.ref.org;
+ int delta2 = delta1;
+ if (delta1 < 0)
+ delta2 = -delta2;
+ delta2 = Fixed.mul16(delta2, scale);
+ if (delta2 < 32)
+ delta2 = 0;
+ else if (delta2 < 64)
+ delta2 = 32 + (((delta2 - 32) + 16) & ~31);
+ else
+ delta2 = Utils.pixRound(delta2);
+ if (delta1 < 0)
+ delta2 = -delta2;
+ blue.ref.fit = Utils.pixRound(blue.ref.cur);
+ blue.shoot.fit = blue.ref.fit + delta2;
+ blue.flags |= LatinBlue.FLAG_BLUE_ACTIVE;
+ }
+ }
+ }
}
/**
@@ -118,12 +694,9 @@
metrics.axis[DIMENSION_HORZ].widthCount = 0;
metrics.axis[DIMENSION_VERT].widthCount = 0;
int glyphIndex = face.getGlyph(ch);
- // TODO: Avoid that AffineTransform constructor and change
- // getRawGlyphOutline() to accept null or remove that parameter altogether.
- // Consider this when the thing is done and we know what we need that for.
- Zone outline = face.getRawGlyphOutline(glyphIndex, new AffineTransform());
+ Zone outline = face.getRawGlyphOutline(glyphIndex, IDENTITY);
LatinMetrics dummy = new LatinMetrics();
- Scaler scaler = dummy.scaler;
+ HintScaler scaler = dummy.scaler;
dummy.unitsPerEm = metrics.unitsPerEm;
scaler.xScale = scaler.yScale = 10000;
scaler.xDelta = scaler.yDelta = 0;
@@ -135,20 +708,24 @@
LatinAxis axis = metrics.axis[dim];
AxisHints axHints = hints.axis[dim];
int numWidths = 0;
- hints.computeSegments(dim);
- hints.linkSegments(dim);
+ computeSegments(hints, dim);
+ linkSegments(hints, dim);
Segment[] segs = axHints.segments;
+ HashSet<Segment> touched = new HashSet<Segment>();
for (int i = 0; i < segs.length; i++)
{
Segment seg = segs[i];
Segment link = seg.link;
- if (link != null && link.link == seg && link.index > i)
+ if (link != null && link.link == seg && ! touched.contains(link))
{
int dist = Math.abs(seg.pos - link.pos);
if (numWidths < MAX_WIDTHS)
- axis.widths[numWidths++].org = dist;
+ axis.widths[numWidths++] = new Width(dist);
}
+ touched.add(seg);
}
+ Utils.sort(numWidths, axis.widths);
+ axis.widthCount = numWidths;
}
for (int dim = 0; dim < DIMENSION_MAX; dim++)
{
@@ -159,6 +736,78 @@
}
}
+ void linkSegments(GlyphHints hints, int dim)
+ {
+ AxisHints axis = hints.axis[dim];
+ Segment[] segments = axis.segments;
+ int numSegs = axis.numSegments;
+ int majorDir = axis.majorDir;
+ int lenThreshold = constant((LatinMetrics) hints.metrics, 8);
+ lenThreshold = Math.min(1, lenThreshold);
+ int lenScore = constant((LatinMetrics) hints.metrics, 3000);
+ for (int i1 = 0; i1 < numSegs; i1++)
+ {
+ Segment seg1 = segments[i1];
+ // The fake segments are introduced to hint the metrics.
+ // Never link them to anything.
+ if (seg1.first == seg1.last || seg1.dir != majorDir)
+ continue;
+ for (int i2 = 0; i2 < numSegs; i2++)
+ {
+ Segment seg2 = segments[i2];
+ if (seg2 != seg1 && seg1.dir + seg2.dir == 0)
+ {
+ int pos1 = seg1.pos;
+ int pos2 = seg2.pos;
+ // The vertical coords are swapped compared to how FT handles
+ // this.
+ int dist = dim == DIMENSION_VERT ? pos1 - pos2 : pos2 - pos1;
+ if (dist >= 0)
+ {
+ int min = seg1.minPos;
+ int max = seg1.maxPos;
+ int len, score;
+ if (min < seg2.minPos)
+ min = seg2.minPos;
+ if (max > seg2.maxPos)
+ max = seg2.maxPos;
+ len = max - min;
+ if (len > lenThreshold)
+ {
+ score = dist + lenScore / len;
+ if (score < seg1.score)
+ {
+ seg1.score = score;
+ seg1.link = seg2;
+ }
+ if (score < seg2.score)
+ {
+ ...
[truncated message content] |