freetype/truetype: implement IUP opcode.

R=bsiegert
CC=golang-dev
https://codereview.appspot.com/13965043
This commit is contained in:
Nigel Tao 2013-09-29 12:09:09 +10:00
parent 75fff80b59
commit c661e372c6
2 changed files with 142 additions and 10 deletions

View File

@ -402,16 +402,37 @@ func (h *Hinter) run(program []byte) error {
h.gs.rp[1] = i
case opIUP0, opIUP1:
// TODO: implement IUP.
// Glyph 4 in luxisr.ttf (the '!' glyph) has IUP instructions but
// they have no effect since none of the points are untouched.
// The IUP ops will be implemented when the N in truetype_test.go
// is raised above 5.
const mask = flagTouchedX | flagTouchedY
for _, p := range h.g.Point {
if p.Flags&mask != mask {
return errors.New("truetype: hinting: unimplemented IUP instruction")
iupY, mask := opcode == opIUP0, uint32(flagTouchedX)
if iupY {
mask = flagTouchedY
}
prevEnd := 0
for _, end := range h.g.End {
for i := prevEnd; i < end; i++ {
for i < end && h.g.Point[i].Flags&mask == 0 {
i++
}
if i == end {
break
}
firstTouched, curTouched := i, i
i++
for ; i < end; i++ {
if h.g.Point[i].Flags&mask != 0 {
h.iupInterp(iupY, curTouched+1, i-1, curTouched, i)
curTouched = i
}
}
if curTouched == firstTouched {
h.iupShift(iupY, prevEnd, end, curTouched)
} else {
h.iupInterp(iupY, curTouched+1, end-1, curTouched, firstTouched)
if firstTouched > 0 {
h.iupInterp(iupY, prevEnd, firstTouched-1, curTouched, firstTouched)
}
}
}
prevEnd = end
}
case opIP:
@ -1043,6 +1064,117 @@ func (h *Hinter) move(p *Point, distance f26dot6) {
p.Flags |= flagTouchedX | flagTouchedY
}
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) {
return
}
var ifu1, ifu2 int32
if interpY {
ifu1 = h.g.InFontUnits[ref1].Y
ifu2 = h.g.InFontUnits[ref2].Y
} else {
ifu1 = h.g.InFontUnits[ref1].X
ifu2 = h.g.InFontUnits[ref2].X
}
if ifu1 > ifu2 {
ifu1, ifu2 = ifu2, ifu1
ref1, ref2 = ref2, ref1
}
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
} 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
}
var xy, ifuXY int32
if ifu1 == ifu2 {
for i := p1; i <= p2; i++ {
if interpY {
xy = h.g.Unhinted[i].Y
} else {
xy = h.g.Unhinted[i].X
}
if xy <= unh1 {
xy += delta1
} else {
xy += delta2
}
if interpY {
h.g.Point[i].Y = xy
} else {
h.g.Point[i].X = xy
}
}
} else {
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
} else {
xy = h.g.Unhinted[i].X
ifuXY = h.g.InFontUnits[i].X
}
if xy <= unh1 {
xy += delta1
} else if xy >= unh2 {
xy += delta2
} else {
if !scaleOK {
scaleOK = true
denom := int64(ifu2 - ifu1)
scale = (int64(unh2+delta2-unh1-delta1)*0x10000 + denom/2) / denom
}
xy = unh1 + delta1 + int32((int64(ifuXY-ifu1)*scale+0x8000)/0x10000)
}
if interpY {
h.g.Point[i].Y = xy
} else {
h.g.Point[i].X = xy
}
}
}
}
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
} else {
delta = h.g.Point[p].X - h.g.Unhinted[p].X
}
if delta == 0 {
return
}
for i := p1; i < p2; i++ {
if i == p {
continue
}
if interpY {
h.g.Point[i].Y += delta
} else {
h.g.Point[i].X += delta
}
}
}
// skipInstructionPayload increments pc by the extra data that follows a
// variable length PUSHB or PUSHW instruction.
func skipInstructionPayload(program []byte, pc int) (newPC int, ok bool) {

View File

@ -116,7 +116,7 @@ func testScaling(t *testing.T, filename string, hinter *Hinter) {
for i, want := range wants {
// TODO: completely implement hinting. For now, only the first N glyphs
// of luxisr.ttf are correctly hinted.
const N = 5
const N = 8
if hinter != nil && i == N {
break
}