From 2a5cbfd47e3e23d75bcbe4e4e0e1d3ae3c20620e Mon Sep 17 00:00:00 2001 From: Nigel Tao Date: Tue, 18 Aug 2015 16:30:37 +1000 Subject: [PATCH] Use the fixed.Int26_6 type in package truetype. --- example/freetype/main.go | 4 +-- example/truetype/main.go | 3 +- freetype.go | 10 +++--- truetype/glyph.go | 76 +++++++++++++++++++++------------------ truetype/hint.go | 46 ++++++++++++------------ truetype/hint_test.go | 4 ++- truetype/truetype.go | 61 +++++++++++++++---------------- truetype/truetype_test.go | 16 +++++---- 8 files changed, 117 insertions(+), 103 deletions(-) diff --git a/example/freetype/main.go b/example/freetype/main.go index e20800e..d5e60f9 100644 --- a/example/freetype/main.go +++ b/example/freetype/main.go @@ -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. diff --git a/example/truetype/main.go b/example/truetype/main.go index bca1755..93ea5d0 100644 --- a/example/truetype/main.go +++ b/example/truetype/main.go @@ -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) diff --git a/freetype.go b/freetype.go index 291935d..221485f 100644 --- a/freetype.go +++ b/freetype.go @@ -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 { diff --git a/truetype/glyph.go b/truetype/glyph.go index b5f3278..7fbc4ef 100644 --- a/truetype/glyph.go +++ b/truetype/glyph.go @@ -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 } } diff --git a/truetype/hint.go b/truetype/hint.go index 26c6314..d967bd9 100644 --- a/truetype/hint.go +++ b/truetype/hint.go @@ -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 { diff --git a/truetype/hint_test.go b/truetype/hint_test.go index c8b8d60..7eb43dd 100644 --- a/truetype/hint_test.go +++ b/truetype/hint_test.go @@ -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}, diff --git a/truetype/truetype.go b/truetype/truetype.go index ce1678d..ebc36e6 100644 --- a/truetype/truetype.go +++ b/truetype/truetype.go @@ -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 diff --git a/truetype/truetype_test.go b/truetype/truetype_test.go index 12cc7e9..568bec6 100644 --- a/truetype/truetype_test.go +++ b/truetype/truetype_test.go @@ -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 }