freetype/truetype: make a (sub-)glyph's points an explicit part of a

Hinter's state, instead of having a glyph shrink and restore its
points around hinting.

R=bsiegert
CC=golang-dev
https://codereview.appspot.com/14203043
This commit is contained in:
Nigel Tao 2013-10-02 20:37:11 +10:00
parent e88fe00bdb
commit 7505dadf20
3 changed files with 74 additions and 96 deletions

View file

@ -124,7 +124,7 @@ func (g *GlyphBuf) Load(f *Font, scale int32, i Index, h *Hinter) error {
g.InFontUnits = g.InFontUnits[:0]
g.End = g.End[:0]
if h != nil {
if err := h.init(g, f, scale); err != nil {
if err := h.init(f, scale); err != nil {
return err
}
}
@ -275,20 +275,10 @@ func (g *GlyphBuf) load(f *Font, scale int32, i Index, h *Hinter,
}
if h != nil {
g.Unhinted = append(g.Unhinted, g.Point[np0:np]...)
// For compound glyphs, the hinting program expects the []Point and
// []End slices to be indexed relative to the inner glyph, not the
// outer glyph. Save the outer slices, run the program, and restore
// the outer slices.
// TODO: make these four slices arguments to Hinter.run?
gp, gu, gi, ge := g.Point, g.Unhinted, g.InFontUnits, g.End
g.Point = g.Point[np0:]
g.Unhinted = g.Unhinted[np0:]
g.InFontUnits = g.InFontUnits[np0:]
g.End = g.End[ne0:]
if err := h.run(program); err != nil {
err := h.run(program, g.Point[np0:], g.Unhinted[np0:], g.InFontUnits[np0:], g.End[ne0:])
if err != nil {
return err
}
g.Point, g.Unhinted, g.InFontUnits, g.End = gp, gu, gi, ge
}
// The hinting program expects the []End values to be indexed relative

View file

@ -12,6 +12,21 @@ import (
"errors"
)
const (
twilightZone = 0
glyphZone = 1
numZone = 2
)
type pointType uint32
const (
current pointType = 0
unhinted pointType = 1
inFontUnits pointType = 2
numPointType = 3
)
// callStackEntry is a bytecode call stack entry.
type callStackEntry struct {
program []byte
@ -28,11 +43,9 @@ type Hinter struct {
// functions is a map from function number to bytecode.
functions map[int32][]byte
// g, font and scale are the glyph buffer, font and scale last used for
// this Hinter. Changing the font will require running the new font's
// fpgm bytecode. Changing either will require running the font's prep
// bytecode.
g *GlyphBuf
// font and scale are the font and scale last used for this Hinter.
// Changing the font will require running the new font's fpgm bytecode.
// Changing either will require running the font's prep bytecode.
font *Font
scale int32
@ -41,8 +54,10 @@ type Hinter struct {
// the font's fpgm and prep programs have been run.
gs, defaultGS graphicsState
// twilightXxx are points created in the twilight zone.
twilightPoint, twilightUnhinted, twilightInFontUnits []Point
// points and ends are the twilight zone's points, glyph's points
// and glyph's contour boundaries.
points [numZone][numPointType][]Point
ends []int
}
// graphicsState is described at https://developer.apple.com/fonts/TTRefMan/RM04/Chap4.html
@ -94,11 +109,10 @@ func resetTwilightPoints(f *Font, p []Point) []Point {
return p
}
func (h *Hinter) init(g *GlyphBuf, f *Font, scale int32) error {
h.g = g
h.twilightPoint = resetTwilightPoints(f, h.twilightPoint)
h.twilightUnhinted = resetTwilightPoints(f, h.twilightUnhinted)
h.twilightInFontUnits = resetTwilightPoints(f, h.twilightInFontUnits)
func (h *Hinter) init(f *Font, scale int32) error {
h.points[twilightZone][0] = resetTwilightPoints(f, h.points[twilightZone][0])
h.points[twilightZone][1] = resetTwilightPoints(f, h.points[twilightZone][1])
h.points[twilightZone][2] = resetTwilightPoints(f, h.points[twilightZone][2])
rescale := h.scale != scale
if h.font != f {
@ -122,7 +136,7 @@ func (h *Hinter) init(g *GlyphBuf, f *Font, scale int32) error {
h.store = make([]int32, x)
}
if len(f.fpgm) != 0 {
if err := h.run(f.fpgm); err != nil {
if err := h.run(f.fpgm, nil, nil, nil, nil); err != nil {
return err
}
}
@ -134,7 +148,7 @@ func (h *Hinter) init(g *GlyphBuf, f *Font, scale int32) error {
h.defaultGS = globalDefaultGS
if len(f.prep) != 0 {
if err := h.run(f.prep); err != nil {
if err := h.run(f.prep, nil, nil, nil, nil); err != nil {
return err
}
h.defaultGS = h.gs
@ -151,8 +165,12 @@ func (h *Hinter) init(g *GlyphBuf, f *Font, scale int32) error {
return nil
}
func (h *Hinter) run(program []byte) error {
func (h *Hinter) run(program []byte, pCurrent, pUnhinted, pInFontUnits []Point, ends []int) error {
h.gs = h.defaultGS
h.points[glyphZone][current] = pCurrent
h.points[glyphZone][unhinted] = pUnhinted
h.points[glyphZone][inFontUnits] = pInFontUnits
h.ends = ends
if len(program) > 50000 {
return errors.New("truetype: hinting: too many instructions")
@ -407,9 +425,9 @@ func (h *Hinter) run(program []byte) error {
mask = flagTouchedY
}
prevEnd := 0
for _, end := range h.g.End {
for _, end := range h.ends {
for i := prevEnd; i < end; i++ {
for i < end && h.g.Point[i].Flags&mask == 0 {
for i < end && h.points[glyphZone][current][i].Flags&mask == 0 {
i++
}
if i == end {
@ -418,7 +436,7 @@ func (h *Hinter) run(program []byte) error {
firstTouched, curTouched := i, i
i++
for ; i < end; i++ {
if h.g.Point[i].Flags&mask != 0 {
if h.points[glyphZone][current][i].Flags&mask != 0 {
h.iupInterp(iupY, curTouched+1, i-1, curTouched, i)
curTouched = i
}
@ -476,19 +494,16 @@ func (h *Hinter) run(program []byte) error {
if top < int(h.gs.loop) {
return errors.New("truetype: hinting: stack underflow")
}
i := h.gs.rp[0]
ref := h.point(0, current, i)
ref := h.point(0, current, h.gs.rp[0])
if ref == nil {
return errors.New("truetype: hinting: point out of range")
}
points := h.points(1, current)
for ; h.gs.loop != 0; h.gs.loop-- {
top--
i = h.stack[top]
if i < 0 || len(points) <= int(i) {
p := h.point(1, current, h.stack[top])
if p == nil {
return errors.New("truetype: hinting: point out of range")
}
p := &points[i]
h.move(p, -dotProduct(f26dot6(p.X-ref.X), f26dot6(p.Y-ref.Y), h.gs.pv))
}
h.gs.loop = 1
@ -1008,41 +1023,14 @@ func (h *Hinter) cvt(i int32) f26dot6 {
return f26dot6(h.font.scale(h.scale * int32(int16(cv))))
}
type pointType uint32
const (
current pointType = 0
unhinted pointType = 1
inFontUnits pointType = 2
)
func (h *Hinter) point(zone uint32, pt pointType, i int32) *Point {
points := h.points(zone, pt)
func (h *Hinter) point(zonePointer uint32, pt pointType, i int32) *Point {
points := h.points[h.gs.zp[zonePointer]][pt]
if i < 0 || len(points) <= int(i) {
return nil
}
return &points[i]
}
func (h *Hinter) points(zone uint32, pt pointType) []Point {
if h.gs.zp[zone] == 0 {
switch pt {
case unhinted:
return h.twilightUnhinted
case inFontUnits:
return h.twilightInFontUnits
}
return h.twilightPoint
}
switch pt {
case unhinted:
return h.g.Unhinted
case inFontUnits:
return h.g.InFontUnits
}
return h.g.Point
}
func (h *Hinter) move(p *Point, distance f26dot6) {
if h.gs.fv[0] == 0 {
p.Y += int32(distance)
@ -1068,17 +1056,18 @@ func (h *Hinter) iupInterp(interpY bool, p1, p2, ref1, ref2 int) {
if p1 > p2 {
return
}
if ref1 >= len(h.g.Point) || ref2 >= len(h.g.Point) {
if ref1 >= len(h.points[glyphZone][current]) ||
ref2 >= len(h.points[glyphZone][current]) {
return
}
var ifu1, ifu2 int32
if interpY {
ifu1 = h.g.InFontUnits[ref1].Y
ifu2 = h.g.InFontUnits[ref2].Y
ifu1 = h.points[glyphZone][inFontUnits][ref1].Y
ifu2 = h.points[glyphZone][inFontUnits][ref2].Y
} else {
ifu1 = h.g.InFontUnits[ref1].X
ifu2 = h.g.InFontUnits[ref2].X
ifu1 = h.points[glyphZone][inFontUnits][ref1].X
ifu2 = h.points[glyphZone][inFontUnits][ref2].X
}
if ifu1 > ifu2 {
ifu1, ifu2 = ifu2, ifu1
@ -1087,24 +1076,24 @@ func (h *Hinter) iupInterp(interpY bool, p1, p2, ref1, ref2 int) {
var unh1, unh2, delta1, delta2 int32
if interpY {
unh1 = h.g.Unhinted[ref1].Y
unh2 = h.g.Unhinted[ref2].Y
delta1 = h.g.Point[ref1].Y - unh1
delta2 = h.g.Point[ref2].Y - unh2
unh1 = h.points[glyphZone][unhinted][ref1].Y
unh2 = h.points[glyphZone][unhinted][ref2].Y
delta1 = h.points[glyphZone][current][ref1].Y - unh1
delta2 = h.points[glyphZone][current][ref2].Y - unh2
} else {
unh1 = h.g.Unhinted[ref1].X
unh2 = h.g.Unhinted[ref2].X
delta1 = h.g.Point[ref1].X - unh1
delta2 = h.g.Point[ref2].X - unh2
unh1 = h.points[glyphZone][unhinted][ref1].X
unh2 = h.points[glyphZone][unhinted][ref2].X
delta1 = h.points[glyphZone][current][ref1].X - unh1
delta2 = h.points[glyphZone][current][ref2].X - unh2
}
var xy, ifuXY int32
if ifu1 == ifu2 {
for i := p1; i <= p2; i++ {
if interpY {
xy = h.g.Unhinted[i].Y
xy = h.points[glyphZone][unhinted][i].Y
} else {
xy = h.g.Unhinted[i].X
xy = h.points[glyphZone][unhinted][i].X
}
if xy <= unh1 {
@ -1114,9 +1103,9 @@ func (h *Hinter) iupInterp(interpY bool, p1, p2, ref1, ref2 int) {
}
if interpY {
h.g.Point[i].Y = xy
h.points[glyphZone][current][i].Y = xy
} else {
h.g.Point[i].X = xy
h.points[glyphZone][current][i].X = xy
}
}
@ -1124,11 +1113,11 @@ func (h *Hinter) iupInterp(interpY bool, p1, p2, ref1, ref2 int) {
scale, scaleOK := int64(0), false
for i := p1; i <= p2; i++ {
if interpY {
xy = h.g.Unhinted[i].Y
ifuXY = h.g.InFontUnits[i].Y
xy = h.points[glyphZone][unhinted][i].Y
ifuXY = h.points[glyphZone][inFontUnits][i].Y
} else {
xy = h.g.Unhinted[i].X
ifuXY = h.g.InFontUnits[i].X
xy = h.points[glyphZone][unhinted][i].X
ifuXY = h.points[glyphZone][inFontUnits][i].X
}
if xy <= unh1 {
@ -1145,9 +1134,9 @@ func (h *Hinter) iupInterp(interpY bool, p1, p2, ref1, ref2 int) {
}
if interpY {
h.g.Point[i].Y = xy
h.points[glyphZone][current][i].Y = xy
} else {
h.g.Point[i].X = xy
h.points[glyphZone][current][i].X = xy
}
}
}
@ -1156,9 +1145,9 @@ func (h *Hinter) iupInterp(interpY bool, p1, p2, ref1, ref2 int) {
func (h *Hinter) iupShift(interpY bool, p1, p2, p int) {
var delta int32
if interpY {
delta = h.g.Point[p].Y - h.g.Unhinted[p].Y
delta = h.points[glyphZone][current][p].Y - h.points[glyphZone][unhinted][p].Y
} else {
delta = h.g.Point[p].X - h.g.Unhinted[p].X
delta = h.points[glyphZone][current][p].X - h.points[glyphZone][unhinted][p].X
}
if delta == 0 {
return
@ -1168,9 +1157,9 @@ func (h *Hinter) iupShift(interpY bool, p1, p2, p int) {
continue
}
if interpY {
h.g.Point[i].Y += delta
h.points[glyphZone][current][i].Y += delta
} else {
h.g.Point[i].X += delta
h.points[glyphZone][current][i].X += delta
}
}
}

View file

@ -553,14 +553,13 @@ func TestBytecode(t *testing.T) {
},
}
var g GlyphBuf
for _, tc := range testCases {
h := &Hinter{}
h.init(&g, &Font{
h.init(&Font{
maxStorage: 32,
maxStackElements: 100,
}, 768)
err, errStr := h.run(tc.prog), ""
err, errStr := h.run(tc.prog, nil, nil, nil, nil), ""
if err != nil {
errStr = err.Error()
}