freetype/truetype: implement ALIGNRP, MDAP and MDRP opcodes.
We can now hint the .notdef glyph from luxisr.ttf. Yay. R=bsiegert CC=golang-dev https://codereview.appspot.com/12829048
This commit is contained in:
parent
a8a5cfeb78
commit
a3c53fdc3f
|
@ -19,8 +19,13 @@ type Point struct {
|
|||
type GlyphBuf struct {
|
||||
// The glyph's bounding box.
|
||||
B Bounds
|
||||
// Point contains all Points from all contours of the glyph.
|
||||
Point []Point
|
||||
// Point contains all Points from all contours of the glyph. If a
|
||||
// Hinter was used to load a glyph then Unhinted contains those
|
||||
// Points before they were hinted, and InFontUnits contains those
|
||||
// Points before they were hinted and scaled. Twilight is those
|
||||
// Points created in the 'twilight zone' by the truetype hinting
|
||||
// process.
|
||||
Point, Unhinted, InFontUnits, Twilight []Point
|
||||
// The length of End is the number of contours in the glyph. The i'th
|
||||
// contour consists of points Point[End[i-1]:End[i]], where End[-1]
|
||||
// is interpreted to mean zero.
|
||||
|
@ -36,6 +41,10 @@ const (
|
|||
flagRepeat
|
||||
flagPositiveXShortVector
|
||||
flagPositiveYShortVector
|
||||
|
||||
// The remaining flags are for internal use.
|
||||
flagTouchedX
|
||||
flagTouchedY
|
||||
)
|
||||
|
||||
// The same flag bits (0x10 and 0x20) are overloaded to have two meanings,
|
||||
|
@ -112,25 +121,27 @@ func (g *GlyphBuf) Load(f *Font, scale int32, i Index, h *Hinter) error {
|
|||
// Reset the GlyphBuf.
|
||||
g.B = Bounds{}
|
||||
g.Point = g.Point[:0]
|
||||
g.Unhinted = g.Unhinted[:0]
|
||||
g.InFontUnits = g.InFontUnits[:0]
|
||||
g.Twilight = g.Twilight[:0]
|
||||
g.End = g.End[:0]
|
||||
if err := g.load(f, scale, i, 0, 0, false, 0); err != nil {
|
||||
if h != nil {
|
||||
if err := h.init(g, f, scale); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := g.load(f, scale, i, h, 0, 0, false, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
g.B.XMin = f.scale(scale * g.B.XMin)
|
||||
g.B.YMin = f.scale(scale * g.B.YMin)
|
||||
g.B.XMax = f.scale(scale * g.B.XMax)
|
||||
g.B.YMax = f.scale(scale * g.B.YMax)
|
||||
if h != nil {
|
||||
if err := h.init(f, scale); err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO: invoke h.
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// loadCompound loads a glyph that is composed of other glyphs.
|
||||
func (g *GlyphBuf) loadCompound(f *Font, scale int32, glyf []byte, offset int,
|
||||
func (g *GlyphBuf) loadCompound(f *Font, scale int32, h *Hinter, glyf []byte, offset int,
|
||||
dx, dy int32, recursion int) error {
|
||||
|
||||
// Flags for decoding a compound glyph. These flags are documented at
|
||||
|
@ -150,7 +161,7 @@ func (g *GlyphBuf) loadCompound(f *Font, scale int32, glyf []byte, offset int,
|
|||
)
|
||||
for {
|
||||
flags := u16(glyf, offset)
|
||||
component := u16(glyf, offset+2)
|
||||
component := Index(u16(glyf, offset+2))
|
||||
dx1, dy1 := dx, dy
|
||||
if flags&flagArg1And2AreWords != 0 {
|
||||
dx1 += int32(int16(u16(glyf, offset+4)))
|
||||
|
@ -168,7 +179,7 @@ func (g *GlyphBuf) loadCompound(f *Font, scale int32, glyf []byte, offset int,
|
|||
return UnsupportedError("compound glyph scale/transform")
|
||||
}
|
||||
b0 := g.B
|
||||
g.load(f, scale, Index(component), dx1, dy1, flags&flagRoundXYToGrid != 0, recursion+1)
|
||||
g.load(f, scale, component, h, dx1, dy1, flags&flagRoundXYToGrid != 0, recursion+1)
|
||||
if flags&flagUseMyMetrics == 0 {
|
||||
g.B = b0
|
||||
}
|
||||
|
@ -180,7 +191,7 @@ func (g *GlyphBuf) loadCompound(f *Font, scale int32, glyf []byte, offset int,
|
|||
}
|
||||
|
||||
// load appends a glyph's contours to this GlyphBuf.
|
||||
func (g *GlyphBuf) load(f *Font, scale int32, i Index,
|
||||
func (g *GlyphBuf) load(f *Font, scale int32, i Index, h *Hinter,
|
||||
dx, dy int32, roundDxDy bool, recursion int) error {
|
||||
|
||||
if recursion >= 4 {
|
||||
|
@ -207,7 +218,7 @@ func (g *GlyphBuf) load(f *Font, scale int32, i Index,
|
|||
g.B.YMax = int32(int16(u16(glyf, 8)))
|
||||
offset := 10
|
||||
if ne == -1 {
|
||||
return g.loadCompound(f, scale, glyf, offset, dx, dy, recursion)
|
||||
return g.loadCompound(f, scale, h, glyf, offset, dx, dy, recursion)
|
||||
} else if ne < 0 {
|
||||
// http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html says that
|
||||
// "the values -2, -3, and so forth, are reserved for future use."
|
||||
|
@ -224,18 +235,33 @@ func (g *GlyphBuf) load(f *Font, scale int32, i Index,
|
|||
g.End[i] = 1 + np0 + int(u16(glyf, offset))
|
||||
offset += 2
|
||||
}
|
||||
// Skip the TrueType hinting instructions.
|
||||
|
||||
// Note the TrueType hinting instructions.
|
||||
instrLen := int(u16(glyf, offset))
|
||||
offset += 2 + instrLen
|
||||
offset += 2
|
||||
program := glyf[offset : offset+instrLen]
|
||||
offset += instrLen
|
||||
|
||||
// Decode the points.
|
||||
np := int(g.End[ne-1])
|
||||
if np <= cap(g.Point) {
|
||||
g.Point = g.Point[:np]
|
||||
} else {
|
||||
p := g.Point
|
||||
g.Point = make([]Point, np, np*2)
|
||||
copy(g.Point, p)
|
||||
}
|
||||
offset = g.decodeFlags(glyf, offset, np0)
|
||||
g.decodeCoords(glyf, offset, np0)
|
||||
|
||||
// Delta-adjust, scale and hint.
|
||||
if h != nil {
|
||||
g.InFontUnits = append(g.InFontUnits, g.Point[np0:np]...)
|
||||
for i := np0; i < np; i++ {
|
||||
g.InFontUnits[i].X += dx
|
||||
g.InFontUnits[i].Y += dy
|
||||
}
|
||||
}
|
||||
if roundDxDy {
|
||||
dx = (f.scale(scale*dx) + 32) &^ 63
|
||||
dy = (f.scale(scale*dy) + 32) &^ 63
|
||||
|
@ -249,9 +275,23 @@ func (g *GlyphBuf) load(f *Font, scale int32, i Index,
|
|||
g.Point[i].Y = f.scale(scale * (g.Point[i].Y + dy))
|
||||
}
|
||||
}
|
||||
if h != nil {
|
||||
g.Unhinted = append(g.Unhinted, g.Point[np0:np]...)
|
||||
if err := h.run(program); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *GlyphBuf) points(zonePointer int32) []Point {
|
||||
if zonePointer == 0 {
|
||||
return g.Twilight
|
||||
}
|
||||
return g.Point
|
||||
}
|
||||
|
||||
// NewGlyphBuf returns a newly allocated GlyphBuf.
|
||||
func NewGlyphBuf() *GlyphBuf {
|
||||
g := new(GlyphBuf)
|
||||
|
|
|
@ -28,9 +28,11 @@ type Hinter struct {
|
|||
// functions is a map from function number to bytecode.
|
||||
functions map[int32][]byte
|
||||
|
||||
// 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.
|
||||
// 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 *Font
|
||||
scale int32
|
||||
|
||||
|
@ -75,7 +77,9 @@ var globalDefaultGS = graphicsState{
|
|||
autoFlip: true,
|
||||
}
|
||||
|
||||
func (h *Hinter) init(f *Font, scale int32) error {
|
||||
func (h *Hinter) init(g *GlyphBuf, f *Font, scale int32) error {
|
||||
h.g = g
|
||||
|
||||
rescale := h.scale != scale
|
||||
if h.font != f {
|
||||
h.font, rescale = f, true
|
||||
|
@ -158,20 +162,20 @@ func (h *Hinter) run(program []byte) error {
|
|||
case opSVTCA0:
|
||||
h.gs.pv = [2]f2dot14{0, 0x4000}
|
||||
h.gs.fv = [2]f2dot14{0, 0x4000}
|
||||
// TODO: h.gs.dv = h.gs.pv ??
|
||||
h.gs.dv = [2]f2dot14{0, 0x4000}
|
||||
|
||||
case opSVTCA1:
|
||||
h.gs.pv = [2]f2dot14{0x4000, 0}
|
||||
h.gs.fv = [2]f2dot14{0x4000, 0}
|
||||
// TODO: h.gs.dv = h.gs.pv ??
|
||||
h.gs.dv = [2]f2dot14{0x4000, 0}
|
||||
|
||||
case opSPVTCA0:
|
||||
h.gs.pv = [2]f2dot14{0, 0x4000}
|
||||
// TODO: h.gs.dv = h.gs.pv ??
|
||||
h.gs.dv = [2]f2dot14{0, 0x4000}
|
||||
|
||||
case opSPVTCA1:
|
||||
h.gs.pv = [2]f2dot14{0x4000, 0}
|
||||
// TODO: h.gs.dv = h.gs.pv ??
|
||||
h.gs.dv = [2]f2dot14{0x4000, 0}
|
||||
|
||||
case opSFVTCA0:
|
||||
h.gs.fv = [2]f2dot14{0, 0x4000}
|
||||
|
@ -360,6 +364,45 @@ func (h *Hinter) run(program []byte) error {
|
|||
}
|
||||
program, pc = callStack[callStackTop].program, callStack[callStackTop].pc
|
||||
|
||||
case opMDAP0, opMDAP1:
|
||||
points := h.g.points(h.gs.zp[0])
|
||||
top--
|
||||
i := int(h.stack[top])
|
||||
if i < 0 || len(points) <= i {
|
||||
return errors.New("truetype: hinting: point out of range")
|
||||
}
|
||||
p := &points[i]
|
||||
distance := f26dot6(0)
|
||||
if opcode == opMDAP1 {
|
||||
distance = dotProduct(f26dot6(p.X), f26dot6(p.Y), h.gs.pv)
|
||||
// TODO: metrics compensation.
|
||||
distance = h.round(distance) - distance
|
||||
}
|
||||
h.move(p, distance)
|
||||
h.gs.rp[0] = int32(i)
|
||||
h.gs.rp[1] = int32(i)
|
||||
|
||||
case opALIGNRP:
|
||||
if top < int(h.gs.loop) {
|
||||
return errors.New("truetype: hinting: stack underflow")
|
||||
}
|
||||
i, points := int(h.gs.rp[0]), h.g.points(h.gs.zp[0])
|
||||
if i < 0 || len(points) <= i {
|
||||
return errors.New("truetype: hinting: point out of range")
|
||||
}
|
||||
ref := &points[i]
|
||||
points = h.g.points(h.gs.zp[1])
|
||||
for ; h.gs.loop != 0; h.gs.loop-- {
|
||||
top--
|
||||
i = int(h.stack[top])
|
||||
if i < 0 || len(points) <= i {
|
||||
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
|
||||
|
||||
case opRTDG:
|
||||
h.gs.roundPeriod = 1 << 5
|
||||
h.gs.roundPhase = 0
|
||||
|
@ -617,6 +660,80 @@ func (h *Hinter) run(program []byte) error {
|
|||
opcode += 0x80
|
||||
goto push
|
||||
|
||||
case opMDRP00000, opMDRP00001, opMDRP00010, opMDRP00011,
|
||||
opMDRP00100, opMDRP00101, opMDRP00110, opMDRP00111,
|
||||
opMDRP01000, opMDRP01001, opMDRP01010, opMDRP01011,
|
||||
opMDRP01100, opMDRP01101, opMDRP01110, opMDRP01111,
|
||||
opMDRP10000, opMDRP10001, opMDRP10010, opMDRP10011,
|
||||
opMDRP10100, opMDRP10101, opMDRP10110, opMDRP10111,
|
||||
opMDRP11000, opMDRP11001, opMDRP11010, opMDRP11011,
|
||||
opMDRP11100, opMDRP11101, opMDRP11110, opMDRP11111:
|
||||
|
||||
i, points := int(h.gs.rp[0]), h.g.points(h.gs.zp[0])
|
||||
if i < 0 || len(points) <= i {
|
||||
return errors.New("truetype: hinting: point out of range")
|
||||
}
|
||||
ref := &points[i]
|
||||
top--
|
||||
i = int(h.stack[top])
|
||||
points = h.g.points(h.gs.zp[1])
|
||||
if i < 0 || len(points) <= i {
|
||||
return errors.New("truetype: hinting: point out of range")
|
||||
}
|
||||
p := &points[i]
|
||||
|
||||
origDist := f26dot6(0)
|
||||
if h.gs.zp[0] == 0 && h.gs.zp[1] == 0 {
|
||||
p0 := &h.g.Unhinted[i]
|
||||
p1 := &h.g.Unhinted[h.gs.rp[0]]
|
||||
origDist = dotProduct(f26dot6(p0.X-p1.X), f26dot6(p0.Y-p1.Y), h.gs.dv)
|
||||
} else {
|
||||
p0 := &h.g.InFontUnits[i]
|
||||
p1 := &h.g.InFontUnits[h.gs.rp[0]]
|
||||
origDist = dotProduct(f26dot6(p0.X-p1.X), f26dot6(p0.Y-p1.Y), h.gs.dv)
|
||||
origDist = f26dot6(h.font.scale(h.scale * int32(origDist)))
|
||||
}
|
||||
|
||||
// Single-width cut-in test.
|
||||
if x := (origDist - h.gs.singleWidth).abs(); x < h.gs.singleWidthCutIn {
|
||||
if origDist >= 0 {
|
||||
origDist = h.gs.singleWidthCutIn
|
||||
} else {
|
||||
origDist = -h.gs.singleWidthCutIn
|
||||
}
|
||||
}
|
||||
|
||||
// Rounding bit.
|
||||
// TODO: metrics compensation.
|
||||
distance := origDist
|
||||
if opcode&0x04 != 0 {
|
||||
distance = h.round(origDist)
|
||||
}
|
||||
|
||||
// Minimum distance bit.
|
||||
if opcode&0x08 != 0 {
|
||||
if origDist >= 0 {
|
||||
if distance < h.gs.minDist {
|
||||
distance = h.gs.minDist
|
||||
}
|
||||
} else {
|
||||
if distance > -h.gs.minDist {
|
||||
distance = -h.gs.minDist
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set-RP0 bit.
|
||||
if opcode&0x10 != 0 {
|
||||
h.gs.rp[0] = int32(i)
|
||||
}
|
||||
h.gs.rp[1] = h.gs.rp[0]
|
||||
h.gs.rp[2] = int32(i)
|
||||
|
||||
// Move the point.
|
||||
origDist = dotProduct(f26dot6(p.X-ref.X), f26dot6(p.Y-ref.Y), h.gs.pv)
|
||||
h.move(p, distance-origDist)
|
||||
|
||||
default:
|
||||
return errors.New("truetype: hinting: unrecognized instruction")
|
||||
}
|
||||
|
@ -698,6 +815,27 @@ func (h *Hinter) run(program []byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (h *Hinter) move(p *Point, distance f26dot6) {
|
||||
if h.gs.fv[0] == 0 {
|
||||
p.Y += int32(distance)
|
||||
p.Flags |= flagTouchedY
|
||||
return
|
||||
}
|
||||
if h.gs.fv[1] == 0 {
|
||||
p.X += int32(distance)
|
||||
p.Flags |= flagTouchedX
|
||||
return
|
||||
}
|
||||
fvx := int64(h.gs.fv[0])
|
||||
fvy := int64(h.gs.fv[1])
|
||||
pvx := int64(h.gs.pv[0])
|
||||
pvy := int64(h.gs.pv[1])
|
||||
fvDotPv := (fvx*pvx + fvy*pvy) >> 14
|
||||
p.X += int32(int64(distance) * fvx / fvDotPv)
|
||||
p.Y += int32(int64(distance) * fvy / fvDotPv)
|
||||
p.Flags |= flagTouchedX | flagTouchedY
|
||||
}
|
||||
|
||||
// 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) {
|
||||
|
@ -730,6 +868,14 @@ type f2dot14 int16
|
|||
// f26dot6 is a 26.6 fixed point number.
|
||||
type f26dot6 int32
|
||||
|
||||
// abs returns abs(x) in 26.6 fixed point arithmetic.
|
||||
func (x f26dot6) abs() f26dot6 {
|
||||
if x < 0 {
|
||||
return -x
|
||||
}
|
||||
return x
|
||||
}
|
||||
|
||||
// div returns x/y in 26.6 fixed point arithmetic.
|
||||
func (x f26dot6) div(y f26dot6) f26dot6 {
|
||||
return f26dot6((int64(x) << 6) / int64(y))
|
||||
|
@ -740,6 +886,14 @@ func (x f26dot6) mul(y f26dot6) f26dot6 {
|
|||
return f26dot6(int64(x) * int64(y) >> 6)
|
||||
}
|
||||
|
||||
func dotProduct(x, y f26dot6, q [2]f2dot14) f26dot6 {
|
||||
px := int64(x)
|
||||
py := int64(y)
|
||||
qx := int64(q[0])
|
||||
qy := int64(q[1])
|
||||
return f26dot6((px*qx + py*qy) >> 14)
|
||||
}
|
||||
|
||||
// round rounds the given number. The rounding algorithm is described at
|
||||
// https://developer.apple.com/fonts/TTRefMan/RM02/Chap2.html#rounding
|
||||
func (h *Hinter) round(x f26dot6) f26dot6 {
|
||||
|
|
|
@ -553,9 +553,10 @@ func TestBytecode(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
var g GlyphBuf
|
||||
for _, tc := range testCases {
|
||||
h := &Hinter{}
|
||||
h.init(&Font{
|
||||
h.init(&g, &Font{
|
||||
maxStorage: 32,
|
||||
maxStackElements: 100,
|
||||
}, 768)
|
||||
|
|
|
@ -56,8 +56,8 @@ const (
|
|||
opCALL = 0x2b // CALL function
|
||||
opFDEF = 0x2c // Function DEFinition
|
||||
opENDF = 0x2d // END Function definition
|
||||
opMDAP0 = 0x2e
|
||||
opMDAP1 = 0x2f
|
||||
opMDAP0 = 0x2e // Move Direct Absolute Point
|
||||
opMDAP1 = 0x2f // .
|
||||
opIUP0 = 0x30
|
||||
opIUP1 = 0x31
|
||||
opSHP0 = 0x32
|
||||
|
@ -70,7 +70,7 @@ const (
|
|||
opIP = 0x39
|
||||
opMSIRP0 = 0x3a
|
||||
opMSIRP1 = 0x3b
|
||||
opALIGNRP = 0x3c
|
||||
opALIGNRP = 0x3c // ALIGN to Reference Point
|
||||
opRTDG = 0x3d // Round To Double Grid
|
||||
opMIAP0 = 0x3e
|
||||
opMIAP1 = 0x3f
|
||||
|
@ -202,38 +202,38 @@ const (
|
|||
opPUSHW101 = 0xbd // .
|
||||
opPUSHW110 = 0xbe // .
|
||||
opPUSHW111 = 0xbf // .
|
||||
opMDRP00000 = 0xc0
|
||||
opMDRP00001 = 0xc1
|
||||
opMDRP00010 = 0xc2
|
||||
opMDRP00011 = 0xc3
|
||||
opMDRP00100 = 0xc4
|
||||
opMDRP00101 = 0xc5
|
||||
opMDRP00110 = 0xc6
|
||||
opMDRP00111 = 0xc7
|
||||
opMDRP01000 = 0xc8
|
||||
opMDRP01001 = 0xc9
|
||||
opMDRP01010 = 0xca
|
||||
opMDRP01011 = 0xcb
|
||||
opMDRP01100 = 0xcc
|
||||
opMDRP01101 = 0xcd
|
||||
opMDRP01110 = 0xce
|
||||
opMDRP01111 = 0xcf
|
||||
opMDRP10000 = 0xd0
|
||||
opMDRP10001 = 0xd1
|
||||
opMDRP10010 = 0xd2
|
||||
opMDRP10011 = 0xd3
|
||||
opMDRP10100 = 0xd4
|
||||
opMDRP10101 = 0xd5
|
||||
opMDRP10110 = 0xd6
|
||||
opMDRP10111 = 0xd7
|
||||
opMDRP11000 = 0xd8
|
||||
opMDRP11001 = 0xd9
|
||||
opMDRP11010 = 0xda
|
||||
opMDRP11011 = 0xdb
|
||||
opMDRP11100 = 0xdd
|
||||
opMDRP11101 = 0xdc
|
||||
opMDRP11110 = 0xde
|
||||
opMDRP11111 = 0xdf
|
||||
opMDRP00000 = 0xc0 // Move Direct Relative Point
|
||||
opMDRP00001 = 0xc1 // .
|
||||
opMDRP00010 = 0xc2 // .
|
||||
opMDRP00011 = 0xc3 // .
|
||||
opMDRP00100 = 0xc4 // .
|
||||
opMDRP00101 = 0xc5 // .
|
||||
opMDRP00110 = 0xc6 // .
|
||||
opMDRP00111 = 0xc7 // .
|
||||
opMDRP01000 = 0xc8 // .
|
||||
opMDRP01001 = 0xc9 // .
|
||||
opMDRP01010 = 0xca // .
|
||||
opMDRP01011 = 0xcb // .
|
||||
opMDRP01100 = 0xcc // .
|
||||
opMDRP01101 = 0xcd // .
|
||||
opMDRP01110 = 0xce // .
|
||||
opMDRP01111 = 0xcf // .
|
||||
opMDRP10000 = 0xd0 // .
|
||||
opMDRP10001 = 0xd1 // .
|
||||
opMDRP10010 = 0xd2 // .
|
||||
opMDRP10011 = 0xd3 // .
|
||||
opMDRP10100 = 0xd4 // .
|
||||
opMDRP10101 = 0xd5 // .
|
||||
opMDRP10110 = 0xd6 // .
|
||||
opMDRP10111 = 0xd7 // .
|
||||
opMDRP11000 = 0xd8 // .
|
||||
opMDRP11001 = 0xd9 // .
|
||||
opMDRP11010 = 0xda // .
|
||||
opMDRP11011 = 0xdb // .
|
||||
opMDRP11100 = 0xdc // .
|
||||
opMDRP11101 = 0xdd // .
|
||||
opMDRP11110 = 0xde // .
|
||||
opMDRP11111 = 0xdf // .
|
||||
opMIRP00000 = 0xe0
|
||||
opMIRP00001 = 0xe1
|
||||
opMIRP00010 = 0xe2
|
||||
|
@ -262,8 +262,8 @@ const (
|
|||
opMIRP11001 = 0xf9
|
||||
opMIRP11010 = 0xfa
|
||||
opMIRP11011 = 0xfb
|
||||
opMIRP11100 = 0xfd
|
||||
opMIRP11101 = 0xfc
|
||||
opMIRP11100 = 0xfc
|
||||
opMIRP11101 = 0xfd
|
||||
opMIRP11110 = 0xfe
|
||||
opMIRP11111 = 0xff
|
||||
)
|
||||
|
@ -273,8 +273,8 @@ var popCount = [256]uint8{
|
|||
// 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f
|
||||
0, 0, 0, 0, 0, 0, q, q, q, q, 2, 2, 0, 0, 0, q, // 0x00 - 0x0f
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, // 0x10 - 0x1f
|
||||
1, 1, 0, 2, 0, 1, 1, q, q, q, 2, 1, 1, 0, q, q, // 0x20 - 0x2f
|
||||
q, q, q, q, q, q, q, q, q, q, q, q, q, 0, q, q, // 0x30 - 0x3f
|
||||
1, 1, 0, 2, 0, 1, 1, q, q, q, 2, 1, 1, 0, 1, 1, // 0x20 - 0x2f
|
||||
q, q, q, q, q, q, q, q, q, q, q, q, 0, 0, q, q, // 0x30 - 0x3f
|
||||
0, 0, 2, 1, q, q, q, q, q, q, q, 0, 0, 0, 0, 0, // 0x40 - 0x4f
|
||||
2, 2, 2, 2, 2, 2, 1, 1, 1, 0, 2, 2, 1, q, 1, 1, // 0x50 - 0x5f
|
||||
2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 - 0x6f
|
||||
|
@ -283,8 +283,8 @@ var popCount = [256]uint8{
|
|||
q, q, q, q, q, q, q, q, q, q, q, q, q, q, q, q, // 0x90 - 0x9f
|
||||
q, q, q, q, q, q, q, q, q, q, q, q, q, q, q, q, // 0xa0 - 0xaf
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xb0 - 0xbf
|
||||
q, q, q, q, q, q, q, q, q, q, q, q, q, q, q, q, // 0xc0 - 0xcf
|
||||
q, q, q, q, q, q, q, q, q, q, q, q, q, q, q, q, // 0xd0 - 0xdf
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xc0 - 0xcf
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xd0 - 0xdf
|
||||
q, q, q, q, q, q, q, q, q, q, q, q, q, q, q, q, // 0xe0 - 0xef
|
||||
q, q, q, q, q, q, q, q, q, q, q, q, q, q, q, q, // 0xf0 - 0xff
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ func TestParse(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func testScaling(t *testing.T, filename string) {
|
||||
func testScaling(t *testing.T, filename string, hinter *Hinter) {
|
||||
b, err := ioutil.ReadFile("../../luxi-fonts/luxisr.ttf")
|
||||
if err != nil {
|
||||
t.Fatalf("ReadFile: %v", err)
|
||||
|
@ -114,7 +114,14 @@ func testScaling(t *testing.T, filename string) {
|
|||
const fontSize = 12
|
||||
glyphBuf := NewGlyphBuf()
|
||||
for i, want := range wants {
|
||||
if err = glyphBuf.Load(font, fontSize*64, Index(i), nil); err != nil {
|
||||
// TODO: completely implement hinting. For now, only the first N glyphs
|
||||
// of luxisr.ttf are correctly hinted.
|
||||
const N = 1
|
||||
if hinter != nil && i == N {
|
||||
break
|
||||
}
|
||||
|
||||
if err = glyphBuf.Load(font, fontSize*64, Index(i), hinter); err != nil {
|
||||
t.Fatalf("Load: %v", err)
|
||||
}
|
||||
got := glyphBuf.Point
|
||||
|
@ -128,9 +135,9 @@ func testScaling(t *testing.T, filename string) {
|
|||
}
|
||||
|
||||
func TestScalingSansHinting(t *testing.T) {
|
||||
testScaling(t, "luxisr-12pt-sans-hinting.txt")
|
||||
testScaling(t, "luxisr-12pt-sans-hinting.txt", nil)
|
||||
}
|
||||
|
||||
func TestScalingWithHinting(t *testing.T) {
|
||||
// TODO.
|
||||
testScaling(t, "luxisr-12pt-with-hinting.txt", &Hinter{})
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user