Use the fixed.Int26_6 type in package truetype.

This commit is contained in:
Nigel Tao 2015-08-18 16:30:37 +10:00
parent a9554eda48
commit 2a5cbfd47e
8 changed files with 117 additions and 103 deletions

View File

@ -116,14 +116,14 @@ func main() {
}
// Draw the text.
pt := freetype.Pt(10, 10+int(c.PointToFix32(*size)>>6))
pt := freetype.Pt(10, 10+int(c.PointToFixed(*size)>>6))
for _, s := range text {
_, err = c.DrawString(s, pt)
if err != nil {
log.Println(err)
return
}
pt.Y += c.PointToFix32(*size * *spacing)
pt.Y += c.PointToFixed(*size * *spacing)
}
// Save that RGBA image to disk.

View File

@ -17,6 +17,7 @@ import (
"log"
"github.com/golang/freetype/truetype"
"golang.org/x/image/math/fixed"
)
var fontfile = flag.String("fontfile", "../../testdata/luxisr.ttf", "filename of the ttf font")
@ -56,7 +57,7 @@ func main() {
log.Println(err)
return
}
fupe := font.FUnitsPerEm()
fupe := fixed.Int26_6(font.FUnitsPerEm())
printBounds(font.Bounds(fupe))
fmt.Printf("FUnitsPerEm:%d\n\n", fupe)

View File

@ -78,15 +78,15 @@ type Context struct {
// fontSize and dpi are used to calculate scale. scale is the number of
// 26.6 fixed point units in 1 em. hinting is the hinting policy.
fontSize, dpi float64
scale int32
scale fixed.Int26_6
hinting Hinting
// cache is the glyph cache.
cache [nGlyphs * nXFractions * nYFractions]cacheEntry
}
// PointToFix32 converts the given number of points (as in ``a 12 point font'')
// into fixed point units.
func (c *Context) PointToFix32(x float64) fixed.Int26_6 {
// PointToFixed converts the given number of points (as in ``a 12 point font'')
// into a 26.6 fixed point number of pixels.
func (c *Context) PointToFixed(x float64) fixed.Int26_6 {
return fixed.Int26_6(x * float64(c.dpi) * (64.0 / 72.0))
}
@ -269,7 +269,7 @@ func (c *Context) DrawString(s string, p fixed.Point26_6) (fixed.Point26_6, erro
// recalc recalculates scale and bounds values from the font size, screen
// resolution and font metrics, and invalidates the glyph cache.
func (c *Context) recalc() {
c.scale = int32(c.fontSize * c.dpi * (64.0 / 72.0))
c.scale = fixed.Int26_6(c.fontSize * c.dpi * (64.0 / 72.0))
if c.font == nil {
c.r.SetBounds(0, 0)
} else {

View File

@ -5,8 +5,12 @@
package truetype
import (
"golang.org/x/image/math/fixed"
)
// Hinting is the policy for snapping a glyph's contours to pixel boundaries.
type Hinting int32
type Hinting uint32
const (
// NoHinting means to not perform any hinting.
@ -20,7 +24,7 @@ const (
// A Point is a co-ordinate pair plus whether it is ``on'' a contour or an
// ``off'' control point.
type Point struct {
X, Y int32
X, Y fixed.Int26_6
// The Flags' LSB means whether or not this Point is ``on'' the contour.
// Other bits are reserved for internal use.
Flags uint32
@ -30,7 +34,7 @@ type Point struct {
// series of glyphs from a Font.
type GlyphBuf struct {
// AdvanceWidth is the glyph's advance width.
AdvanceWidth int32
AdvanceWidth fixed.Int26_6
// B is the glyph's bounding box.
B Bounds
// Point contains all Points from all contours of the glyph. If
@ -45,7 +49,7 @@ type GlyphBuf struct {
End []int
font *Font
scale int32
scale fixed.Int26_6
hinting Hinting
hinter hinter
// phantomPoints are the co-ordinates of the synthetic phantom points
@ -54,7 +58,7 @@ type GlyphBuf struct {
// pp1x is the X co-ordinate of the first phantom point. The '1' is
// using 1-based indexing; pp1x is almost always phantomPoints[0].X.
// TODO: eliminate this and consistently use phantomPoints[0].X.
pp1x int32
pp1x fixed.Int26_6
// metricsSet is whether the glyph's metrics have been set yet. For a
// compound glyph, a sub-glyph may override the outer glyph's metrics.
metricsSet bool
@ -84,10 +88,10 @@ const (
flagThisYIsSame = flagPositiveYShortVector
)
// Load loads a glyph's contours from a Font, overwriting any previously
// loaded contours for this GlyphBuf. scale is the number of 26.6 fixed point
// units in 1 em, i is the glyph index, and h is the hinting policy.
func (g *GlyphBuf) Load(f *Font, scale int32, i Index, h Hinting) error {
// Load loads a glyph's contours from a Font, overwriting any previously loaded
// contours for this GlyphBuf. scale is the number of 26.6 fixed point units in
// 1 em, i is the glyph index, and h is the hinting policy.
func (g *GlyphBuf) Load(f *Font, scale fixed.Int26_6, i Index, h Hinting) error {
g.Point = g.Point[:0]
g.Unhinted = g.Unhinted[:0]
g.InFontUnits = g.InFontUnits[:0]
@ -125,8 +129,8 @@ func (g *GlyphBuf) Load(f *Font, scale int32, i Index, h Hinting) error {
if len(f.hdmx) >= 8 {
if n := u32(f.hdmx, 4); n > 3+uint32(i) {
for hdmx := f.hdmx[8:]; uint32(len(hdmx)) >= n; hdmx = hdmx[n:] {
if int32(hdmx[0]) == scale>>6 {
advanceWidth = int32(hdmx[2+i]) << 6
if fixed.Int26_6(hdmx[0]) == scale>>6 {
advanceWidth = fixed.Int26_6(hdmx[2+i]) << 6
break
}
}
@ -175,7 +179,7 @@ func (g *GlyphBuf) Load(f *Font, scale int32, i Index, h Hinting) error {
return nil
}
func (g *GlyphBuf) load(recursion int32, i Index, useMyMetrics bool) (err error) {
func (g *GlyphBuf) load(recursion uint32, i Index, useMyMetrics bool) (err error) {
// The recursion limit here is arbitrary, but defends against malformed glyphs.
if recursion >= 32 {
return UnsupportedError("excessive compound glyph recursion")
@ -193,16 +197,16 @@ func (g *GlyphBuf) load(recursion int32, i Index, useMyMetrics bool) (err error)
// Decode the contour count and nominal bounding box, from the first
// 10 bytes of the glyf data. boundsYMin and boundsXMax, at offsets 4
// and 6, are unused.
glyf, ne, boundsXMin, boundsYMax := []byte(nil), 0, int32(0), int32(0)
glyf, ne, boundsXMin, boundsYMax := []byte(nil), 0, fixed.Int26_6(0), fixed.Int26_6(0)
if g0+10 <= g1 {
glyf = g.font.glyf[g0:g1]
ne = int(int16(u16(glyf, 0)))
boundsXMin = int32(int16(u16(glyf, 2)))
boundsYMax = int32(int16(u16(glyf, 8)))
boundsXMin = fixed.Int26_6(int16(u16(glyf, 2)))
boundsYMax = fixed.Int26_6(int16(u16(glyf, 8)))
}
// Create the phantom points.
uhm, pp1x := g.font.unscaledHMetric(i), int32(0)
uhm, pp1x := g.font.unscaledHMetric(i), fixed.Int26_6(0)
uvm := g.font.unscaledVMetric(i, boundsYMax)
g.phantomPoints = [4]Point{
{X: boundsXMin - uhm.LeftSideBearing},
@ -322,7 +326,7 @@ func (g *GlyphBuf) loadSimple(glyf []byte, ne int) (program []byte) {
x += int16(u16(glyf, offset))
offset += 2
}
g.Point[i].X = int32(x)
g.Point[i].X = fixed.Int26_6(x)
}
var y int16
for i := np0; i < np1; i++ {
@ -339,13 +343,13 @@ func (g *GlyphBuf) loadSimple(glyf []byte, ne int) (program []byte) {
y += int16(u16(glyf, offset))
offset += 2
}
g.Point[i].Y = int32(y)
g.Point[i].Y = fixed.Int26_6(y)
}
return program
}
func (g *GlyphBuf) loadCompound(recursion int32, uhm HMetric, i Index,
func (g *GlyphBuf) loadCompound(recursion uint32, uhm HMetric, i Index,
glyf []byte, useMyMetrics bool) error {
// Flags for decoding a compound glyph. These flags are documented at
@ -368,14 +372,14 @@ func (g *GlyphBuf) loadCompound(recursion int32, uhm HMetric, i Index,
for {
flags := u16(glyf, offset)
component := Index(u16(glyf, offset+2))
dx, dy, transform, hasTransform := int32(0), int32(0), [4]int32{}, false
dx, dy, transform, hasTransform := fixed.Int26_6(0), fixed.Int26_6(0), [4]int16{}, false
if flags&flagArg1And2AreWords != 0 {
dx = int32(int16(u16(glyf, offset+4)))
dy = int32(int16(u16(glyf, offset+6)))
dx = fixed.Int26_6(int16(u16(glyf, offset+4)))
dy = fixed.Int26_6(int16(u16(glyf, offset+6)))
offset += 8
} else {
dx = int32(int16(int8(glyf[offset+4])))
dy = int32(int16(int8(glyf[offset+5])))
dx = fixed.Int26_6(int16(int8(glyf[offset+4])))
dy = fixed.Int26_6(int16(int8(glyf[offset+5])))
offset += 6
}
if flags&flagArgsAreXYValues == 0 {
@ -385,18 +389,18 @@ func (g *GlyphBuf) loadCompound(recursion int32, uhm HMetric, i Index,
hasTransform = true
switch {
case flags&flagWeHaveAScale != 0:
transform[0] = int32(int16(u16(glyf, offset+0)))
transform[0] = int16(u16(glyf, offset+0))
transform[3] = transform[0]
offset += 2
case flags&flagWeHaveAnXAndYScale != 0:
transform[0] = int32(int16(u16(glyf, offset+0)))
transform[3] = int32(int16(u16(glyf, offset+2)))
transform[0] = int16(u16(glyf, offset+0))
transform[3] = int16(u16(glyf, offset+2))
offset += 4
case flags&flagWeHaveATwoByTwo != 0:
transform[0] = int32(int16(u16(glyf, offset+0)))
transform[1] = int32(int16(u16(glyf, offset+2)))
transform[2] = int32(int16(u16(glyf, offset+4)))
transform[3] = int32(int16(u16(glyf, offset+6)))
transform[0] = int16(u16(glyf, offset+0))
transform[1] = int16(u16(glyf, offset+2))
transform[2] = int16(u16(glyf, offset+4))
transform[3] = int16(u16(glyf, offset+6))
offset += 8
}
}
@ -412,10 +416,12 @@ func (g *GlyphBuf) loadCompound(recursion int32, uhm HMetric, i Index,
if hasTransform {
for j := np0; j < len(g.Point); j++ {
p := &g.Point[j]
newX := int32((int64(p.X)*int64(transform[0])+1<<13)>>14) +
int32((int64(p.Y)*int64(transform[2])+1<<13)>>14)
newY := int32((int64(p.X)*int64(transform[1])+1<<13)>>14) +
int32((int64(p.Y)*int64(transform[3])+1<<13)>>14)
newX := 0 +
fixed.Int26_6((int64(p.X)*int64(transform[0])+1<<13)>>14) +
fixed.Int26_6((int64(p.Y)*int64(transform[2])+1<<13)>>14)
newY := 0 +
fixed.Int26_6((int64(p.X)*int64(transform[1])+1<<13)>>14) +
fixed.Int26_6((int64(p.Y)*int64(transform[3])+1<<13)>>14)
p.X, p.Y = newX, newY
}
}

View File

@ -11,6 +11,8 @@ package truetype
import (
"errors"
"math"
"golang.org/x/image/math/fixed"
)
const (
@ -47,7 +49,7 @@ type hinter struct {
// 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
scale fixed.Int26_6
// gs and defaultGS are the current and default graphics state. The
// default graphics state is the global default graphics state after
@ -113,7 +115,7 @@ func resetTwilightPoints(f *Font, p []Point) []Point {
return p
}
func (h *hinter) init(f *Font, scale int32) error {
func (h *hinter) init(f *Font, scale fixed.Int26_6) 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])
@ -315,8 +317,8 @@ func (h *hinter) run(program []byte, pCurrent, pUnhinted, pInFontUnits []Point,
mulDiv(int64(dy), int64(dbx), 0x40)
rx := mulDiv(val, int64(dax), discriminant)
ry := mulDiv(val, int64(day), discriminant)
p.X = a0.X + int32(rx)
p.Y = a0.Y + int32(ry)
p.X = a0.X + fixed.Int26_6(rx)
p.Y = a0.Y + fixed.Int26_6(ry)
} else {
p.X = (a0.X + a1.X + b0.X + b1.X) / 4
p.Y = (a0.Y + a1.Y + b0.Y + b1.Y) / 4
@ -379,7 +381,7 @@ func (h *hinter) run(program []byte, pCurrent, pUnhinted, pInFontUnits []Point,
case opSSW:
top--
h.gs.singleWidth = f26dot6(h.font.scale(h.scale * h.stack[top]))
h.gs.singleWidth = f26dot6(h.font.scale(h.scale * fixed.Int26_6(h.stack[top])))
case opDUP:
if top >= len(h.stack) {
@ -711,8 +713,8 @@ func (h *hinter) run(program []byte, pCurrent, pUnhinted, pInFontUnits []Point,
if h.gs.zp[0] == 0 {
p := h.point(0, unhinted, i)
q := h.point(0, current, i)
p.X = int32((int64(distance) * int64(h.gs.fv[0])) >> 14)
p.Y = int32((int64(distance) * int64(h.gs.fv[1])) >> 14)
p.X = fixed.Int26_6((int64(distance) * int64(h.gs.fv[0])) >> 14)
p.Y = fixed.Int26_6((int64(distance) * int64(h.gs.fv[1])) >> 14)
*q = *p
}
p := h.point(0, current, i)
@ -809,7 +811,7 @@ func (h *hinter) run(program []byte, pCurrent, pUnhinted, pInFontUnits []Point,
}
d := int32(dotProduct(f26dot6(p.X-q.X), f26dot6(p.Y-q.Y), v))
if scale {
d = int32(int64(d*h.scale) / int64(h.font.fUnitsPerEm))
d = int32(int64(d*int32(h.scale)) / int64(h.font.fUnitsPerEm))
}
h.stack[top-1] = d
@ -818,7 +820,7 @@ func (h *hinter) run(program []byte, pCurrent, pUnhinted, pInFontUnits []Point,
return errors.New("truetype: hinting: stack overflow")
}
// For MPS, point size should be irrelevant; we return the PPEM.
h.stack[top] = h.scale >> 6
h.stack[top] = int32(h.scale) >> 6
top++
case opFLIPON, opFLIPOFF:
@ -935,7 +937,7 @@ func (h *hinter) run(program []byte, pCurrent, pUnhinted, pInFontUnits []Point,
case opWCVTF:
top -= 2
h.setScaledCVT(h.stack[top], f26dot6(h.font.scale(h.scale*h.stack[top+1])))
h.setScaledCVT(h.stack[top], f26dot6(h.font.scale(h.scale*fixed.Int26_6(h.stack[top+1]))))
case opDELTAP2, opDELTAP3, opDELTAC1, opDELTAC2, opDELTAC3:
goto delta
@ -1144,7 +1146,7 @@ func (h *hinter) run(program []byte, pCurrent, pUnhinted, pInFontUnits []Point,
p0 := h.point(1, inFontUnits, i)
p1 := h.point(0, inFontUnits, h.gs.rp[0])
oldDist = dotProduct(f26dot6(p0.X-p1.X), f26dot6(p0.Y-p1.Y), h.gs.dv)
oldDist = f26dot6(h.font.scale(h.scale * int32(oldDist)))
oldDist = f26dot6(h.font.scale(h.scale * fixed.Int26_6(oldDist)))
}
// Single-width cut-in test.
@ -1358,7 +1360,7 @@ func (h *hinter) run(program []byte, pCurrent, pUnhinted, pInFontUnits []Point,
c += 32
}
c += h.gs.deltaBase
if ppem := (h.scale + 1<<5) >> 6; ppem != c {
if ppem := (int32(h.scale) + 1<<5) >> 6; ppem != c {
continue
}
b = (b & 0x0f) - 8
@ -1399,7 +1401,7 @@ func (h *hinter) initializeScaledCVT() {
}
for i := range h.scaledCVT {
unscaled := uint16(h.font.cvt[2*i])<<8 | uint16(h.font.cvt[2*i+1])
h.scaledCVT[i] = f26dot6(h.font.scale(h.scale * int32(int16(unscaled))))
h.scaledCVT[i] = f26dot6(h.font.scale(h.scale * fixed.Int26_6(int16(unscaled))))
}
}
@ -1437,7 +1439,7 @@ func (h *hinter) move(p *Point, distance f26dot6, touch bool) {
fvx := int64(h.gs.fv[0])
pvx := int64(h.gs.pv[0])
if fvx == 0x4000 && pvx == 0x4000 {
p.X += int32(distance)
p.X += fixed.Int26_6(distance)
if touch {
p.Flags |= flagTouchedX
}
@ -1447,7 +1449,7 @@ func (h *hinter) move(p *Point, distance f26dot6, touch bool) {
fvy := int64(h.gs.fv[1])
pvy := int64(h.gs.pv[1])
if fvy == 0x4000 && pvy == 0x4000 {
p.Y += int32(distance)
p.Y += fixed.Int26_6(distance)
if touch {
p.Flags |= flagTouchedY
}
@ -1457,14 +1459,14 @@ func (h *hinter) move(p *Point, distance f26dot6, touch bool) {
fvDotPv := (fvx*pvx + fvy*pvy) >> 14
if fvx != 0 {
p.X += int32(mulDiv(fvx, int64(distance), fvDotPv))
p.X += fixed.Int26_6(mulDiv(fvx, int64(distance), fvDotPv))
if touch {
p.Flags |= flagTouchedX
}
}
if fvy != 0 {
p.Y += int32(mulDiv(fvy, int64(distance), fvDotPv))
p.Y += fixed.Int26_6(mulDiv(fvy, int64(distance), fvDotPv))
if touch {
p.Flags |= flagTouchedY
}
@ -1480,7 +1482,7 @@ func (h *hinter) iupInterp(interpY bool, p1, p2, ref1, ref2 int) {
return
}
var ifu1, ifu2 int32
var ifu1, ifu2 fixed.Int26_6
if interpY {
ifu1 = h.points[glyphZone][inFontUnits][ref1].Y
ifu2 = h.points[glyphZone][inFontUnits][ref2].Y
@ -1493,7 +1495,7 @@ func (h *hinter) iupInterp(interpY bool, p1, p2, ref1, ref2 int) {
ref1, ref2 = ref2, ref1
}
var unh1, unh2, delta1, delta2 int32
var unh1, unh2, delta1, delta2 fixed.Int26_6
if interpY {
unh1 = h.points[glyphZone][unhinted][ref1].Y
unh2 = h.points[glyphZone][unhinted][ref2].Y
@ -1506,7 +1508,7 @@ func (h *hinter) iupInterp(interpY bool, p1, p2, ref1, ref2 int) {
delta2 = h.points[glyphZone][current][ref2].X - unh2
}
var xy, ifuXY int32
var xy, ifuXY fixed.Int26_6
if ifu1 == ifu2 {
for i := p1; i <= p2; i++ {
if interpY {
@ -1555,7 +1557,7 @@ func (h *hinter) iupInterp(interpY bool, p1, p2, ref1, ref2 int) {
} else {
numer -= 0x8000
}
xy = unh1 + delta1 + int32(numer/0x10000)
xy = unh1 + delta1 + fixed.Int26_6(numer/0x10000)
}
if interpY {
@ -1567,7 +1569,7 @@ func (h *hinter) iupInterp(interpY bool, p1, p2, ref1, ref2 int) {
}
func (h *hinter) iupShift(interpY bool, p1, p2, p int) {
var delta int32
var delta fixed.Int26_6
if interpY {
delta = h.points[glyphZone][current][p].Y - h.points[glyphZone][unhinted][p].Y
} else {

View File

@ -9,6 +9,8 @@ import (
"reflect"
"strings"
"testing"
"golang.org/x/image/math/fixed"
)
func TestBytecode(t *testing.T) {
@ -589,7 +591,7 @@ func TestMove(t *testing.T) {
h, p := hinter{}, Point{}
testCases := []struct {
pvX, pvY, fvX, fvY f2dot14
wantX, wantY int32
wantX, wantY fixed.Int26_6
}{
{+0x4000, +0x0000, +0x4000, +0x0000, +1000, +0},
{+0x4000, +0x0000, -0x4000, +0x0000, +1000, +0},

View File

@ -9,10 +9,9 @@
//
// Some of a font's methods provide lengths or co-ordinates, e.g. bounds, font
// metrics and control points. All these methods take a scale parameter, which
// is the number of device units in 1 em. For example, if 1 em is 10 pixels and
// 1 pixel is 64 units, then scale is 640. If the device space involves pixels,
// 64 units per pixel is recommended, since that is what the bytecode hinter
// uses when snapping point co-ordinates to the pixel grid.
// is the number of pixels in 1 em, expressed as a 26.6 fixed point value. For
// example, if 1 em is 10 pixels then scale is fixed.I(10), which is equal to
// fixed.Int26_6(10 << 6).
//
// To measure a TrueType font in ideal FUnit space, use scale equal to
// font.FUnitsPerEm().
@ -20,6 +19,8 @@ package truetype // import "github.com/golang/freetype/truetype"
import (
"fmt"
"golang.org/x/image/math/fixed"
)
// An Index is a Font's index of a rune.
@ -28,17 +29,17 @@ type Index uint16
// A Bounds holds the co-ordinate range of one or more glyphs.
// The endpoints are inclusive.
type Bounds struct {
XMin, YMin, XMax, YMax int32
XMin, YMin, XMax, YMax fixed.Int26_6
}
// An HMetric holds the horizontal metrics of a single glyph.
type HMetric struct {
AdvanceWidth, LeftSideBearing int32
AdvanceWidth, LeftSideBearing fixed.Int26_6
}
// A VMetric holds the vertical metrics of a single glyph.
type VMetric struct {
AdvanceHeight, TopSideBearing int32
AdvanceHeight, TopSideBearing fixed.Int26_6
}
// A FormatError reports that the input is not a valid TrueType font.
@ -226,10 +227,10 @@ func (f *Font) parseHead() error {
return FormatError(fmt.Sprintf("bad head length: %d", len(f.head)))
}
f.fUnitsPerEm = int32(u16(f.head, 18))
f.bounds.XMin = int32(int16(u16(f.head, 36)))
f.bounds.YMin = int32(int16(u16(f.head, 38)))
f.bounds.XMax = int32(int16(u16(f.head, 40)))
f.bounds.YMax = int32(int16(u16(f.head, 42)))
f.bounds.XMin = fixed.Int26_6(int16(u16(f.head, 36)))
f.bounds.YMin = fixed.Int26_6(int16(u16(f.head, 38)))
f.bounds.XMax = fixed.Int26_6(int16(u16(f.head, 40)))
f.bounds.YMax = fixed.Int26_6(int16(u16(f.head, 42)))
switch i := u16(f.head, 50); i {
case 0:
f.locaOffsetFormat = locaOffsetFormatShort
@ -306,17 +307,17 @@ func (f *Font) parseMaxp() error {
}
// scale returns x divided by f.fUnitsPerEm, rounded to the nearest integer.
func (f *Font) scale(x int32) int32 {
func (f *Font) scale(x fixed.Int26_6) fixed.Int26_6 {
if x >= 0 {
x += f.fUnitsPerEm / 2
x += fixed.Int26_6(f.fUnitsPerEm) / 2
} else {
x -= f.fUnitsPerEm / 2
x -= fixed.Int26_6(f.fUnitsPerEm) / 2
}
return x / f.fUnitsPerEm
return x / fixed.Int26_6(f.fUnitsPerEm)
}
// Bounds returns the union of a Font's glyphs' bounds.
func (f *Font) Bounds(scale int32) Bounds {
func (f *Font) Bounds(scale fixed.Int26_6) Bounds {
b := f.bounds
b.XMin = f.scale(scale * b.XMin)
b.YMin = f.scale(scale * b.YMin)
@ -360,18 +361,18 @@ func (f *Font) unscaledHMetric(i Index) (h HMetric) {
if j >= f.nHMetric {
p := 4 * (f.nHMetric - 1)
return HMetric{
AdvanceWidth: int32(u16(f.hmtx, p)),
LeftSideBearing: int32(int16(u16(f.hmtx, p+2*(j-f.nHMetric)+4))),
AdvanceWidth: fixed.Int26_6(u16(f.hmtx, p)),
LeftSideBearing: fixed.Int26_6(int16(u16(f.hmtx, p+2*(j-f.nHMetric)+4))),
}
}
return HMetric{
AdvanceWidth: int32(u16(f.hmtx, 4*j)),
LeftSideBearing: int32(int16(u16(f.hmtx, 4*j+2))),
AdvanceWidth: fixed.Int26_6(u16(f.hmtx, 4*j)),
LeftSideBearing: fixed.Int26_6(int16(u16(f.hmtx, 4*j+2))),
}
}
// HMetric returns the horizontal metrics for the glyph with the given index.
func (f *Font) HMetric(scale int32, i Index) HMetric {
func (f *Font) HMetric(scale fixed.Int26_6, i Index) HMetric {
h := f.unscaledHMetric(i)
h.AdvanceWidth = f.scale(scale * h.AdvanceWidth)
h.LeftSideBearing = f.scale(scale * h.LeftSideBearing)
@ -380,15 +381,15 @@ func (f *Font) HMetric(scale int32, i Index) HMetric {
// unscaledVMetric returns the unscaled vertical metrics for the glyph with
// the given index. yMax is the top of the glyph's bounding box.
func (f *Font) unscaledVMetric(i Index, yMax int32) (v VMetric) {
func (f *Font) unscaledVMetric(i Index, yMax fixed.Int26_6) (v VMetric) {
j := int(i)
if j < 0 || f.nGlyph <= j {
return VMetric{}
}
if 4*j+4 <= len(f.vmtx) {
return VMetric{
AdvanceHeight: int32(u16(f.vmtx, 4*j)),
TopSideBearing: int32(int16(u16(f.vmtx, 4*j+2))),
AdvanceHeight: fixed.Int26_6(u16(f.vmtx, 4*j)),
TopSideBearing: fixed.Int26_6(int16(u16(f.vmtx, 4*j+2))),
}
}
// The OS/2 table has grown over time.
@ -397,21 +398,21 @@ func (f *Font) unscaledVMetric(i Index, yMax int32) (v VMetric) {
// the ascender and descender, are described at
// http://www.microsoft.com/typography/otspec/os2.htm
if len(f.os2) >= 72 {
sTypoAscender := int32(int16(u16(f.os2, 68)))
sTypoDescender := int32(int16(u16(f.os2, 70)))
sTypoAscender := fixed.Int26_6(int16(u16(f.os2, 68)))
sTypoDescender := fixed.Int26_6(int16(u16(f.os2, 70)))
return VMetric{
AdvanceHeight: sTypoAscender - sTypoDescender,
TopSideBearing: sTypoAscender - yMax,
}
}
return VMetric{
AdvanceHeight: f.fUnitsPerEm,
AdvanceHeight: fixed.Int26_6(f.fUnitsPerEm),
TopSideBearing: 0,
}
}
// VMetric returns the vertical metrics for the glyph with the given index.
func (f *Font) VMetric(scale int32, i Index) VMetric {
func (f *Font) VMetric(scale fixed.Int26_6, i Index) VMetric {
// TODO: should 0 be bounds.YMax?
v := f.unscaledVMetric(i, 0)
v.AdvanceHeight = f.scale(scale * v.AdvanceHeight)
@ -420,7 +421,7 @@ func (f *Font) VMetric(scale int32, i Index) VMetric {
}
// Kerning returns the kerning for the given glyph pair.
func (f *Font) Kerning(scale int32, i0, i1 Index) int32 {
func (f *Font) Kerning(scale fixed.Int26_6, i0, i1 Index) fixed.Int26_6 {
if f.nKern == 0 {
return 0
}
@ -434,7 +435,7 @@ func (f *Font) Kerning(scale int32, i0, i1 Index) int32 {
} else if ig > g {
hi = i
} else {
return f.scale(scale * int32(int16(u16(f.kern, 22+6*i))))
return f.scale(scale * fixed.Int26_6(int16(u16(f.kern, 22+6*i))))
}
}
return 0

View File

@ -14,6 +14,8 @@ import (
"strconv"
"strings"
"testing"
"golang.org/x/image/math/fixed"
)
func parseTestdataFont(name string) (font *Font, testdataIsOptional bool, err error) {
@ -40,7 +42,7 @@ func TestParse(t *testing.T) {
if got, want := font.FUnitsPerEm(), int32(2048); got != want {
t.Errorf("FUnitsPerEm: got %v, want %v", got, want)
}
fupe := font.FUnitsPerEm()
fupe := fixed.Int26_6(font.FUnitsPerEm())
if got, want := font.Bounds(fupe), (Bounds{-441, -432, 2024, 2033}); got != want {
t.Errorf("Bounds: got %v, want %v", got, want)
}
@ -56,7 +58,7 @@ func TestParse(t *testing.T) {
if got, want := font.VMetric(fupe, i0), (VMetric{2465, 553}); got != want {
t.Errorf("VMetric: got %v, want %v", got, want)
}
if got, want := font.Kerning(fupe, i0, i1), int32(-144); got != want {
if got, want := font.Kerning(fupe, i0, i1), fixed.Int26_6(-144); got != want {
t.Errorf("Kerning: got %v, want %v", got, want)
}
@ -196,7 +198,7 @@ func TestIndex(t *testing.T) {
}
type scalingTestData struct {
advanceWidth int32
advanceWidth fixed.Int26_6
bounds Bounds
points []Point
}
@ -205,13 +207,13 @@ type scalingTestData struct {
// 213 -22 -111 236 555;-22 -111 1, 178 555 1, 236 555 1, 36 -111 1
// The line will not have a trailing "\n".
func scalingTestParse(line string) (ret scalingTestData) {
next := func(s string) (string, int32) {
next := func(s string) (string, fixed.Int26_6) {
t, i := "", strings.Index(s, " ")
if i != -1 {
s, t = s[:i], s[i+1:]
}
x, _ := strconv.Atoi(s)
return t, int32(x)
return t, fixed.Int26_6(x)
}
i := strings.Index(line, ";")
@ -257,7 +259,7 @@ func scalingTestEquals(a, b []Point) (index int, equals bool) {
var scalingTestCases = []struct {
name string
size int32
size int
}{
{"luxisr", 12},
{"x-arial-bold", 11},
@ -318,7 +320,7 @@ func testScaling(t *testing.T, h Hinting) {
glyphBuf := NewGlyphBuf()
for i, want := range wants {
if err = glyphBuf.Load(font, tc.size*64, Index(i), h); err != nil {
if err = glyphBuf.Load(font, fixed.I(tc.size), Index(i), h); err != nil {
t.Errorf("%s: glyph #%d: Load: %v", tc.name, i, err)
continue
}