[Top][All Lists]
[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