1 /* GlyphHints.java -- Data and methods for actual hinting
2 Copyright (C) 2006 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
39 package gnu.java.awt.font.autofit;
41 import gnu.java.awt.font.FontDelegate;
42 import gnu.java.awt.font.opentype.truetype.Fixed;
43 import gnu.java.awt.font.opentype.truetype.Point;
44 import gnu.java.awt.font.opentype.truetype.Zone;
47 * The data and methods used for the actual hinting process.
68 ScriptMetrics metrics;
74 axis = new AxisHints[Constants.DIMENSION_MAX];
75 axis[Constants.DIMENSION_VERT] = new AxisHints();
76 axis[Constants.DIMENSION_HORZ] = new AxisHints();
82 void rescale(ScriptMetrics m)
85 // TODO: Copy scalerFlags.
88 void reload(Zone outline)
92 axis[0].numSegments = 0;
94 axis[1].numSegments = 0;
97 // Create/reallocate the contours array.
98 int newMax = outline.getNumContours();
99 if (newMax > maxContours || contours == null)
101 newMax = (newMax + 3) & ~3; // Taken from afhints.c .
102 Point[] newContours = new Point[newMax];
103 if (contours != null)
105 System.arraycopy(contours, 0, newContours, 0, maxContours);
107 contours = newContours;
108 maxContours = newMax;
111 // Create/reallocate the points array.
112 newMax = outline.getSize() + 2;
113 if (newMax > maxPoints || points == null)
115 newMax = (newMax + 2 + 7) & ~7; // Taken from afhints.c .
116 Point[] newPoints = new Point[newMax];
119 System.arraycopy(points, 0, newPoints, 0, maxPoints);
125 numPoints = outline.getSize() - 4; // 4 phantom points.
126 numContours = outline.getNumContours();
128 // Set major direction. We don't handle Type 1 fonts yet.
129 axis[DIMENSION_HORZ].majorDir = DIR_UP;
130 axis[DIMENSION_VERT].majorDir = DIR_LEFT;
132 // TODO: Freetype seems to scale and translate the glyph at that point.
133 // I suppose that this is not really needed.
134 // The scales are scaling from font units to 1/64 device pixels.
135 xScale = Fixed.valueOf16(outline.scaleX * 64);
136 yScale = Fixed.valueOf16(outline.scaleY * 64);
138 // FIXME: What is that xDelta and yDelta used for?
139 System.arraycopy(outline.getPoints(), 0, points, 0, numPoints);
141 // Setup prev and next and contours array.
142 // TODO: Probably cache this.
143 contours = new Point[numContours];
144 Point currentContour = points[0];
145 for (int i = 0, cIndex = 0; i < numPoints; i++)
147 // Start new contour when the last point has been a contour end.
148 if (outline.isContourEnd(i))
150 // Connect the contour end point to the start point.
151 points[i].setNext(currentContour);
152 currentContour.setPrev(points[i]);
153 contours[cIndex] = currentContour;
155 currentContour = i < numPoints - 1 ? points[i + 1] : null;
159 // Connect the current and the previous point.
160 points[i].setNext(points[i + 1]);
161 points[i + 1].setPrev(points[i]);
164 // Compute directions of in and out vectors of all points as well
165 // as the weak point flag.
166 for (int i = 0; i < numPoints; i++)
168 // Compute in and out dir.
170 Point prev = p.getPrev();
171 int inX = p.getOrigX() - prev.getOrigX();
172 int inY = p.getOrigY() - prev.getOrigY();
173 p.setInDir(Utils.computeDirection(inX, inY));
174 Point next = p.getNext();
175 int outX = next.getOrigX() - p.getOrigX();
176 int outY = next.getOrigY() - p.getOrigY();
177 p.setOutDir(Utils.computeDirection(outX, outY));
179 if (p.isControlPoint())
183 else if (p.getOutDir() == p.getInDir())
185 if (p.getOutDir() != DIR_NONE)
189 int angleIn = Utils.atan(inY, inX);
190 int angleOut = Utils.atan(outY, outX);
191 int delta = Utils.angleDiff(angleIn, angleOut);
192 if (delta < 2 && delta > -2)
196 else if (p.getInDir() == - p.getOutDir())
201 computeInflectionPoints();
204 private void setWeakPoint(Point p)
206 p.setFlags((byte) (p.getFlags() | Point.FLAG_WEAK_INTERPOLATION));
210 * Computes the inflection points for a glyph.
212 private void computeInflectionPoints()
214 // Do each contour separately.
215 contours : for (int c = 0; c < contours.length; c++)
217 Point point = contours[c];
226 } while (end.getOrigX() == first.getOrigX()
227 && end.getOrigY() == first.getOrigY());
229 // Extend segment start whenever possible.
230 Point before = start;
232 int angleSeg = Utils.atan(end.getOrigX() - start.getOrigX(),
233 end.getOrigY() - start.getOrigY());
239 before = before.getPrev();
242 } while (before.getOrigX() == start.getOrigX()
243 && before.getOrigY() == start.getOrigY());
244 angleIn = Utils.atan(start.getOrigX() - before.getOrigX(),
245 start.getOrigY() - before.getOrigY());
246 } while (angleIn == angleSeg);
249 int diffIn = Utils.angleDiff(angleIn, angleSeg);
250 // Now, process all segments in the contour.
252 boolean finished = false;
253 int angleOut, diffOut;
256 // First, extend the current segment's end whenever possible.
263 after = after.getNext();
266 } while (end.getOrigX() == after.getOrigX()
267 && end.getOrigY() == after.getOrigY());
268 angleOut = Utils.atan(after.getOrigX() - end.getOrigX(),
269 after.getOrigY() - end.getOrigY());
270 } while (angleOut == angleSeg);
271 diffOut = Utils.angleDiff(angleSeg, angleOut);
272 if ((diffIn ^ diffOut) < 0)
274 // diffIn and diffOut have different signs, we have
275 // inflection points here.
278 start.addFlags(Point.FLAG_INFLECTION);
279 start = start.getNext();
280 } while (start != end);
281 start.addFlags(Point.FLAG_INFLECTION);
287 } while (! finished);
291 boolean doHorizontal()
293 return (flags & FontDelegate.FLAG_NO_HINT_HORIZONTAL) == 0;
298 return (flags & FontDelegate.FLAG_NO_HINT_VERTICAL) == 0;
301 void alignWeakPoints(int dim)
305 // PASS 1 : Move segments to edge positions.
306 if (dim == DIMENSION_HORZ)
308 touchFlag = Point.FLAG_DONE_X;
309 for (int p = 0; p < numPoints; p++)
312 point.setU(point.getX());
313 point.setV(point.getScaledX());
318 touchFlag = Point.FLAG_DONE_Y;
319 for (int p = 0; p < numPoints; p++)
322 point.setU(point.getY());
323 point.setV(point.getScaledY());
327 for (int c = 0; c < numContours; c++)
330 int idx = getPointIndex(point);
331 Point endPoint = point.getPrev();
332 int endIdx = getPointIndex(endPoint);
335 && (point.getFlags() & touchFlag) == 0)
342 int firstTouched = idx;
343 int curTouched = idx;
346 while (idx <= endIdx)
348 if ((point.getFlags() & touchFlag) != 0)
350 // We found two successive touch points. We interpolate
351 // all contour points between them.
352 iupInterp(curTouched + 1, idx - 1, curTouched, idx);
358 if (curTouched == firstTouched)
360 // This is a special case: Only one point was touched in the
361 // contour. We thus simply shift the whole contour.
362 iupShift(firstIdx, endIdx, curTouched);
366 // Now interpolate after the last touched point to the end
368 iupInterp(curTouched + 1, endIdx, curTouched, firstTouched);
369 // If the first contour point isn't touched, interpolate
370 // from the contour start to the first touched point.
371 if (firstTouched > 0)
373 iupInterp(firstIdx, firstTouched - 1, curTouched,
379 // Now store the values back.
380 if (dim == DIMENSION_HORZ)
382 for (int p = 0; p < numPoints; p++)
385 point.setX(point.getU());
390 for (int p = 0; p < numPoints; p++)
393 point.setY(point.getU());
398 private void iupShift(int p1, int p2, int ref)
400 int delta = points[ref].getU() - points[ref].getV();
401 for (int p = p1; p < ref; p++)
403 points[p].setU(points[p].getV() + delta);
405 for (int p = ref + 1; p <= p2; p++)
407 points[p].setU(points[p].getV() + delta);
411 private void iupInterp(int p1, int p2, int ref1, int ref2)
413 int v1 = points[ref1].getV();
414 int v2 = points[ref2].getV();
415 int d1 = points[ref1].getU() - v1;
416 int d2 = points[ref2].getU() - v2;
421 for (int p = p1; p <= p2; p++)
423 int u = points[p].getV();
433 for (int p = p1; p <= p2; p++)
435 int u = points[p].getV();
442 u = points[ref1].getU() + Utils.mulDiv(u - v1,
444 - points[ref1].getU(),
452 for (int p = p1; p <= p2; p++)
454 int u = points[p].getV();
461 u = points[ref1].getU() + Utils.mulDiv(u - v1,
463 - points[ref1].getU(),
471 void alignStrongPoints(int dim)
473 AxisHints ax = axis[dim];
474 Edge[] edges = ax.edges;
475 int numEdges = ax.numEdges;
477 if (dim == DIMENSION_HORZ)
478 touchFlag = Point.FLAG_DONE_X;
480 touchFlag = Point.FLAG_DONE_Y;
484 for (int p = 0; p < numPoints; p++)
486 Point point = points[p];
487 if ((point.getFlags() & touchFlag) != 0)
489 // If this point is a candidate for weak interpolation, we
490 // interpolate it after all strong points have been processed.
491 if ((point.getFlags() & Point.FLAG_WEAK_INTERPOLATION) != 0
492 && (point.getFlags() & Point.FLAG_INFLECTION) == 0)
495 int u, ou, fu, delta;
496 if (dim == DIMENSION_VERT)
498 u = point.getOrigY();
499 ou = point.getScaledY();
503 u = point.getOrigX();
504 ou = point.getScaledX();
507 // Is the point before the first edge?
508 Edge edge = edges[0];
509 // Inversed vertical dimension.
510 delta = edge.fpos - u;
513 u = edge.pos - (edge.opos - ou);
514 storePoint(point, u, dim, touchFlag);
518 // Is the point after the last edge?
519 edge = edges[numEdges - 1];
520 delta = u - edge.fpos;
523 u = edge.pos + (ou - edge.opos);
524 storePoint(point, u, dim, touchFlag);
528 // Find enclosing edges.
532 boolean found = false;
535 mid = (max + min) / 2;
544 // Directly on the edge.
546 storePoint(point, u, dim, touchFlag);
553 Edge before = edges[min - 1];
554 Edge after = edges[min];
555 if (before.scale == 0)
557 before.scale = Fixed.div16(after.pos - before.pos,
558 after.fpos - before.fpos);
560 u = before.pos + Fixed.mul16(fu - before.fpos,
563 storePoint(point, u, dim, touchFlag);
570 private void storePoint(Point p, int u, int dim, short touchFlag)
572 if (dim == DIMENSION_HORZ)
576 p.addFlags(touchFlag);
579 void alignEdgePoints(int dim)
581 AxisHints ax = axis[dim];
582 Edge[] edges = ax.edges;
583 int numEdges = ax.numEdges;
584 for (int e = 0; e < numEdges; e++)
586 Edge edge = edges[e];
587 Segment seg = edge.first;
590 Point point = seg.first;
593 if (dim == DIMENSION_HORZ)
595 point.setX(edge.pos);
596 point.addFlags(Point.FLAG_DONE_X);
600 point.setY(edge.pos);
601 point.addFlags(Point.FLAG_DONE_Y);
603 if (point == seg.last)
605 point = point.getNext();
608 } while (seg != edge.first);
612 private int getPointIndex(Point p)
615 for (int i = 0; i < numPoints; i++)
626 public boolean doAlignEdgePoints()
628 return (flags & FontDelegate.FLAG_NO_HINT_EDGE_POINTS) == 0;
631 public boolean doAlignStrongPoints()
633 return (flags & FontDelegate.FLAG_NO_HINT_STRONG_POINTS) == 0;
636 public boolean doAlignWeakPoints()
638 return (flags & FontDelegate.FLAG_NO_HINT_WEAK_POINTS) == 0;