OSDN Git Service

libjava/classpath/ChangeLog.gcj:
[pf3gnuchains/gcc-fork.git] / libjava / classpath / gnu / java / awt / font / autofit / GlyphHints.java
1 /* GlyphHints.java -- Data and methods for actual hinting
2    Copyright (C) 2006 Free Software Foundation, Inc.
3
4 This file is part of GNU Classpath.
5
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)
9 any later version.
10
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.
15
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
19 02110-1301 USA.
20
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
24 combination.
25
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. */
37
38
39 package gnu.java.awt.font.autofit;
40
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;
45
46 /**
47  * The data and methods used for the actual hinting process.
48  */
49 class GlyphHints
50   implements Constants
51 {
52
53   int xScale;
54   int xDelta;
55   int yScale;
56   int yDelta;
57
58   AxisHints[] axis;
59
60   Point[] points;
61   int numPoints;
62   int maxPoints;
63
64   Point[] contours;
65   int numContours;
66   int maxContours;
67
68   ScriptMetrics metrics;
69
70   int flags;
71
72   GlyphHints()
73   {
74     axis = new AxisHints[Constants.DIMENSION_MAX];
75     axis[Constants.DIMENSION_VERT] = new AxisHints();
76     axis[Constants.DIMENSION_HORZ] = new AxisHints();
77
78     xScale = Fixed.ONE;
79     yScale = Fixed.ONE;
80   }
81
82   void rescale(ScriptMetrics m)
83   {
84     metrics = m;
85     // TODO: Copy scalerFlags.
86   }
87   
88   void reload(Zone outline)
89   {
90     numPoints = 0;
91     numContours = 0;
92     axis[0].numSegments = 0;
93     axis[0].numEdges = 0;
94     axis[1].numSegments = 0;
95     axis[1].numEdges = 0;
96
97     // Create/reallocate the contours array.
98     int newMax = outline.getNumContours();
99     if (newMax > maxContours || contours == null)
100       {
101         newMax = (newMax + 3) & ~3; // Taken from afhints.c .
102         Point[] newContours = new Point[newMax];
103         if (contours != null)
104           {
105             System.arraycopy(contours, 0, newContours, 0, maxContours);
106           }
107         contours = newContours;
108         maxContours = newMax;
109       }
110
111     // Create/reallocate the points array.
112     newMax = outline.getSize() + 2;
113     if (newMax > maxPoints || points == null)
114       {
115         newMax = (newMax + 2 + 7) & ~7; // Taken from afhints.c .
116         Point[] newPoints = new Point[newMax];
117         if (points != null)
118           {
119             System.arraycopy(points, 0, newPoints, 0, maxPoints);
120           }
121         points = newPoints;
122         maxPoints = newMax;
123       }
124
125     numPoints = outline.getSize() - 4; // 4 phantom points.
126     numContours = outline.getNumContours();
127
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;
131
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);
137
138     // FIXME: What is that xDelta and yDelta used for?
139     System.arraycopy(outline.getPoints(), 0, points, 0, numPoints);
140
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++)
146       {
147         // Start new contour when the last point has been a contour end.
148         if (outline.isContourEnd(i))
149           {
150             // Connect the contour end point to the start point.
151             points[i].setNext(currentContour);
152             currentContour.setPrev(points[i]);
153             contours[cIndex] = currentContour;
154             cIndex++;
155             currentContour = i < numPoints - 1 ? points[i + 1] : null;
156           }
157         else
158           {
159             // Connect the current and the previous point.
160             points[i].setNext(points[i + 1]);
161             points[i + 1].setPrev(points[i]);
162           }
163       }
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++)
167       {
168         // Compute in and out dir.
169         Point p = points[i];
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));
178
179         if (p.isControlPoint())
180           {
181             setWeakPoint(p);
182           }
183         else if (p.getOutDir() == p.getInDir())
184           {
185             if (p.getOutDir() != DIR_NONE)
186               setWeakPoint(p);
187             else
188               {
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)
193                   setWeakPoint(p);
194               }
195           }
196         else if (p.getInDir() == - p.getOutDir())
197           {
198             setWeakPoint(p);
199           }
200       }
201     computeInflectionPoints();
202   }
203
204   private void setWeakPoint(Point p)
205   {
206     p.setFlags((byte) (p.getFlags() | Point.FLAG_WEAK_INTERPOLATION));
207   }
208
209   /**
210    * Computes the inflection points for a glyph.
211    */
212   private void computeInflectionPoints()
213   {
214     // Do each contour separately.
215     contours : for (int c = 0; c < contours.length; c++)
216       {
217         Point point = contours[c];
218         Point first = point;
219         Point start = point;
220         Point end = point;
221         do
222           {
223             end = end.getNext();
224             if (end == first)
225               continue contours;
226           } while (end.getOrigX() == first.getOrigX()
227                    && end.getOrigY() == first.getOrigY());
228
229         // Extend segment start whenever possible.
230         Point before = start;
231         int angleIn;
232         int angleSeg = Utils.atan(end.getOrigX() - start.getOrigX(),
233                                   end.getOrigY() - start.getOrigY());
234         do
235           {
236             do
237               {
238                 start = before;
239                 before = before.getPrev();
240                 if (before == first)
241                   continue contours;
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);
247
248         first = start;
249         int diffIn = Utils.angleDiff(angleIn, angleSeg);
250         // Now, process all segments in the contour.
251         Point after;
252         boolean finished = false;
253         int angleOut, diffOut;
254         do
255           {
256             // First, extend the current segment's end whenever possible.
257             after = end;
258             do
259               {
260                 do
261                   {
262                     end = after;
263                     after = after.getNext();
264                     if (after == first)
265                       finished = true;
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)
273               {
274                 // diffIn and diffOut have different signs, we have
275                 // inflection points here.
276                 do
277                   {
278                     start.addFlags(Point.FLAG_INFLECTION);
279                     start = start.getNext();
280                   } while (start != end);
281                 start.addFlags(Point.FLAG_INFLECTION);
282               }
283             start = end;
284             end = after;
285             angleSeg = angleOut;
286             diffIn = diffOut;
287           } while (! finished);
288       }
289   }
290
291   boolean doHorizontal()
292   {
293     return (flags & FontDelegate.FLAG_NO_HINT_HORIZONTAL) == 0;
294   }
295
296   boolean doVertical()
297   {
298     return (flags & FontDelegate.FLAG_NO_HINT_VERTICAL) == 0;
299   }
300
301   void alignWeakPoints(int dim)
302   {
303     short touchFlag;
304     Point point;
305     // PASS 1 : Move segments to edge positions.
306     if (dim == DIMENSION_HORZ)
307       {
308         touchFlag = Point.FLAG_DONE_X;
309         for (int p = 0; p < numPoints; p++)
310           {
311             point = points[p];
312             point.setU(point.getX());
313             point.setV(point.getScaledX());
314           }
315       }
316     else
317       {
318         touchFlag = Point.FLAG_DONE_Y;
319         for (int p = 0; p < numPoints; p++)
320           {
321             point = points[p];
322             point.setU(point.getY());
323             point.setV(point.getScaledY());
324           }
325       }
326     point = points[0];
327     for (int c = 0; c < numContours; c++)
328       {
329         point = contours[c];
330         int idx = getPointIndex(point);
331         Point endPoint = point.getPrev();
332         int endIdx = getPointIndex(endPoint);
333         int firstIdx = idx;
334         while (idx <=  endIdx
335             && (point.getFlags() & touchFlag) == 0)
336           {
337             idx++;
338             point = points[idx];
339           }
340         if (idx <= endIdx)
341           {
342             int firstTouched = idx;
343             int curTouched = idx;
344             idx++;
345             point = points[idx];
346             while (idx <= endIdx)
347               {
348                 if ((point.getFlags() & touchFlag) != 0)
349                   {
350                     // We found two successive touch points. We interpolate
351                     // all contour points between them.
352                     iupInterp(curTouched + 1, idx - 1, curTouched, idx);
353                     curTouched = idx;
354                   }
355                 idx++;
356                 point = points[idx];
357               }
358             if (curTouched == firstTouched)
359               {
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);
363               }
364             else
365               {
366                 // Now interpolate after the last touched point to the end
367                 // of the contour.
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)
372                   {
373                     iupInterp(firstIdx, firstTouched - 1, curTouched,
374                               firstTouched);
375                   }
376               }
377           }
378       }
379     // Now store the values back.
380     if (dim == DIMENSION_HORZ)
381       {
382         for (int p = 0; p < numPoints; p++)
383           {
384             point = points[p];
385             point.setX(point.getU());
386           }
387       }
388     else
389       {
390         for (int p = 0; p < numPoints; p++)
391           {
392             point = points[p];
393             point.setY(point.getU());
394           }
395       }
396   }
397
398   private void iupShift(int p1, int p2, int ref)
399   {
400     int delta = points[ref].getU() - points[ref].getV();
401     for (int p = p1; p < ref; p++)
402       {
403         points[p].setU(points[p].getV() + delta);
404       }
405     for (int p = ref + 1; p <= p2; p++)
406       {
407         points[p].setU(points[p].getV() + delta);
408       }
409   }
410
411   private void iupInterp(int p1, int p2, int ref1, int ref2)
412   {
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;
417     if (p1 > p2)
418       return;
419     if (v1 == v2)
420       {
421         for (int p = p1; p <= p2; p++)
422           {
423             int u = points[p].getV();
424             if (u <= v1)
425               u += d1;
426             else
427               u += d2;
428             points[p].setU(u);
429           }
430       }
431     else if (v1 < v2)
432       {
433         for (int p = p1; p <= p2; p++)
434           {
435             int u = points[p].getV();
436             if (u <= v1)
437               u += d1;
438             else if (u >= v2)
439               u += d2;
440             else
441               {
442                 u = points[ref1].getU() + Utils.mulDiv(u - v1,
443                                                        points[ref2].getU()
444                                                        - points[ref1].getU(),
445                                                        v2 - v1);
446               }
447             points[p].setU(u);
448           }
449       }
450     else
451       {
452         for (int p = p1; p <= p2; p++)
453           {
454             int u = points[p].getV();
455             if (u <= v2)
456               u += d2;
457             else if (u >= v1)
458               u += d1;
459             else
460               {
461                 u = points[ref1].getU() + Utils.mulDiv(u - v1,
462                                                        points[ref2].getU()
463                                                        - points[ref1].getU(),
464                                                        v2 - v1);
465               }
466             points[p].setU(u);
467           }
468       }
469   }
470
471   void alignStrongPoints(int dim)
472   {
473     AxisHints ax = axis[dim];
474     Edge[] edges = ax.edges;
475     int numEdges = ax.numEdges;
476     short touchFlag;
477     if (dim == DIMENSION_HORZ)
478       touchFlag = Point.FLAG_DONE_X;
479     else
480       touchFlag = Point.FLAG_DONE_Y;
481
482     if (numEdges > 0)
483       {
484         for (int p = 0; p < numPoints; p++)
485           {
486             Point point = points[p];
487             if ((point.getFlags() & touchFlag) != 0)
488               continue;
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)
493               continue;
494
495             int u, ou, fu, delta;
496             if (dim == DIMENSION_VERT)
497               {
498                 u = point.getOrigY();
499                 ou = point.getScaledY();
500               }
501             else
502               {
503                 u = point.getOrigX();
504                 ou = point.getScaledX();
505               }
506             fu = u;
507             // Is the point before the first edge?
508             Edge edge = edges[0];
509             // Inversed vertical dimension.
510             delta = edge.fpos - u;
511             if (delta >= 0)
512               {
513                 u = edge.pos - (edge.opos - ou);
514                 storePoint(point, u, dim, touchFlag);
515               }
516             else
517               {
518                 // Is the point after the last edge?
519                 edge = edges[numEdges - 1];
520                 delta = u - edge.fpos;
521                 if (delta >= 0)
522                   {
523                     u = edge.pos + (ou - edge.opos);
524                     storePoint(point, u, dim, touchFlag);
525                   }
526                 else
527                   {
528                     // Find enclosing edges.
529                     int min = 0;
530                     int max = numEdges;
531                     int mid, fpos;
532                     boolean found = false;
533                     while (min < max)
534                       {
535                         mid = (max + min) / 2;
536                         edge = edges[mid];
537                         fpos = edge.fpos;
538                         if (u < fpos)
539                           max = mid;
540                         else if (u > fpos)
541                           min = mid + 1;
542                         else
543                           {
544                             // Directly on the edge.
545                             u = edge.pos;
546                             storePoint(point, u, dim, touchFlag);
547                             found = true;
548                             break;
549                           }
550                       }
551                     if (! found)
552                       {
553                         Edge before = edges[min - 1];
554                         Edge after = edges[min];
555                         if (before.scale == 0)
556                           {
557                             before.scale = Fixed.div16(after.pos - before.pos,
558                                                      after.fpos - before.fpos);
559                           }
560                         u = before.pos + Fixed.mul16(fu - before.fpos,
561                                                      before.scale);
562                       }
563                     storePoint(point, u, dim, touchFlag);
564                   }
565               }
566           }
567       }
568   }
569
570   private void storePoint(Point p, int u, int dim, short touchFlag)
571   {
572     if (dim == DIMENSION_HORZ)
573       p.setX(u);
574     else
575       p.setY(u);
576     p.addFlags(touchFlag);
577   }
578
579   void alignEdgePoints(int dim)
580   {
581     AxisHints ax = axis[dim];
582     Edge[] edges = ax.edges;
583     int numEdges = ax.numEdges;
584     for (int e = 0; e < numEdges; e++)
585       {
586         Edge edge = edges[e];
587         Segment seg = edge.first;
588         do
589           {
590             Point point = seg.first;
591             while (true)
592               {
593                 if (dim == DIMENSION_HORZ)
594                   {
595                     point.setX(edge.pos);
596                     point.addFlags(Point.FLAG_DONE_X);
597                   }
598                 else
599                   {
600                     point.setY(edge.pos);
601                     point.addFlags(Point.FLAG_DONE_Y);
602                   }
603                 if (point == seg.last)
604                   break;
605                 point = point.getNext();
606               }
607             seg = seg.edgeNext;
608           } while (seg != edge.first);
609       }
610   }
611
612   private int getPointIndex(Point p)
613   {
614     int idx = -1;
615     for (int i = 0; i < numPoints; i++)
616       {
617         if (p == points[i])
618           {
619             idx = i;
620             break;
621           }
622       }
623     return idx;
624   }
625
626   public boolean doAlignEdgePoints()
627   {
628     return (flags & FontDelegate.FLAG_NO_HINT_EDGE_POINTS) == 0;
629   }
630
631   public boolean doAlignStrongPoints()
632   {
633     return (flags & FontDelegate.FLAG_NO_HINT_STRONG_POINTS) == 0;
634   }
635
636   public boolean doAlignWeakPoints()
637   {
638     return (flags & FontDelegate.FLAG_NO_HINT_WEAK_POINTS) == 0;
639   }
640 }