freetype-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[ft-devel] ttfautohint: fun with assembler code


From: Werner LEMBERG
Subject: [ft-devel] ttfautohint: fun with assembler code
Date: Fri, 29 Apr 2011 22:07:10 +0200 (CEST)

I've had some hope to show working bytecode stuff at the beginning of
May, but things are a bit more complicated, unfortunately: While the
library has been extended to support generated bytecode already, I
have first to define some auxiliary bytecode functions to support
hinting similar to FreeType's autohinter.

On the other hand, I have now an outline how things will work: I will
run the autohinter for a set of sizes which are then hardcoded as
bytecode.  Contrary to the autohinter, TrueType instructions can only
handle integer values for PPEM sizes; this reduces a lot the number of
configurations to test for.

Some weeks ago I've adapted the autohinter logging stuff to standard
FreeType tracing (at level 5); now can you actually see the
`high-level hints' the autohinter is going to apply with the usual
tracing methods as outlined in FreeType's `DEBUG' documentation file.
As an example, here are the messages for vertical hinting of glyph `R'
from pala.ttf at the 10ppem:

  BLUE: edge 0 (opos=0.00) snapped to (0.00), was (0.00)
  LINK: edge 1 (opos=0.30) linked to (0.30), dist was 0.30, now 0.30
  BLUE: edge 4 (opos=7.34) snapped to (8.00), was (7.34)
  LINK: edge 3 (opos=6.97) linked to (7.62), dist was -0.38, now -0.38
  SERIF_LINK1: edge 2 (opos=3.95) snapped to (4.31) from 1 (opos=0.30)

I'll skip a detailed explanation here; you might read the comments in
file `afhints.h' for more.

The most important fact which can be seen from the above output is
that `LINK' commands don't snap to pixel borders.  This is
intentional.  Except for blue zones where either the top or the bottom
line of a stem gets aligned with a pixel border (the width of small
stems gets increased if necessary), the autohinter aligns the *center*
of stems, applying some rounding voodoo to the stem width.  To imitate
this behaviour, I'm diving right now into the world of writing
bytecode assembler code.

Here an example.  The following code is taken from
`af_latin_compute_stem_width', transformed into some pseudo code which
can be easier converted to bytecode:

  Function: compute_stem_width

  in: width
      is_serif
      is_round
  out: new_width
  CVT: std_width

    dist = ABS(width)

    if is_serif
       && dist < 3*64:
      return width
    else if is_round:
      if dist < 80
        dist = 64
    else:
      dist = MIN(56, dist)

    delta = ABS(dist - std_width)

    if delta < 40:
      dist = MIN(48, std_width)
      goto End

    if dist < 3*64:
      delta = dist
      dist = FLOOR(dist)
      delta = delta - dist

      if delta < 10:
        dist = dist + delta
      else if delta < 32:
        dist = dist + 10
      else if delta < 54:
        dist = dist + 54
      else
        dist = dist + delta
    else
      dist = ROUND(dist)

  End:
    if width < 0:
      dist = -dist
    return dist

This corresponds to the following (still untested) bytecode:

  // In the comments below, the top of the stack (`s:')
  // is the rightmost element.

  // Function 0: compute_stem_width
  //
  // in: width
  //     is_serif
  //     is_round
  // out: new_width
  // CVT: std_width

  0xB0, // PUSHB_1
  0x00, //   0
  0x2C, // FDEF

  0x20, // DUP
  0x64, // ABS -- s: is_round is_serif width dist

  0x20, // DUP
  0xB0, // PUSHB_1
  0xC0, //   3*64
  0x50, // LT -- (dist < 3*64)

  0xB0, // PUSHB_1
  0x04, //   4
  0x26, // MINDEX -- s: is_round width dist (dist<3*64) is_serif

  0x5A, // AND -- (is_serif && dist < 3*64)
  0x58, // IF -- s: is_round width dist
  0x21, //   POP
  0x23, //   SWAP
  0x21, //   POP -- s: width

  0x1B, // ELSE
  0x8A, //   ROLL -- s: width dist is_round
  0x58, //   IF -- s: width dist
  0x20, //     DUP
  0xB0, //     PUSHB_1
  0x50, //       80
  0x50, //     LT -- (dist < 80)
  0x58, //     IF -- s: width dist
  0x21, //       POP
  0xB0, //       PUSHB_1
  0x40, //         64 -- dist = 64
  0x59, //     EIF

  0x1B, //   ELSE
  0xB0, //     PUSHB_1
  0x38, //       56
  0x8C, //       MIN -- dist = min(56, dist)
  0x59, //   EIF

  0x20, //   DUP -- s: width dist dist
  0xB0, //   PUSHB_1
  %c,   //     index of std_width
  0x45, //   RCVT
  0x61, //   SUB
  0x64, //   ABS -- s: width dist delta

  0xB0, //   PUSHB_1
  0x28, //     40
  0x50, //   LT -- (delta < 40)
  0x58, //   IF -- s: width dist
  0x21, //     POP
  0xB1, //     PUSHB_2
  0x30, //       48
  %c,   //       index of std_width
  0x45, //     RCVT
  0x8C, //     MIN -- dist = min(48, std_width)

  0x1B, //   ELSE
  0x20, //     DUP -- s: width dist dist
  0xB0, //     PUSHB_1
  0xC0, //       3*64
  0x50, //     LT -- (dist < 3*64)
  0x58, //     IF
  0x20, //       DUP -- s: width delta dist
  0x66, //       FLOOR -- dist = FLOOR(dist)
  0x20, //       DUP -- s: width delta dist dist
  0x8A, //       ROLL
  0x8A, //       ROLL -- s: width dist delta dist
  0x61, //       SUB -- delta = delta - dist

  0x20, //       DUP -- s: width dist delta delta
  0xB0, //       PUSHB_1
  0x0A, //         10
  0x50, //       LT -- (delta < 10)
  0x58, //       IF -- s: width dist delta
  0x60, //         ADD -- dist = dist + delta

  0x1B, //       ELSE
  0x20, //         DUP
  0xB0, //         PUSHB_1
  0x20, //           32
  0x50, //         LT -- (delta < 32)
  0x58, //         IF
  0x21, //           POP
  0xB0, //           PUSHB_1
  0x0A, //             10
  0x60, //           ADD -- dist = dist + 10

  0x1B, //         ELSE
  0x20, //           DUP
  0xB0, //           PUSHB_1
  0x36, //             54
  0x50, //           LT -- (delta < 54)
  0x58, //           IF
  0x21, //             POP
  0xB0, //             PUSHB_1
  0x36, //               54
  0x60, //             ADD -- dist = dist + 54

  0x1B, //           ELSE
  0x60, //             ADD -- dist = dist + delta

  0x59, //           EIF
  0x59, //         EIF
  0x59, //       EIF

  0x1B, //     ELSE
  0xB0, //       PUSHB_1
  0x20, //         32
  0x60, //       ADD
  0x66, //       FLOOR -- dist = round(dist)

  0x59, //     EIF
  0x59, //   EIF

  0x23, //   SWAP -- s: dist width
  0xB0, //   PUSHB_1
  0x00, //     0
  0x50, //   LT -- (width < 0)
  0x65, //   NEG -- dist = -dist

  0x59, // EIF
  0x2D, // ENDF


It took me three hours to write that small bit of code, but meanwhile
it's getting much faster :-)


    Werner



reply via email to

[Prev in Thread] Current Thread [Next in Thread]