OpenCores
URL https://opencores.org/ocsvn/openrisc/openrisc/trunk

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [gnu-dev/] [or1k-gcc/] [libjava/] [classpath/] [gnu/] [java/] [awt/] [font/] [autofit/] [Latin.java] - Blame information for rev 769

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 769 jeremybenn
/* Latin.java -- Latin specific glyph handling
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 java.awt.geom.AffineTransform;
42
import java.util.HashSet;
43
 
44
import gnu.java.awt.font.opentype.OpenTypeFont;
45
import gnu.java.awt.font.opentype.truetype.Fixed;
46
import gnu.java.awt.font.opentype.truetype.Point;
47
import gnu.java.awt.font.opentype.truetype.Zone;
48
 
49
/**
50
 * Implements Latin specific glyph handling.
51
 */
52
class Latin
53
  implements Script, Constants
54
{
55
 
56
  static final int MAX_WIDTHS = 16;
57
 
58
  private final static int MAX_TEST_CHARS = 12;
59
 
60
  /**
61
   * The types of the 6 blue zones.
62
   */
63
  private static final int CAPITAL_TOP = 0;
64
  private static final int CAPITAL_BOTTOM = 1;
65
  private static final int SMALL_F_TOP = 2;
66
  private static final int SMALL_TOP = 3;
67
  private static final int SMALL_BOTTOM = 4;
68
  private static final int SMALL_MINOR = 5;
69
  static final int BLUE_MAX = 6;
70
 
71
  /**
72
   * The test chars for the blue zones.
73
   *
74
   * @see #initBlues(LatinMetrics, OpenTypeFont)
75
   */
76
  private static final String[] TEST_CHARS =
77
    new String[]{"THEZOCQS", "HEZLOCUS", "fijkdbh",
78
                 "xzroesc", "xzroesc", "pqgjy"};
79
 
80
  public void applyHints(GlyphHints hints, Zone outline, ScriptMetrics metrics)
81
  {
82
    hints.reload(outline);
83
    hints.rescale(metrics);
84
    if (hints.doHorizontal())
85
      {
86
        detectFeatures(hints, DIMENSION_HORZ);
87
      }
88
    if (hints.doVertical())
89
      {
90
        detectFeatures(hints, DIMENSION_VERT);
91
        computeBlueEdges(hints, (LatinMetrics) metrics);
92
      }
93
    // Grid-fit the outline.
94
    for (int dim = 0; dim < DIMENSION_MAX; dim++)
95
      {
96
        if (dim == DIMENSION_HORZ && hints.doHorizontal()
97
            || dim == DIMENSION_VERT && hints.doVertical())
98
          {
99
            hintEdges(hints, dim);
100
            if (hints.doAlignEdgePoints())
101
              hints.alignEdgePoints(dim);
102
            if (hints.doAlignStrongPoints())
103
              hints.alignStrongPoints(dim);
104
            if (hints.doAlignWeakPoints())
105
              hints.alignWeakPoints(dim);
106
 
107
         }
108
      }
109
    // FreeType does a save call here. I guess that's not needed as we operate
110
    // on the live glyph data anyway.
111
  }
112
 
113
  private void hintEdges(GlyphHints hints, int dim)
114
  {
115
    AxisHints axis = hints.axis[dim];
116
    Edge[] edges = axis.edges;
117
    int numEdges = axis.numEdges;
118
    Edge anchor = null;
119
    int hasSerifs = 0;
120
 
121
    // We begin by aligning all stems relative to the blue zone if
122
    // needed -- that's only for horizontal edges.
123
    if (dim == DIMENSION_VERT)
124
      {
125
        for (int e = 0; e < numEdges; e++)
126
          {
127
            Edge edge = edges[e];
128
            if ((edge.flags & Segment.FLAG_EDGE_DONE) != 0)
129
              continue;
130
 
131
            Width blue = edge.blueEdge;
132
            Edge edge1 = null;
133
            Edge edge2 = edge.link;
134
            if (blue != null)
135
              {
136
                edge1 = edge;
137
              }
138
            else if (edge2 != null && edge2.blueEdge != null)
139
              {
140
                blue = edge2.blueEdge;
141
                edge1 = edge2;
142
                edge2 = edge;
143
              }
144
            if (edge1 == null)
145
              continue;
146
 
147
            edge1.pos = blue.fit;
148
            edge1.flags |= Segment.FLAG_EDGE_DONE;
149
 
150
            if (edge2 != null && edge2.blueEdge == null)
151
              {
152
                alignLinkedEdge(hints, dim, edge1, edge2);
153
                edge2.flags |= Segment.FLAG_EDGE_DONE;
154
              }
155
            if (anchor == null)
156
              anchor = edge;
157
          }
158
      }
159
 
160
    // Now we will align all stem edges, trying to maintain the
161
    // relative order of stems in the glyph.
162
    for (int e = 0; e < numEdges; e++)
163
      {
164
        Edge edge = edges[e];
165
        if ((edge.flags & Segment.FLAG_EDGE_DONE) != 0)
166
          continue;
167
        Edge edge2 = edge.link;
168
        if (edge2 == null)
169
          {
170
            hasSerifs++;
171
            continue;
172
          }
173
        // Now align the stem.
174
        // This should not happen, but it's better to be safe.
175
        if (edge2.blueEdge != null || axis.getEdgeIndex(edge2) < e)
176
          {
177
            alignLinkedEdge(hints, dim, edge2, edge);
178
            edge.flags |= Segment.FLAG_EDGE_DONE;
179
            continue;
180
          }
181
 
182
        if (anchor == null)
183
          {
184
            int orgLen = edge2.opos - edge.opos;
185
            int curLen = computeStemWidth(hints, dim, orgLen, edge.flags,
186
                                          edge2.flags);
187
            int uOff, dOff, orgCenter, curPos1, error1, error2;
188
            if (curLen <= 64) // < 1 Pixel.
189
              {
190
                uOff = 32;
191
                dOff = 32;
192
              }
193
            else
194
              {
195
                uOff = 38;
196
                dOff = 26;
197
              }
198
            if (curLen < 96)
199
              {
200
                orgCenter = edge.opos + (orgLen >> 1);
201
                curPos1 = Utils.pixRound(orgCenter);
202
                error1 = orgCenter - (curPos1 - uOff);
203
                if (error1 < 0)
204
                  error1 = -error1;
205
                error2 = orgCenter - (curPos1 + dOff);
206
                if (error2 < 0)
207
                  error2 = -error2;
208
                if (error1 < error2)
209
                  {
210
                    curPos1 -= uOff;
211
                  }
212
                else
213
                  {
214
                    curPos1 += dOff;
215
                  }
216
                edge.pos = curPos1 - curLen / 2;
217
                edge2.pos = curPos1 + curLen / 2;
218
              }
219
            else
220
              {
221
                edge.pos = Utils.pixRound(edge.opos);
222
              }
223
            anchor = edge;
224
            edge.flags |= Segment.FLAG_EDGE_DONE;
225
            alignLinkedEdge(hints, dim, edge, edge2);
226
          }
227
        else
228
          {
229
            int aDiff = edge.opos - anchor.opos;
230
            int orgPos = anchor.pos + aDiff;
231
            int orgLen = edge2.opos - edge.opos;
232
            int orgCenter = orgPos + (orgLen >> 1);
233
            int curLen = computeStemWidth(hints, dim, orgLen, edge.flags,
234
                                          edge2.flags);
235
            //System.err.println("stem width: " + curLen);
236
            if (curLen < 96)
237
              {
238
                int uOff, dOff;
239
                int curPos1 = Utils.pixRound(orgCenter);
240
                if (curLen <= 64)
241
                  {
242
                    uOff = 32;
243
                    dOff = 32;
244
                  }
245
                else
246
                  {
247
                    uOff = 38;
248
                    dOff = 26;
249
                  }
250
                int delta1 = orgCenter - (curPos1 - uOff);
251
                if (delta1 < 0)
252
                  delta1 = -delta1;
253
                int delta2 = orgCenter - (curPos1 + dOff);
254
                if (delta2 < 0)
255
                  delta2 = -delta2;
256
                if (delta1 < delta2)
257
                  {
258
                    curPos1 -= uOff;
259
                  }
260
                else
261
                  {
262
                    curPos1 += dOff;
263
                  }
264
                edge.pos = curPos1 - curLen / 2;
265
                edge2.pos = curPos1 + curLen / 2;
266
              }
267
            else
268
              {
269
                orgPos = anchor.pos + (edge.opos - anchor.opos);
270
                orgLen = edge2.opos - edge.opos;
271
                orgCenter = orgPos + (orgLen >> 1);
272
                curLen = computeStemWidth(hints, dim, orgLen, edge.flags,
273
                                          edge2.flags);
274
                int curPos1 = Utils.pixRound(orgPos);
275
                int delta1 = curPos1 + (curLen >> 1) - orgCenter;
276
                if (delta1 < 0)
277
                  delta1 = -delta1;
278
                int curPos2 = Utils.pixRound(orgPos + orgLen) - curLen;
279
                int delta2 = curPos2 + (curLen >> 1) - orgCenter;
280
                if (delta2 < 0)
281
                  delta2 = -delta2;
282
                edge.pos = (delta1 < delta2) ? curPos1 : curPos2;
283
                edge2.pos = edge.pos + curLen;
284
              }
285
            edge.flags |= Segment.FLAG_EDGE_DONE;
286
            edge2.flags |= Segment.FLAG_EDGE_DONE;
287
 
288
            if (e > 0 && edge.pos < edges[e - 1].pos)
289
              {
290
                edge.pos = edges[e - 1].pos;
291
              }
292
          }
293
      }
294
    // TODO: Implement the lowercase m symmetry thing.
295
 
296
    // Now we hint the remaining edges (serifs and singles) in order
297
    // to complete our processing.
298
    if (hasSerifs > 0 || anchor == null)
299
      {
300
        for (int e = 0; e < numEdges; e++)
301
          {
302
            Edge edge = edges[e];
303
            if ((edge.flags & Segment.FLAG_EDGE_DONE) != 0)
304
              continue;
305
            if (edge.serif != null)
306
              {
307
                alignSerifEdge(hints, edge.serif, edge);
308
              }
309
            else if (anchor == null)
310
              {
311
                edge.pos = Utils.pixRound(edge.opos);
312
                anchor = edge;
313
              }
314
            else
315
              {
316
                edge.pos = anchor.pos
317
                           + Utils.pixRound(edge.opos - anchor.opos);
318
              }
319
            edge.flags |= Segment.FLAG_EDGE_DONE;
320
 
321
            if (e > 0 && edge.pos < edges[e - 1].pos)
322
              {
323
                edge.pos = edges[e - 1].pos;
324
              }
325
            if (e + 1 < numEdges
326
                && (edges[e + 1].flags & Segment.FLAG_EDGE_DONE) != 0
327
                && edge.pos > edges[e + 1].pos)
328
              {
329
                edge.pos = edges[e + 1].pos;
330
              }
331
          }
332
      }
333
 
334
    // Debug: print all hinted edges.
335
    // System.err.println("hinted edges: " );
336
    // for (int i = 0; i < numEdges; i++)
337
    //   {
338
    //     System.err.println("edge#" + i + ": " + edges[i]);
339
    //   }
340
  }
341
 
342
  private void alignSerifEdge(GlyphHints hints, Edge base, Edge serif)
343
  {
344
    serif.pos = base.pos + (serif.opos - base.opos);
345
  }
346
 
347
  private int computeStemWidth(GlyphHints hints, int dim, int width,
348
                               int baseFlags, int stemFlags)
349
  {
350
    LatinMetrics metrics = (LatinMetrics) hints.metrics;
351
    LatinAxis axis = metrics.axis[dim];
352
    int dist = width;
353
    int sign = 0;
354
    boolean vertical = dim == DIMENSION_VERT;
355
    if (! doStemAdjust(hints))
356
      return width;
357
    if (dist < 0)
358
      {
359
        dist = -width;
360
        sign = 1;
361
      }
362
    if ((vertical && ! doVertSnap(hints)) || ! vertical && ! doHorzSnap(hints))
363
      {
364
        // Smooth hinting process. Very lightly quantize the stem width.
365
        // Leave the widths of serifs alone.
366
        if ((stemFlags & Segment.FLAG_EDGE_SERIF) != 0 && vertical
367
            && dist < 3 * 64)
368
          {
369
            return doneWidth(dist, sign);
370
          }
371
        else if ((baseFlags & Segment.FLAG_EDGE_ROUND) != 0)
372
          {
373
            if (dist < 80)
374
              dist = 64;
375
          }
376
        else if (dist < 56)
377
          {
378
            dist = 56;
379
          }
380
        if (axis.widthCount > 0)
381
          {
382
            int delta;
383
            if (axis.widthCount > 0)
384
              {
385
                delta = dist - axis.widths[0].cur;
386
                if (delta < 0)
387
                  {
388
                    delta = -delta;
389
                  }
390
                if (delta < 40)
391
                  {
392
                    dist = axis.widths[0].cur;
393
                    if (dist < 48)
394
                      dist = 48;
395
                    return doneWidth(dist, sign);
396
                  }
397
              }
398
            if (dist < 3 * 64) // < 3 pixels.
399
              {
400
                delta = dist & 63;
401
                dist &= -64;
402
                if (delta < 10)
403
                  dist += delta;
404
                else if (delta < 32)
405
                  dist += 10;
406
                else if (delta < 54)
407
                  dist += 54;
408
                else
409
                  dist += delta;
410
 
411
              }
412
            else
413
              {
414
                dist = (dist + 32) & ~63;
415
              }
416
          }
417
      }
418
    else
419
      {
420
        // Strong hinting process: Snap the stem width to integer pixels.
421
        dist = snapWidth(axis.widths, axis.widthCount, dist);
422
        if (vertical)
423
          {
424
            // In the case of vertical hinting, always round
425
            // the stem heights to integer pixels.
426
            if (dist >= 64)
427
              dist = (dist + 16) & ~63;
428
            else
429
              dist = 64;
430
          }
431
        else
432
          {
433
            if (doMono(hints))
434
              {
435
                // Monochrome horizontal hinting: Snap widths to integer pixels
436
                // with a different threshold.
437
                if (dist < 64)
438
                  dist = 64;
439
                else
440
                  dist = (dist + 32) & ~63;
441
              }
442
            else
443
              {
444
                // For anti-aliased hinting, we adopt a more subtle
445
                // approach: We strengthen small stems, round those stems
446
                // whose size is between 1 and 2 pixels to an integer,
447
                // otherwise nothing.
448
                if (dist < 48)
449
                  dist = (dist + 64) >> 1;
450
                else if (dist < 128)
451
                  dist = (dist + 22) & ~63;
452
                else
453
                  // Round otherwise to prevent color fringes in LCD mode.
454
                  dist = (dist + 32) & ~63;
455
              }
456
          }
457
      }
458
    return doneWidth(dist, sign);
459
  }
460
 
461
  private boolean doMono(GlyphHints hints)
462
  {
463
    return true;
464
  }
465
 
466
  private int snapWidth(Width[] widths, int count, int width)
467
  {
468
    int best = 64 + 32 + 2;
469
    int reference = width;
470
    for (int n = 0; n < count; n++)
471
      {
472
        int w = widths[n].cur;
473
        int dist = width - w;
474
        if (dist < 0)
475
          dist = -dist;
476
        if (dist < best)
477
          {
478
            best = dist;
479
            reference = w;
480
          }
481
      }
482
    int scaled = Utils.pixRound(reference);
483
    if (width >= reference)
484
      {
485
        if (width < scaled + 48)
486
          width = reference;
487
      }
488
    else
489
      {
490
        if (width > scaled + 48)
491
          width = reference;
492
      }
493
    return width;
494
  }
495
 
496
  private int doneWidth(int w, int s)
497
  {
498
    if (s == 1)
499
      w = -w;
500
    return w;
501
  }
502
 
503
  private boolean doVertSnap(GlyphHints hints)
504
  {
505
    // TODO Auto-generated method stub
506
    return true;
507
  }
508
 
509
  private boolean doHorzSnap(GlyphHints hints)
510
  {
511
    // TODO Auto-generated method stub
512
    return true;
513
  }
514
 
515
  private boolean doStemAdjust(GlyphHints hints)
516
  {
517
    // TODO Auto-generated method stub
518
    return true;
519
  }
520
 
521
  private void alignLinkedEdge(GlyphHints hints, int dim, Edge base, Edge stem)
522
  {
523
    int dist = stem.opos - base.opos;
524
    int fitted = computeStemWidth(hints, dim, dist, base.flags, stem.flags);
525
    stem.pos = base.pos + fitted;
526
  }
527
 
528
  public void doneMetrics(ScriptMetrics metrics)
529
  {
530
    // TODO Auto-generated method stub
531
 
532
  }
533
 
534
  /**
535
   * Initializes the <code>hints</code> object.
536
   *
537
   * @param hints the hints to initialize
538
   * @param metrics the metrics to use
539
   */
540
  public void initHints(GlyphHints hints, ScriptMetrics metrics)
541
  {
542
    hints.rescale(metrics);
543
    LatinMetrics lm = (LatinMetrics) metrics;
544
    hints.xScale = lm.axis[DIMENSION_HORZ].scale;
545
    hints.xDelta = lm.axis[DIMENSION_HORZ].delta;
546
    hints.yScale = lm.axis[DIMENSION_VERT].scale;
547
    hints.yDelta = lm.axis[DIMENSION_VERT].delta;
548
    // TODO: Set the scaler and other flags.
549
  }
550
 
551
  /**
552
   * Initializes the script metrics.
553
   *
554
   * @param metrics the script metrics to initialize
555
   * @param face the font
556
   */
557
  public void initMetrics(ScriptMetrics metrics, OpenTypeFont face)
558
  {
559
    assert metrics instanceof LatinMetrics;
560
    LatinMetrics lm = (LatinMetrics) metrics;
561
    lm.unitsPerEm = face.unitsPerEm;
562
 
563
    // TODO: Check for latin charmap.
564
 
565
    initWidths(lm, face, 'o');
566
    initBlues(lm, face);
567
  }
568
 
569
  public void scaleMetrics(ScriptMetrics metrics, HintScaler scaler)
570
  {
571
    LatinMetrics lm = (LatinMetrics) metrics;
572
    lm.scaler.renderMode = scaler.renderMode;
573
    lm.scaler.face = scaler.face;
574
    scaleMetricsDim(lm, scaler, DIMENSION_HORZ);
575
    scaleMetricsDim(lm, scaler, DIMENSION_VERT);
576
  }
577
 
578
  private void scaleMetricsDim(LatinMetrics lm, HintScaler scaler, int dim)
579
  {
580
    int scale;
581
    int delta;
582
    if (dim == DIMENSION_HORZ)
583
      {
584
        scale = scaler.xScale;
585
        delta = scaler.xDelta;
586
      }
587
    else
588
      {
589
        scale = scaler.yScale;
590
        delta = scaler.yDelta;
591
      }
592
    LatinAxis axis = lm.axis[dim];
593
    if (axis.orgScale == scale && axis.orgDelta == delta)
594
      // No change, no need to adjust.
595
      return;
596
    axis.orgScale = scale;
597
    axis.orgDelta = delta;
598
 
599
    // Correct X and Y scale to optimize the alignment of the top small
600
    // letters to the pixel grid.
601
    LatinAxis axis2 = lm.axis[DIMENSION_VERT];
602
    LatinBlue blue = null;
603
//    for (int nn = 0; nn < axis2.blueCount; nn++)
604
//      {
605
//        if ((axis2.blues[nn].flags & LatinBlue.FLAG_ADJUSTMENT) != 0)
606
//          {
607
//            blue = axis2.blues[nn];
608
//            break;
609
//          }
610
//      }
611
//    if (blue != null)
612
//      {
613
//        int scaled = Fixed.mul16(blue.shoot.org, scaler.yScale);
614
//        int fitted = Utils.pixRound(scaled);
615
//        if (scaled != fitted)
616
//          {
617
//            if (dim == DIMENSION_HORZ)
618
//              {
619
//                if (fitted < scaled)
620
//                  {
621
//                    scale -= scale / 50;
622
//                  }
623
//              }
624
//            else
625
//              {
626
//                scale = Utils.mulDiv(scale, fitted, scaled);
627
//              }
628
//          }
629
//      }
630
    axis.scale = scale;
631
    axis.delta = delta;
632
    if (dim == DIMENSION_HORZ)
633
      {
634
        lm.scaler.xScale = scale;
635
        lm.scaler.xDelta = delta;
636
      }
637
    else
638
      {
639
        lm.scaler.yScale = scale;
640
        lm.scaler.yDelta = delta;
641
      }
642
    // Scale the standard widths.
643
    for (int nn = 0; nn < axis.widthCount; nn++)
644
      {
645
        Width w = axis.widths[nn];
646
        w.cur = Fixed.mul16(w.org, scale);
647
        w.fit = w.cur;
648
      }
649
    // Scale blue zones.
650
    if (dim == DIMENSION_VERT)
651
      {
652
        for (int nn = 0; nn < axis.blueCount; nn++)
653
          {
654
            blue = axis.blues[nn];
655
            blue.ref.cur = Fixed.mul16(blue.ref.org, scale) + delta;
656
            blue.ref.fit = blue.ref.cur;
657
            blue.shoot.cur = Fixed.mul16(blue.ref.org, scale) + delta;
658
            blue.flags &= ~LatinBlue.FLAG_BLUE_ACTIVE;
659
            // A blue zone is only active if it is less than 3/4 pixels tall.
660
            int dist = Fixed.mul16(blue.ref.org - blue.shoot.org, scale);
661
            if (dist <= 48 && dist >= -48)
662
              {
663
                int delta1 = blue.shoot.org - blue.ref.org;
664
                int delta2 = delta1;
665
                if (delta1 < 0)
666
                  delta2 = -delta2;
667
                delta2 = Fixed.mul16(delta2, scale);
668
                if (delta2 < 32)
669
                  delta2 = 0;
670
                else if (delta2 < 64)
671
                  delta2 = 32 + (((delta2 - 32) + 16) & ~31);
672
                else
673
                  delta2 = Utils.pixRound(delta2);
674
                if (delta1 < 0)
675
                  delta2 = -delta2;
676
                blue.ref.fit = Utils.pixRound(blue.ref.cur);
677
                blue.shoot.fit = blue.ref.fit + delta2;
678
                blue.flags |= LatinBlue.FLAG_BLUE_ACTIVE;
679
              }
680
          }
681
      }
682
  }
683
 
684
  /**
685
   * Determines the standard stem widths.
686
   *
687
   * @param metrics the metrics to use
688
   * @param face the font face
689
   * @param ch the character that is used for getting the widths
690
   */
691
  private void initWidths(LatinMetrics metrics, OpenTypeFont face, char ch)
692
  {
693
    GlyphHints hints = new GlyphHints();
694
    metrics.axis[DIMENSION_HORZ].widthCount = 0;
695
    metrics.axis[DIMENSION_VERT].widthCount = 0;
696
    int glyphIndex = face.getGlyph(ch);
697
    Zone outline = face.getRawGlyphOutline(glyphIndex, IDENTITY);
698
    LatinMetrics dummy = new LatinMetrics();
699
    HintScaler scaler = dummy.scaler;
700
    dummy.unitsPerEm = metrics.unitsPerEm;
701
    scaler.xScale = scaler.yScale = 10000;
702
    scaler.xDelta = scaler.yDelta = 0;
703
    scaler.face = face;
704
    hints.rescale(dummy);
705
    hints.reload(outline);
706
    for (int dim = 0; dim < DIMENSION_MAX; dim++)
707
      {
708
        LatinAxis axis = metrics.axis[dim];
709
        AxisHints axHints = hints.axis[dim];
710
        int numWidths = 0;
711
        computeSegments(hints, dim);
712
        linkSegments(hints, dim);
713
        Segment[] segs = axHints.segments;
714
        HashSet<Segment> touched = new HashSet<Segment>();
715
        for (int i = 0; i < segs.length; i++)
716
          {
717
            Segment seg = segs[i];
718
            Segment link = seg.link;
719
            if (link != null && link.link == seg && ! touched.contains(link))
720
              {
721
                int dist = Math.abs(seg.pos - link.pos);
722
                if (numWidths < MAX_WIDTHS)
723
                  axis.widths[numWidths++] = new Width(dist);
724
              }
725
            touched.add(seg);
726
          }
727
        Utils.sort(numWidths, axis.widths);
728
        axis.widthCount = numWidths;
729
      }
730
    for (int dim = 0; dim < DIMENSION_MAX; dim++)
731
      {
732
        LatinAxis axis = metrics.axis[dim];
733
        int stdw = axis.widthCount > 0 ? axis.widths[0].org
734
                                       : constant(metrics, 50);
735
        axis.edgeDistanceTreshold= stdw / 5;
736
      }
737
  }
738
 
739
  void linkSegments(GlyphHints hints, int dim)
740
  {
741
    AxisHints axis = hints.axis[dim];
742
    Segment[] segments = axis.segments;
743
    int numSegs = axis.numSegments;
744
    int majorDir = axis.majorDir;
745
    int lenThreshold = constant((LatinMetrics) hints.metrics, 8);
746
    lenThreshold = Math.min(1, lenThreshold);
747
    int lenScore = constant((LatinMetrics) hints.metrics, 3000);
748
    for (int i1 = 0; i1 < numSegs; i1++)
749
      {
750
        Segment seg1 = segments[i1];
751
        // The fake segments are introduced to hint the metrics.
752
        // Never link them to anything.
753
        if (seg1.first == seg1.last || seg1.dir != majorDir)
754
          continue;
755
        for (int i2 = 0; i2 < numSegs; i2++)
756
          {
757
            Segment seg2 = segments[i2];
758
            if (seg2 != seg1 && seg1.dir + seg2.dir == 0)
759
              {
760
                int pos1 = seg1.pos;
761
                int pos2 = seg2.pos;
762
                // The vertical coords are swapped compared to how FT handles
763
                // this.
764
                int dist = dim == DIMENSION_VERT ? pos1 - pos2 : pos2 - pos1;
765
                if (dist >= 0)
766
                  {
767
                    int min = seg1.minPos;
768
                    int max = seg1.maxPos;
769
                    int len, score;
770
                    if (min < seg2.minPos)
771
                      min = seg2.minPos;
772
                    if (max > seg2.maxPos)
773
                      max = seg2.maxPos;
774
                    len = max - min;
775
                    if (len > lenThreshold)
776
                      {
777
                        score = dist + lenScore / len;
778
                        if (score < seg1.score)
779
                          {
780
                            seg1.score = score;
781
                            seg1.link = seg2;
782
                          }
783
                        if (score < seg2.score)
784
                          {
785
                            seg2.score = score;
786
                            seg2.link = seg1;
787
                          }
788
                      }
789
                  }
790
              }
791
          }
792
      }
793
    for (int i1 = 0; i1 < numSegs; i1++)
794
      {
795
        Segment seg1 = segments[i1];
796
        Segment seg2 = seg1.link;
797
        if (seg2 != null)
798
          {
799
            seg2.numLinked++;
800
            if (seg2.link != seg1)
801
              {
802
                seg1.link = null;
803
                seg1.serif = seg2.link;
804
              }
805
          }
806
        // Uncomment to show all segments.
807
        // System.err.println("segment#" + i1 + ": " + seg1);
808
      }
809
  }
810
 
811
  /**
812
   * Initializes the blue zones of the font.
813
   *
814
   * @param metrics the metrics to use
815
   * @param face the font face to analyze
816
   */
817
  private void initBlues(LatinMetrics metrics, OpenTypeFont face)
818
  {
819
    int[] flats = new int[MAX_TEST_CHARS];
820
    int[] rounds = new int[MAX_TEST_CHARS];
821
    int numFlats;
822
    int numRounds;
823
    LatinBlue blue;
824
    LatinAxis axis = metrics.axis[DIMENSION_VERT];
825
    // We compute the blues simply by loading each character in the test
826
    // strings, then compute its topmost or bottommost points.
827
    for (int bb = 0; bb < BLUE_MAX; bb++)
828
      {
829
        String p = TEST_CHARS[bb];
830
        int blueRef;
831
        int blueShoot;
832
        numFlats = 0;
833
        numRounds = 0;
834
        for (int i = 0; i < p.length(); i++)
835
          {
836
            // Load the character.
837
            int glyphIndex = face.getGlyph(p.charAt(i));
838
            Zone glyph =
839
              face.getRawGlyphOutline(glyphIndex, IDENTITY);
840
 
841
            // Now compute the min and max points.
842
            int numPoints = glyph.getSize() - 4; // 4 phantom points.
843
            Point[] points = glyph.getPoints();
844
            Point point = points[0];
845
            int extremum = 0;
846
            int index = 1;
847
            if (isTopBlue(bb))
848
              {
849
                for (; index < numPoints; index++)
850
                  {
851
                    point = points[index];
852
                    // We have the vertical direction swapped. The higher
853
                    // points have smaller (negative) Y.
854
                    if (point.getOrigY() < points[extremum].getOrigY())
855
                      extremum = index;
856
                  }
857
              }
858
            else
859
              {
860
                for (; index < numPoints; index++)
861
                  {
862
                    point = points[index];
863
                    // We have the vertical direction swapped. The higher
864
                    // points have smaller (negative) Y.
865
                    if (point.getOrigY() > points[extremum].getOrigY())
866
                      extremum = index;
867
                  }
868
              }
869
            // Debug, prints out the maxima.
870
            // System.err.println("extremum for " + bb + " / "+ p.charAt(i)
871
            //                    + ": " + points[extremum]);
872
 
873
            // Now determine if the point is part of a straight or round
874
            // segment.
875
            boolean round;
876
            int idx = extremum;
877
            int first, last, prev, next, end;
878
            int dist;
879
            last = -1;
880
            first = 0;
881
            for (int n = 0; n < glyph.getNumContours(); n++)
882
              {
883
                end = glyph.getContourEnd(n);
884
                // System.err.println("contour end for " + n + ": " + end);
885
                if (end >= idx)
886
                  {
887
                    last = end;
888
                    break;
889
                  }
890
                first = end + 1;
891
              }
892
            // Should never happen.
893
            assert last >= 0;
894
 
895
            // Now look for the previous and next points that are not on the
896
            // same Y coordinate. Threshold the 'closeness'.
897
            prev = idx;
898
            next = prev;
899
            do
900
              {
901
                if (prev > first)
902
                  prev--;
903
                else
904
                  prev = last;
905
                dist = points[prev].getOrigY() - points[extremum].getOrigY();
906
                if (dist < -5 || dist > 5)
907
                  break;
908
              } while (prev != idx);
909
            do
910
              {
911
                if (next < last)
912
                  next++;
913
                else
914
                  next = first;
915
                dist = points[next].getOrigY() - points[extremum].getOrigY();
916
                if (dist < -5 || dist > 5)
917
                  break;
918
              } while (next != idx);
919
            round = points[prev].isControlPoint()
920
                    || points[next].isControlPoint();
921
 
922
            if (round)
923
              {
924
                rounds[numRounds++] = points[extremum].getOrigY();
925
                // System.err.println("new round extremum: " + bb + ": "
926
                //                   + points[extremum].getOrigY());
927
              }
928
            else
929
              {
930
                flats[numFlats++] = points[extremum].getOrigY();
931
                // System.err.println("new flat extremum: " + bb + ": "
932
                //                    + points[extremum].getOrigY());
933
              }
934
          }
935
        // We have computed the contents of the rounds and flats tables.
936
        // Now determine the reference and overshoot position of the blues --
937
        // we simply take the median after a simple sort.
938
        Utils.sort(numRounds, rounds);
939
        Utils.sort(numFlats, flats);
940
        blue = axis.blues[axis.blueCount] = new LatinBlue();
941
        axis.blueCount++;
942
        if (numFlats == 0)
943
          {
944
            blue.ref = blue.shoot = new Width(rounds[numRounds / 2]);
945
          }
946
        else if (numRounds == 0)
947
          {
948
            blue.ref = blue.shoot = new Width(flats[numFlats / 2]);
949
          }
950
        else
951
          {
952
            blue.ref = new Width(flats[numFlats / 2]);
953
            blue.shoot = new Width(rounds[numRounds / 2]);
954
          }
955
        // There are sometimes problems:  if the overshoot position of top
956
        // zones is under its reference position, or the opposite for bottom
957
        // zones. We must check everything there and correct problems.
958
        if (blue.shoot != blue.ref)
959
          {
960
            int ref = blue.ref.org;
961
            int shoot = blue.shoot.org;
962
            // Inversed vertical coordinates!
963
            boolean overRef = shoot < ref;
964
            if (isTopBlue(bb) ^ overRef)
965
              {
966
                blue.shoot = blue.ref = new Width((shoot + ref) / 2);
967
              }
968
          }
969
        blue.flags = 0;
970
        if (isTopBlue(bb))
971
          blue.flags |= LatinBlue.FLAG_TOP;
972
        // The following flag is used later to adjust y and x scales in
973
        // order to optimize the pixel grid alignment of the top small
974
        // letters.
975
        if (bb == SMALL_TOP)
976
          {
977
            blue.flags |= LatinBlue.FLAG_ADJUSTMENT;
978
          }
979
        // Debug: print out the blue zones.
980
        // System.err.println("blue zone #" + bb + ": " + blue);
981
      }
982
  }
983
 
984
  private static final AffineTransform IDENTITY = new AffineTransform();
985
 
986
  private int constant(LatinMetrics metrics, int c)
987
  {
988
    return c * (metrics.unitsPerEm / 2048);
989
  }
990
 
991
  private void computeSegments(GlyphHints hints, int dim)
992
  {
993
    Point[] points = hints.points;
994
    if (dim == DIMENSION_HORZ)
995
      {
996
        for (int i = 0; i < hints.numPoints; i++)
997
          {
998
            points[i].setU(points[i].getOrigX());
999
            points[i].setV(points[i].getOrigY());
1000
          }
1001
      }
1002
    else
1003
      {
1004
        for (int i = 0; i < hints.numPoints; i++)
1005
          {
1006
            points[i].setU(points[i].getOrigY());
1007
            points[i].setV(points[i].getOrigX());
1008
          }
1009
      }
1010
    // Now look at each contour.
1011
    AxisHints axis = hints.axis[dim];
1012
    int majorDir = Math.abs(axis.majorDir);
1013
    int segmentDir = majorDir;
1014
    Point[] contours = hints.contours;
1015
    int numContours = hints.numContours;
1016
    Segment segment = null;
1017
    for (int i = 0; i < numContours; i++)
1018
      {
1019
        int minPos = 32000;
1020
        int maxPos = -32000;
1021
 
1022
        Point point = contours[i];
1023
        Point last = point.getPrev();
1024
        if (point == last) // Skip singletons.
1025
          continue;
1026
        if (Math.abs(last.getOutDir()) == majorDir
1027
            && Math.abs(point.getOutDir()) == majorDir)
1028
          {
1029
            // We are already on an edge. Locate its start.
1030
            last = point;
1031
            while (true)
1032
              {
1033
                point = point.getPrev();
1034
                if (Math.abs(point.getOutDir()) != majorDir)
1035
                  {
1036
                    point = point.getNext();
1037
                    break;
1038
                  }
1039
                if (point == last)
1040
                  break;
1041
              }
1042
          }
1043
        last = point;
1044
        boolean passed = false;
1045
        boolean onEdge = false;
1046
        while (true)
1047
          {
1048
            int u, v;
1049
            if (onEdge)
1050
              {
1051
                u = point.getU();
1052
                if (u < minPos)
1053
                  minPos = u;
1054
                if (u > maxPos)
1055
                  maxPos = u;
1056
                if (point.getOutDir() != segmentDir || point == last)
1057
                  {
1058
                    // Leaving an edge. Record new segment.
1059
                    segment.last = point;
1060
                    // (minPos + maxPos) / 2.
1061
                    segment.pos = (minPos + maxPos) >> 1;
1062
                    if (segment.first.isControlPoint()
1063
                        || point.isControlPoint())
1064
                      segment.flags |= Segment.FLAG_EDGE_ROUND;
1065
                    minPos = maxPos = point.getV();
1066
                    v = segment.first.getV();
1067
                    if (v < minPos)
1068
                      minPos = v;
1069
                    if (v > maxPos)
1070
                      maxPos = v;
1071
                    segment.minPos = minPos;
1072
                    segment.maxPos = maxPos;
1073
                    onEdge = false;
1074
                    segment = null;
1075
                  }
1076
              }
1077
            if (point == last)
1078
              {
1079
                if (passed)
1080
                  break;
1081
                passed = true;
1082
              }
1083
            if (! onEdge && Math.abs(point.getOutDir()) == majorDir)
1084
              {
1085
                // This is the start of a new segment.
1086
                segmentDir = point.getOutDir();
1087
                segment = axis.newSegment();
1088
                segment.dir = segmentDir;
1089
                segment.flags = Segment.FLAG_EDGE_NORMAL;
1090
                minPos = maxPos = point.getU();
1091
                segment.first = point;
1092
                segment.last = point;
1093
                segment.contour = contours[i];
1094
                segment.score = 32000;
1095
                segment.len = 0;
1096
                segment.link = null;
1097
                onEdge = true;
1098
              }
1099
            point = point.getNext();
1100
          }
1101
      }
1102
 
1103
  }
1104
 
1105
  private boolean isTopBlue(int b)
1106
  {
1107
    return b == CAPITAL_TOP || b == SMALL_F_TOP || b == SMALL_TOP;
1108
  }
1109
 
1110
  private void detectFeatures(GlyphHints hints, int dim)
1111
  {
1112
    computeSegments(hints, dim);
1113
    linkSegments(hints, dim);
1114
    computeEdges(hints, dim);
1115
  }
1116
 
1117
  private void computeEdges(GlyphHints hints, int dim)
1118
  {
1119
    AxisHints axis = hints.axis[dim];
1120
    LatinAxis laxis = ((LatinMetrics) hints.metrics).axis[dim];
1121
    Segment[] segments = axis.segments;
1122
    int numSegments = axis.numSegments;
1123
    Segment seg;
1124
    int upDir;
1125
    int scale;
1126
    int edgeDistanceThreshold;
1127
    axis.numEdges = 0;
1128
    scale = dim == DIMENSION_HORZ ? hints.xScale : hints.yScale;
1129
    upDir = dim == DIMENSION_HORZ ? DIR_UP : DIR_RIGHT;
1130
 
1131
    // We will begin by generating a sorted table of edges for the
1132
    // current direction. To do so, we simply scan each segment and try
1133
    // to find an edge in our table that corresponds to its position.
1134
    //
1135
    // If no edge is found, we create one and insert a new edge in the
1136
    // sorted table. Otherwise, we simply add the segment to the egde's
1137
    // list which will be processed in the second step to compute the
1138
    // edge's properties.
1139
    //
1140
    // Note that the edge table is sorted along the segment/edge
1141
    // position.
1142
 
1143
    edgeDistanceThreshold = Fixed.mul16(laxis.edgeDistanceTreshold, scale);
1144
    if (edgeDistanceThreshold > 64 / 4)
1145
      edgeDistanceThreshold = 64 / 4;
1146
    edgeDistanceThreshold = Fixed.div16(edgeDistanceThreshold, scale);
1147
    for (int i = 0; i < numSegments; i++)
1148
      {
1149
        seg = segments[i];
1150
        Edge found = null;
1151
        for (int ee = 0; ee < axis.numEdges; ee++)
1152
          {
1153
            Edge edge = axis.edges[ee];
1154
            int dist = seg.pos - edge.fpos;
1155
            if (dist < 0)
1156
              dist = -dist;
1157
            if (dist < edgeDistanceThreshold)
1158
              {
1159
                found = edge;
1160
                break;
1161
              }
1162
          }
1163
        if (found == null)
1164
          {
1165
            // Insert new edge in the list and sort according to
1166
            // the position.
1167
            Edge edge = axis.newEdge(seg.pos);
1168
            edge.first = seg;
1169
            edge.last = seg;
1170
            edge.fpos = seg.pos;
1171
            edge.opos = edge.pos = Fixed.mul16(seg.pos, scale);
1172
            seg.edgeNext = seg;
1173
            seg.edge = edge;
1174
          }
1175
        else
1176
          {
1177
            seg.edgeNext = found.first;
1178
            found.last.edgeNext = seg;
1179
            found.last = seg;
1180
            seg.edge = found;
1181
          }
1182
      }
1183
    // Good. We will now compute each edge's properties according to
1184
    // segments found on its position. Basically these are:
1185
    // - Edge's main direction.
1186
    // - Stem edge, serif edge, or both (which defaults to stem edge).
1187
    // - Rounded edge, straight or both (which defaults to straight).
1188
    // - Link for edge.
1189
 
1190
    // Now, compute each edge properties.
1191
    for (int e = 0; e < axis.numEdges; e++)
1192
      {
1193
        Edge edge = axis.edges[e];
1194
        // Does it contain round segments?
1195
        int isRound = 0;
1196
        // Does it contain straight segments?
1197
        int isStraight = 0;
1198
        // Number of upward segments.
1199
        int ups = 0;
1200
        // Number of downward segments.
1201
        int downs = 0;
1202
 
1203
        seg = edge.first;
1204
        do
1205
          {
1206
            // Check for roundness of segment.
1207
            if ((seg.flags & Segment.FLAG_EDGE_ROUND) != 0)
1208
              isRound++;
1209
            else
1210
              isStraight++;
1211
 
1212
            // Check for segment direction.
1213
            if (seg.dir == upDir)
1214
              ups += seg.maxPos - seg.minPos;
1215
            else
1216
              downs += seg.maxPos - seg.minPos;
1217
 
1218
            // Check for links. If seg.serif is set, then seg.link must
1219
            // be ignored.
1220
            boolean isSerif = seg.serif != null && seg.serif.edge != edge;
1221
            if (seg.link != null || isSerif)
1222
              {
1223
                Edge edge2 = edge.link;
1224
                Segment seg2 = seg.link;
1225
                if (isSerif)
1226
                  {
1227
                    seg2 = seg.serif;
1228
                    edge2 = edge.serif;
1229
                  }
1230
                if (edge2 != null)
1231
                  {
1232
                    int edgeDelta = edge.fpos - edge2.fpos;
1233
                    if (edgeDelta < 0)
1234
                      edgeDelta = -edgeDelta;
1235
                    int segDelta = seg.pos - seg2.pos;
1236
                    if (segDelta < 0)
1237
                      segDelta = -segDelta;
1238
                    if (segDelta < edgeDelta)
1239
                      edge2 = seg2.edge;
1240
                  }
1241
                else
1242
                  {
1243
                    edge2 = seg2.edge;
1244
                  }
1245
                if (isSerif)
1246
                  {
1247
                    edge.serif = edge2;
1248
                    edge2.flags |= Segment.FLAG_EDGE_SERIF;
1249
                  }
1250
                else
1251
                  {
1252
                    edge.link = edge2;
1253
                  }
1254
              }
1255
            seg = seg.edgeNext;
1256
          } while (seg != edge.first);
1257
        edge.flags = Segment.FLAG_EDGE_NORMAL;
1258
        if (isRound > 0 && isRound > isStraight)
1259
          edge.flags |= Segment.FLAG_EDGE_ROUND;
1260
 
1261
        // Set the edge's main direction.
1262
        edge.dir = DIR_NONE;
1263
        if (ups > downs)
1264
          edge.dir = upDir;
1265
        else if (ups < downs)
1266
          edge.dir = -upDir;
1267
        else if (ups == downs)
1268
          edge.dir = 0;
1269
 
1270
        // Gets rid of serif if link is set. This gets rid of many
1271
        // unpleasant artifacts.
1272
        if (edge.serif != null && edge.link != null)
1273
          {
1274
            edge.serif = null;
1275
          }
1276
 
1277
        // Debug: Print out all edges.
1278
        // System.err.println("edge# " + e + ": " + edge);
1279
      }
1280
  }
1281
 
1282
  private void computeBlueEdges(GlyphHints hints, LatinMetrics metrics)
1283
  {
1284
    AxisHints axis = hints.axis[DIMENSION_VERT];
1285
    Edge[] edges = axis.edges;
1286
    int numEdges = axis.numEdges;
1287
    LatinAxis latin = metrics.axis[DIMENSION_VERT];
1288
    int scale = latin.scale;
1289
 
1290
    // Compute which blue zones are active. I.e. have their scaled
1291
    // size < 3/4 pixels.
1292
 
1293
    // For each horizontal edge search the blue zone that is closest.
1294
    for (int e = 0; e < numEdges; e++)
1295
      {
1296
        Edge edge = edges[e];
1297
        // System.err.println("checking edge: " + edge);
1298
        Width bestBlue = null;
1299
        int bestDist = Fixed.mul16(metrics.unitsPerEm / 40, scale);
1300
 
1301
        if (bestDist > 64 / 2)
1302
          bestDist = 64 / 2;
1303
        for (int bb = 0; bb < BLUE_MAX; bb++)
1304
          {
1305
            LatinBlue blue = latin.blues[bb];
1306
            // System.err.println("checking blue: " + blue);
1307
            // Skip inactive blue zones, i.e. those that are too small.
1308
            if ((blue.flags & LatinBlue.FLAG_BLUE_ACTIVE) == 0)
1309
              continue;
1310
            // If it is a top zone, check for right edges. If it is a bottom
1311
            // zone, check for left edges.
1312
            boolean isTopBlue = (blue.flags & LatinBlue.FLAG_TOP) != 0;
1313
            boolean isMajorDir = edge.dir == axis.majorDir;
1314
 
1315
            // If it is a top zone, the edge must be against the major
1316
            // direction. If it is a bottom zone it must be in the major
1317
            // direction.
1318
            if (isTopBlue ^ isMajorDir)
1319
              {
1320
                int dist = edge.fpos - blue.ref.org;
1321
                if (dist < 0)
1322
                  dist = -dist;
1323
                dist = Fixed.mul16(dist, scale);
1324
                if (dist < bestDist)
1325
                  {
1326
                    bestDist = dist;
1327
                    bestBlue = blue.ref;
1328
                  }
1329
 
1330
                // Now, compare it to the overshoot position if the edge is
1331
                // rounded, and if the edge is over the reference position of
1332
                // a top zone, or under the reference position of a bottom
1333
                // zone.
1334
                if ((edge.flags & Segment.FLAG_EDGE_ROUND) != 0 && dist != 0)
1335
                  {
1336
                    // Inversed vertical coordinates!
1337
                    boolean isUnderRef = edge.fpos > blue.ref.org;
1338
                    if (isTopBlue ^ isUnderRef)
1339
                      {
1340
                        blue = latin.blues[bb]; // Needed?
1341
                        dist = edge.fpos - blue.shoot.org;
1342
                        if (dist < 0)
1343
                          dist = -dist;
1344
                        dist = Fixed.mul16(dist, scale);
1345
                        if (dist < bestDist)
1346
                          {
1347
                            bestDist = dist;
1348
                            bestBlue = blue.shoot;
1349
                          }
1350
                      }
1351
                  }
1352
 
1353
              }
1354
          }
1355
        if (bestBlue != null)
1356
          {
1357
            edge.blueEdge = bestBlue;
1358
            // Debug: Print out the blue edges.
1359
            // System.err.println("blue edge for: " + edge + ": " + bestBlue);
1360
          }
1361
      }
1362
  }
1363
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.