summaryrefslogtreecommitdiffstats
path: root/Godeps/_workspace/src/code.google.com/p/freetype-go
diff options
context:
space:
mode:
Diffstat (limited to 'Godeps/_workspace/src/code.google.com/p/freetype-go')
-rw-r--r--Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/geom.go280
-rw-r--r--Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/paint.go292
-rw-r--r--Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/raster.go579
-rw-r--r--Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/stroke.go466
-rw-r--r--Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/glyph.go530
-rw-r--r--Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/hint.go1764
-rw-r--r--Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/hint_test.go673
-rw-r--r--Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/opcodes.go289
-rw-r--r--Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/truetype.go554
-rw-r--r--Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/truetype_test.go366
10 files changed, 5793 insertions, 0 deletions
diff --git a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/geom.go b/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/geom.go
new file mode 100644
index 000000000..63c86e6ab
--- /dev/null
+++ b/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/geom.go
@@ -0,0 +1,280 @@
+// Copyright 2010 The Freetype-Go Authors. All rights reserved.
+// Use of this source code is governed by your choice of either the
+// FreeType License or the GNU General Public License version 2 (or
+// any later version), both of which can be found in the LICENSE file.
+
+package raster
+
+import (
+ "fmt"
+ "math"
+)
+
+// A Fix32 is a 24.8 fixed point number.
+type Fix32 int32
+
+// A Fix64 is a 48.16 fixed point number.
+type Fix64 int64
+
+// String returns a human-readable representation of a 24.8 fixed point number.
+// For example, the number one-and-a-quarter becomes "1:064".
+func (x Fix32) String() string {
+ if x < 0 {
+ x = -x
+ return fmt.Sprintf("-%d:%03d", int32(x/256), int32(x%256))
+ }
+ return fmt.Sprintf("%d:%03d", int32(x/256), int32(x%256))
+}
+
+// String returns a human-readable representation of a 48.16 fixed point number.
+// For example, the number one-and-a-quarter becomes "1:16384".
+func (x Fix64) String() string {
+ if x < 0 {
+ x = -x
+ return fmt.Sprintf("-%d:%05d", int64(x/65536), int64(x%65536))
+ }
+ return fmt.Sprintf("%d:%05d", int64(x/65536), int64(x%65536))
+}
+
+// maxAbs returns the maximum of abs(a) and abs(b).
+func maxAbs(a, b Fix32) Fix32 {
+ if a < 0 {
+ a = -a
+ }
+ if b < 0 {
+ b = -b
+ }
+ if a < b {
+ return b
+ }
+ return a
+}
+
+// A Point represents a two-dimensional point or vector, in 24.8 fixed point
+// format.
+type Point struct {
+ X, Y Fix32
+}
+
+// String returns a human-readable representation of a Point.
+func (p Point) String() string {
+ return "(" + p.X.String() + ", " + p.Y.String() + ")"
+}
+
+// Add returns the vector p + q.
+func (p Point) Add(q Point) Point {
+ return Point{p.X + q.X, p.Y + q.Y}
+}
+
+// Sub returns the vector p - q.
+func (p Point) Sub(q Point) Point {
+ return Point{p.X - q.X, p.Y - q.Y}
+}
+
+// Mul returns the vector k * p.
+func (p Point) Mul(k Fix32) Point {
+ return Point{p.X * k / 256, p.Y * k / 256}
+}
+
+// Neg returns the vector -p, or equivalently p rotated by 180 degrees.
+func (p Point) Neg() Point {
+ return Point{-p.X, -p.Y}
+}
+
+// Dot returns the dot product p·q.
+func (p Point) Dot(q Point) Fix64 {
+ px, py := int64(p.X), int64(p.Y)
+ qx, qy := int64(q.X), int64(q.Y)
+ return Fix64(px*qx + py*qy)
+}
+
+// Len returns the length of the vector p.
+func (p Point) Len() Fix32 {
+ // TODO(nigeltao): use fixed point math.
+ x := float64(p.X)
+ y := float64(p.Y)
+ return Fix32(math.Sqrt(x*x + y*y))
+}
+
+// Norm returns the vector p normalized to the given length, or the zero Point
+// if p is degenerate.
+func (p Point) Norm(length Fix32) Point {
+ d := p.Len()
+ if d == 0 {
+ return Point{}
+ }
+ s, t := int64(length), int64(d)
+ x := int64(p.X) * s / t
+ y := int64(p.Y) * s / t
+ return Point{Fix32(x), Fix32(y)}
+}
+
+// Rot45CW returns the vector p rotated clockwise by 45 degrees.
+// Note that the Y-axis grows downwards, so {1, 0}.Rot45CW is {1/√2, 1/√2}.
+func (p Point) Rot45CW() Point {
+ // 181/256 is approximately 1/√2, or sin(π/4).
+ px, py := int64(p.X), int64(p.Y)
+ qx := (+px - py) * 181 / 256
+ qy := (+px + py) * 181 / 256
+ return Point{Fix32(qx), Fix32(qy)}
+}
+
+// Rot90CW returns the vector p rotated clockwise by 90 degrees.
+// Note that the Y-axis grows downwards, so {1, 0}.Rot90CW is {0, 1}.
+func (p Point) Rot90CW() Point {
+ return Point{-p.Y, p.X}
+}
+
+// Rot135CW returns the vector p rotated clockwise by 135 degrees.
+// Note that the Y-axis grows downwards, so {1, 0}.Rot135CW is {-1/√2, 1/√2}.
+func (p Point) Rot135CW() Point {
+ // 181/256 is approximately 1/√2, or sin(π/4).
+ px, py := int64(p.X), int64(p.Y)
+ qx := (-px - py) * 181 / 256
+ qy := (+px - py) * 181 / 256
+ return Point{Fix32(qx), Fix32(qy)}
+}
+
+// Rot45CCW returns the vector p rotated counter-clockwise by 45 degrees.
+// Note that the Y-axis grows downwards, so {1, 0}.Rot45CCW is {1/√2, -1/√2}.
+func (p Point) Rot45CCW() Point {
+ // 181/256 is approximately 1/√2, or sin(π/4).
+ px, py := int64(p.X), int64(p.Y)
+ qx := (+px + py) * 181 / 256
+ qy := (-px + py) * 181 / 256
+ return Point{Fix32(qx), Fix32(qy)}
+}
+
+// Rot90CCW returns the vector p rotated counter-clockwise by 90 degrees.
+// Note that the Y-axis grows downwards, so {1, 0}.Rot90CCW is {0, -1}.
+func (p Point) Rot90CCW() Point {
+ return Point{p.Y, -p.X}
+}
+
+// Rot135CCW returns the vector p rotated counter-clockwise by 135 degrees.
+// Note that the Y-axis grows downwards, so {1, 0}.Rot135CCW is {-1/√2, -1/√2}.
+func (p Point) Rot135CCW() Point {
+ // 181/256 is approximately 1/√2, or sin(π/4).
+ px, py := int64(p.X), int64(p.Y)
+ qx := (-px + py) * 181 / 256
+ qy := (-px - py) * 181 / 256
+ return Point{Fix32(qx), Fix32(qy)}
+}
+
+// An Adder accumulates points on a curve.
+type Adder interface {
+ // Start starts a new curve at the given point.
+ Start(a Point)
+ // Add1 adds a linear segment to the current curve.
+ Add1(b Point)
+ // Add2 adds a quadratic segment to the current curve.
+ Add2(b, c Point)
+ // Add3 adds a cubic segment to the current curve.
+ Add3(b, c, d Point)
+}
+
+// A Path is a sequence of curves, and a curve is a start point followed by a
+// sequence of linear, quadratic or cubic segments.
+type Path []Fix32
+
+// String returns a human-readable representation of a Path.
+func (p Path) String() string {
+ s := ""
+ for i := 0; i < len(p); {
+ if i != 0 {
+ s += " "
+ }
+ switch p[i] {
+ case 0:
+ s += "S0" + fmt.Sprint([]Fix32(p[i+1:i+3]))
+ i += 4
+ case 1:
+ s += "A1" + fmt.Sprint([]Fix32(p[i+1:i+3]))
+ i += 4
+ case 2:
+ s += "A2" + fmt.Sprint([]Fix32(p[i+1:i+5]))
+ i += 6
+ case 3:
+ s += "A3" + fmt.Sprint([]Fix32(p[i+1:i+7]))
+ i += 8
+ default:
+ panic("freetype/raster: bad path")
+ }
+ }
+ return s
+}
+
+// Clear cancels any previous calls to p.Start or p.AddXxx.
+func (p *Path) Clear() {
+ *p = (*p)[:0]
+}
+
+// Start starts a new curve at the given point.
+func (p *Path) Start(a Point) {
+ *p = append(*p, 0, a.X, a.Y, 0)
+}
+
+// Add1 adds a linear segment to the current curve.
+func (p *Path) Add1(b Point) {
+ *p = append(*p, 1, b.X, b.Y, 1)
+}
+
+// Add2 adds a quadratic segment to the current curve.
+func (p *Path) Add2(b, c Point) {
+ *p = append(*p, 2, b.X, b.Y, c.X, c.Y, 2)
+}
+
+// Add3 adds a cubic segment to the current curve.
+func (p *Path) Add3(b, c, d Point) {
+ *p = append(*p, 3, b.X, b.Y, c.X, c.Y, d.X, d.Y, 3)
+}
+
+// AddPath adds the Path q to p.
+func (p *Path) AddPath(q Path) {
+ *p = append(*p, q...)
+}
+
+// AddStroke adds a stroked Path.
+func (p *Path) AddStroke(q Path, width Fix32, cr Capper, jr Joiner) {
+ Stroke(p, q, width, cr, jr)
+}
+
+// firstPoint returns the first point in a non-empty Path.
+func (p Path) firstPoint() Point {
+ return Point{p[1], p[2]}
+}
+
+// lastPoint returns the last point in a non-empty Path.
+func (p Path) lastPoint() Point {
+ return Point{p[len(p)-3], p[len(p)-2]}
+}
+
+// addPathReversed adds q reversed to p.
+// For example, if q consists of a linear segment from A to B followed by a
+// quadratic segment from B to C to D, then the values of q looks like:
+// index: 01234567890123
+// value: 0AA01BB12CCDD2
+// So, when adding q backwards to p, we want to Add2(C, B) followed by Add1(A).
+func addPathReversed(p Adder, q Path) {
+ if len(q) == 0 {
+ return
+ }
+ i := len(q) - 1
+ for {
+ switch q[i] {
+ case 0:
+ return
+ case 1:
+ i -= 4
+ p.Add1(Point{q[i-2], q[i-1]})
+ case 2:
+ i -= 6
+ p.Add2(Point{q[i+2], q[i+3]}, Point{q[i-2], q[i-1]})
+ case 3:
+ i -= 8
+ p.Add3(Point{q[i+4], q[i+5]}, Point{q[i+2], q[i+3]}, Point{q[i-2], q[i-1]})
+ default:
+ panic("freetype/raster: bad path")
+ }
+ }
+}
diff --git a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/paint.go b/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/paint.go
new file mode 100644
index 000000000..13cccc192
--- /dev/null
+++ b/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/paint.go
@@ -0,0 +1,292 @@
+// Copyright 2010 The Freetype-Go Authors. All rights reserved.
+// Use of this source code is governed by your choice of either the
+// FreeType License or the GNU General Public License version 2 (or
+// any later version), both of which can be found in the LICENSE file.
+
+package raster
+
+import (
+ "image"
+ "image/color"
+ "image/draw"
+ "math"
+)
+
+// A Span is a horizontal segment of pixels with constant alpha. X0 is an
+// inclusive bound and X1 is exclusive, the same as for slices. A fully
+// opaque Span has A == 1<<32 - 1.
+type Span struct {
+ Y, X0, X1 int
+ A uint32
+}
+
+// A Painter knows how to paint a batch of Spans. Rasterization may involve
+// Painting multiple batches, and done will be true for the final batch.
+// The Spans' Y values are monotonically increasing during a rasterization.
+// Paint may use all of ss as scratch space during the call.
+type Painter interface {
+ Paint(ss []Span, done bool)
+}
+
+// The PainterFunc type adapts an ordinary function to the Painter interface.
+type PainterFunc func(ss []Span, done bool)
+
+// Paint just delegates the call to f.
+func (f PainterFunc) Paint(ss []Span, done bool) { f(ss, done) }
+
+// An AlphaOverPainter is a Painter that paints Spans onto an image.Alpha
+// using the Over Porter-Duff composition operator.
+type AlphaOverPainter struct {
+ Image *image.Alpha
+}
+
+// Paint satisfies the Painter interface by painting ss onto an image.Alpha.
+func (r AlphaOverPainter) Paint(ss []Span, done bool) {
+ b := r.Image.Bounds()
+ for _, s := range ss {
+ if s.Y < b.Min.Y {
+ continue
+ }
+ if s.Y >= b.Max.Y {
+ return
+ }
+ if s.X0 < b.Min.X {
+ s.X0 = b.Min.X
+ }
+ if s.X1 > b.Max.X {
+ s.X1 = b.Max.X
+ }
+ if s.X0 >= s.X1 {
+ continue
+ }
+ base := (s.Y-r.Image.Rect.Min.Y)*r.Image.Stride - r.Image.Rect.Min.X
+ p := r.Image.Pix[base+s.X0 : base+s.X1]
+ a := int(s.A >> 24)
+ for i, c := range p {
+ v := int(c)
+ p[i] = uint8((v*255 + (255-v)*a) / 255)
+ }
+ }
+}
+
+// NewAlphaOverPainter creates a new AlphaOverPainter for the given image.
+func NewAlphaOverPainter(m *image.Alpha) AlphaOverPainter {
+ return AlphaOverPainter{m}
+}
+
+// An AlphaSrcPainter is a Painter that paints Spans onto an image.Alpha
+// using the Src Porter-Duff composition operator.
+type AlphaSrcPainter struct {
+ Image *image.Alpha
+}
+
+// Paint satisfies the Painter interface by painting ss onto an image.Alpha.
+func (r AlphaSrcPainter) Paint(ss []Span, done bool) {
+ b := r.Image.Bounds()
+ for _, s := range ss {
+ if s.Y < b.Min.Y {
+ continue
+ }
+ if s.Y >= b.Max.Y {
+ return
+ }
+ if s.X0 < b.Min.X {
+ s.X0 = b.Min.X
+ }
+ if s.X1 > b.Max.X {
+ s.X1 = b.Max.X
+ }
+ if s.X0 >= s.X1 {
+ continue
+ }
+ base := (s.Y-r.Image.Rect.Min.Y)*r.Image.Stride - r.Image.Rect.Min.X
+ p := r.Image.Pix[base+s.X0 : base+s.X1]
+ color := uint8(s.A >> 24)
+ for i := range p {
+ p[i] = color
+ }
+ }
+}
+
+// NewAlphaSrcPainter creates a new AlphaSrcPainter for the given image.
+func NewAlphaSrcPainter(m *image.Alpha) AlphaSrcPainter {
+ return AlphaSrcPainter{m}
+}
+
+type RGBAPainter struct {
+ // The image to compose onto.
+ Image *image.RGBA
+ // The Porter-Duff composition operator.
+ Op draw.Op
+ // The 16-bit color to paint the spans.
+ cr, cg, cb, ca uint32
+}
+
+// Paint satisfies the Painter interface by painting ss onto an image.RGBA.
+func (r *RGBAPainter) Paint(ss []Span, done bool) {
+ b := r.Image.Bounds()
+ for _, s := range ss {
+ if s.Y < b.Min.Y {
+ continue
+ }
+ if s.Y >= b.Max.Y {
+ return
+ }
+ if s.X0 < b.Min.X {
+ s.X0 = b.Min.X
+ }
+ if s.X1 > b.Max.X {
+ s.X1 = b.Max.X
+ }
+ if s.X0 >= s.X1 {
+ continue
+ }
+ // This code is similar to drawGlyphOver in $GOROOT/src/pkg/image/draw/draw.go.
+ ma := s.A >> 16
+ const m = 1<<16 - 1
+ i0 := (s.Y-r.Image.Rect.Min.Y)*r.Image.Stride + (s.X0-r.Image.Rect.Min.X)*4
+ i1 := i0 + (s.X1-s.X0)*4
+ if r.Op == draw.Over {
+ for i := i0; i < i1; i += 4 {
+ dr := uint32(r.Image.Pix[i+0])
+ dg := uint32(r.Image.Pix[i+1])
+ db := uint32(r.Image.Pix[i+2])
+ da := uint32(r.Image.Pix[i+3])
+ a := (m - (r.ca * ma / m)) * 0x101
+ r.Image.Pix[i+0] = uint8((dr*a + r.cr*ma) / m >> 8)
+ r.Image.Pix[i+1] = uint8((dg*a + r.cg*ma) / m >> 8)
+ r.Image.Pix[i+2] = uint8((db*a + r.cb*ma) / m >> 8)
+ r.Image.Pix[i+3] = uint8((da*a + r.ca*ma) / m >> 8)
+ }
+ } else {
+ for i := i0; i < i1; i += 4 {
+ r.Image.Pix[i+0] = uint8(r.cr * ma / m >> 8)
+ r.Image.Pix[i+1] = uint8(r.cg * ma / m >> 8)
+ r.Image.Pix[i+2] = uint8(r.cb * ma / m >> 8)
+ r.Image.Pix[i+3] = uint8(r.ca * ma / m >> 8)
+ }
+ }
+ }
+}
+
+// SetColor sets the color to paint the spans.
+func (r *RGBAPainter) SetColor(c color.Color) {
+ r.cr, r.cg, r.cb, r.ca = c.RGBA()
+}
+
+// NewRGBAPainter creates a new RGBAPainter for the given image.
+func NewRGBAPainter(m *image.RGBA) *RGBAPainter {
+ return &RGBAPainter{Image: m}
+}
+
+// A MonochromePainter wraps another Painter, quantizing each Span's alpha to
+// be either fully opaque or fully transparent.
+type MonochromePainter struct {
+ Painter Painter
+ y, x0, x1 int
+}
+
+// Paint delegates to the wrapped Painter after quantizing each Span's alpha
+// value and merging adjacent fully opaque Spans.
+func (m *MonochromePainter) Paint(ss []Span, done bool) {
+ // We compact the ss slice, discarding any Spans whose alpha quantizes to zero.
+ j := 0
+ for _, s := range ss {
+ if s.A >= 1<<31 {
+ if m.y == s.Y && m.x1 == s.X0 {
+ m.x1 = s.X1
+ } else {
+ ss[j] = Span{m.y, m.x0, m.x1, 1<<32 - 1}
+ j++
+ m.y, m.x0, m.x1 = s.Y, s.X0, s.X1
+ }
+ }
+ }
+ if done {
+ // Flush the accumulated Span.
+ finalSpan := Span{m.y, m.x0, m.x1, 1<<32 - 1}
+ if j < len(ss) {
+ ss[j] = finalSpan
+ j++
+ m.Painter.Paint(ss[:j], true)
+ } else if j == len(ss) {
+ m.Painter.Paint(ss, false)
+ if cap(ss) > 0 {
+ ss = ss[:1]
+ } else {
+ ss = make([]Span, 1)
+ }
+ ss[0] = finalSpan
+ m.Painter.Paint(ss, true)
+ } else {
+ panic("unreachable")
+ }
+ // Reset the accumulator, so that this Painter can be re-used.
+ m.y, m.x0, m.x1 = 0, 0, 0
+ } else {
+ m.Painter.Paint(ss[:j], false)
+ }
+}
+
+// NewMonochromePainter creates a new MonochromePainter that wraps the given
+// Painter.
+func NewMonochromePainter(p Painter) *MonochromePainter {
+ return &MonochromePainter{Painter: p}
+}
+
+// A GammaCorrectionPainter wraps another Painter, performing gamma-correction
+// on each Span's alpha value.
+type GammaCorrectionPainter struct {
+ // The wrapped Painter.
+ Painter Painter
+ // Precomputed alpha values for linear interpolation, with fully opaque == 1<<16-1.
+ a [256]uint16
+ // Whether gamma correction is a no-op.
+ gammaIsOne bool
+}
+
+// Paint delegates to the wrapped Painter after performing gamma-correction
+// on each Span.
+func (g *GammaCorrectionPainter) Paint(ss []Span, done bool) {
+ if !g.gammaIsOne {
+ const (
+ M = 0x1010101 // 255*M == 1<<32-1
+ N = 0x8080 // N = M>>9, and N < 1<<16-1
+ )
+ for i, s := range ss {
+ if s.A == 0 || s.A == 1<<32-1 {
+ continue
+ }
+ p, q := s.A/M, (s.A%M)>>9
+ // The resultant alpha is a linear interpolation of g.a[p] and g.a[p+1].
+ a := uint32(g.a[p])*(N-q) + uint32(g.a[p+1])*q
+ a = (a + N/2) / N
+ // Convert the alpha from 16-bit (which is g.a's range) to 32-bit.
+ a |= a << 16
+ ss[i].A = a
+ }
+ }
+ g.Painter.Paint(ss, done)
+}
+
+// SetGamma sets the gamma value.
+func (g *GammaCorrectionPainter) SetGamma(gamma float64) {
+ if gamma == 1.0 {
+ g.gammaIsOne = true
+ return
+ }
+ g.gammaIsOne = false
+ for i := 0; i < 256; i++ {
+ a := float64(i) / 0xff
+ a = math.Pow(a, gamma)
+ g.a[i] = uint16(0xffff * a)
+ }
+}
+
+// NewGammaCorrectionPainter creates a new GammaCorrectionPainter that wraps
+// the given Painter.
+func NewGammaCorrectionPainter(p Painter, gamma float64) *GammaCorrectionPainter {
+ g := &GammaCorrectionPainter{Painter: p}
+ g.SetGamma(gamma)
+ return g
+}
diff --git a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/raster.go b/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/raster.go
new file mode 100644
index 000000000..45af7eaa2
--- /dev/null
+++ b/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/raster.go
@@ -0,0 +1,579 @@
+// Copyright 2010 The Freetype-Go Authors. All rights reserved.
+// Use of this source code is governed by your choice of either the
+// FreeType License or the GNU General Public License version 2 (or
+// any later version), both of which can be found in the LICENSE file.
+
+// The raster package provides an anti-aliasing 2-D rasterizer.
+//
+// It is part of the larger Freetype-Go suite of font-related packages,
+// but the raster package is not specific to font rasterization, and can
+// be used standalone without any other Freetype-Go package.
+//
+// Rasterization is done by the same area/coverage accumulation algorithm
+// as the Freetype "smooth" module, and the Anti-Grain Geometry library.
+// A description of the area/coverage algorithm is at
+// http://projects.tuxee.net/cl-vectors/section-the-cl-aa-algorithm
+package raster
+
+import (
+ "strconv"
+)
+
+// A cell is part of a linked list (for a given yi co-ordinate) of accumulated
+// area/coverage for the pixel at (xi, yi).
+type cell struct {
+ xi int
+ area, cover int
+ next int
+}
+
+type Rasterizer struct {
+ // If false, the default behavior is to use the even-odd winding fill
+ // rule during Rasterize.
+ UseNonZeroWinding bool
+ // An offset (in pixels) to the painted spans.
+ Dx, Dy int
+
+ // The width of the Rasterizer. The height is implicit in len(cellIndex).
+ width int
+ // splitScaleN is the scaling factor used to determine how many times
+ // to decompose a quadratic or cubic segment into a linear approximation.
+ splitScale2, splitScale3 int
+
+ // The current pen position.
+ a Point
+ // The current cell and its area/coverage being accumulated.
+ xi, yi int
+ area, cover int
+
+ // Saved cells.
+ cell []cell
+ // Linked list of cells, one per row.
+ cellIndex []int
+ // Buffers.
+ cellBuf [256]cell
+ cellIndexBuf [64]int
+ spanBuf [64]Span
+}
+
+// findCell returns the index in r.cell for the cell corresponding to
+// (r.xi, r.yi). The cell is created if necessary.
+func (r *Rasterizer) findCell() int {
+ if r.yi < 0 || r.yi >= len(r.cellIndex) {
+ return -1
+ }
+ xi := r.xi
+ if xi < 0 {
+ xi = -1
+ } else if xi > r.width {
+ xi = r.width
+ }
+ i, prev := r.cellIndex[r.yi], -1
+ for i != -1 && r.cell[i].xi <= xi {
+ if r.cell[i].xi == xi {
+ return i
+ }
+ i, prev = r.cell[i].next, i
+ }
+ c := len(r.cell)
+ if c == cap(r.cell) {
+ buf := make([]cell, c, 4*c)
+ copy(buf, r.cell)
+ r.cell = buf[0 : c+1]
+ } else {
+ r.cell = r.cell[0 : c+1]
+ }
+ r.cell[c] = cell{xi, 0, 0, i}
+ if prev == -1 {
+ r.cellIndex[r.yi] = c
+ } else {
+ r.cell[prev].next = c
+ }
+ return c
+}
+
+// saveCell saves any accumulated r.area/r.cover for (r.xi, r.yi).
+func (r *Rasterizer) saveCell() {
+ if r.area != 0 || r.cover != 0 {
+ i := r.findCell()
+ if i != -1 {
+ r.cell[i].area += r.area
+ r.cell[i].cover += r.cover
+ }
+ r.area = 0
+ r.cover = 0
+ }
+}
+
+// setCell sets the (xi, yi) cell that r is accumulating area/coverage for.
+func (r *Rasterizer) setCell(xi, yi int) {
+ if r.xi != xi || r.yi != yi {
+ r.saveCell()
+ r.xi, r.yi = xi, yi
+ }
+}
+
+// scan accumulates area/coverage for the yi'th scanline, going from
+// x0 to x1 in the horizontal direction (in 24.8 fixed point co-ordinates)
+// and from y0f to y1f fractional vertical units within that scanline.
+func (r *Rasterizer) scan(yi int, x0, y0f, x1, y1f Fix32) {
+ // Break the 24.8 fixed point X co-ordinates into integral and fractional parts.
+ x0i := int(x0) / 256
+ x0f := x0 - Fix32(256*x0i)
+ x1i := int(x1) / 256
+ x1f := x1 - Fix32(256*x1i)
+
+ // A perfectly horizontal scan.
+ if y0f == y1f {
+ r.setCell(x1i, yi)
+ return
+ }
+ dx, dy := x1-x0, y1f-y0f
+ // A single cell scan.
+ if x0i == x1i {
+ r.area += int((x0f + x1f) * dy)
+ r.cover += int(dy)
+ return
+ }
+ // There are at least two cells. Apart from the first and last cells,
+ // all intermediate cells go through the full width of the cell,
+ // or 256 units in 24.8 fixed point format.
+ var (
+ p, q, edge0, edge1 Fix32
+ xiDelta int
+ )
+ if dx > 0 {
+ p, q = (256-x0f)*dy, dx
+ edge0, edge1, xiDelta = 0, 256, 1
+ } else {
+ p, q = x0f*dy, -dx
+ edge0, edge1, xiDelta = 256, 0, -1
+ }
+ yDelta, yRem := p/q, p%q
+ if yRem < 0 {
+ yDelta -= 1
+ yRem += q
+ }
+ // Do the first cell.
+ xi, y := x0i, y0f
+ r.area += int((x0f + edge1) * yDelta)
+ r.cover += int(yDelta)
+ xi, y = xi+xiDelta, y+yDelta
+ r.setCell(xi, yi)
+ if xi != x1i {
+ // Do all the intermediate cells.
+ p = 256 * (y1f - y + yDelta)
+ fullDelta, fullRem := p/q, p%q
+ if fullRem < 0 {
+ fullDelta -= 1
+ fullRem += q
+ }
+ yRem -= q
+ for xi != x1i {
+ yDelta = fullDelta
+ yRem += fullRem
+ if yRem >= 0 {
+ yDelta += 1
+ yRem -= q
+ }
+ r.area += int(256 * yDelta)
+ r.cover += int(yDelta)
+ xi, y = xi+xiDelta, y+yDelta
+ r.setCell(xi, yi)
+ }
+ }
+ // Do the last cell.
+ yDelta = y1f - y
+ r.area += int((edge0 + x1f) * yDelta)
+ r.cover += int(yDelta)
+}
+
+// Start starts a new curve at the given point.
+func (r *Rasterizer) Start(a Point) {
+ r.setCell(int(a.X/256), int(a.Y/256))
+ r.a = a
+}
+
+// Add1 adds a linear segment to the current curve.
+func (r *Rasterizer) Add1(b Point) {
+ x0, y0 := r.a.X, r.a.Y
+ x1, y1 := b.X, b.Y
+ dx, dy := x1-x0, y1-y0
+ // Break the 24.8 fixed point Y co-ordinates into integral and fractional parts.
+ y0i := int(y0) / 256
+ y0f := y0 - Fix32(256*y0i)
+ y1i := int(y1) / 256
+ y1f := y1 - Fix32(256*y1i)
+
+ if y0i == y1i {
+ // There is only one scanline.
+ r.scan(y0i, x0, y0f, x1, y1f)
+
+ } else if dx == 0 {
+ // This is a vertical line segment. We avoid calling r.scan and instead
+ // manipulate r.area and r.cover directly.
+ var (
+ edge0, edge1 Fix32
+ yiDelta int
+ )
+ if dy > 0 {
+ edge0, edge1, yiDelta = 0, 256, 1
+ } else {
+ edge0, edge1, yiDelta = 256, 0, -1
+ }
+ x0i, yi := int(x0)/256, y0i
+ x0fTimes2 := (int(x0) - (256 * x0i)) * 2
+ // Do the first pixel.
+ dcover := int(edge1 - y0f)
+ darea := int(x0fTimes2 * dcover)
+ r.area += darea
+ r.cover += dcover
+ yi += yiDelta
+ r.setCell(x0i, yi)
+ // Do all the intermediate pixels.
+ dcover = int(edge1 - edge0)
+ darea = int(x0fTimes2 * dcover)
+ for yi != y1i {
+ r.area += darea
+ r.cover += dcover
+ yi += yiDelta
+ r.setCell(x0i, yi)
+ }
+ // Do the last pixel.
+ dcover = int(y1f - edge0)
+ darea = int(x0fTimes2 * dcover)
+ r.area += darea
+ r.cover += dcover
+
+ } else {
+ // There are at least two scanlines. Apart from the first and last scanlines,
+ // all intermediate scanlines go through the full height of the row, or 256
+ // units in 24.8 fixed point format.
+ var (
+ p, q, edge0, edge1 Fix32
+ yiDelta int
+ )
+ if dy > 0 {
+ p, q = (256-y0f)*dx, dy
+ edge0, edge1, yiDelta = 0, 256, 1
+ } else {
+ p, q = y0f*dx, -dy
+ edge0, edge1, yiDelta = 256, 0, -1
+ }
+ xDelta, xRem := p/q, p%q
+ if xRem < 0 {
+ xDelta -= 1
+ xRem += q
+ }
+ // Do the first scanline.
+ x, yi := x0, y0i
+ r.scan(yi, x, y0f, x+xDelta, edge1)
+ x, yi = x+xDelta, yi+yiDelta
+ r.setCell(int(x)/256, yi)
+ if yi != y1i {
+ // Do all the intermediate scanlines.
+ p = 256 * dx
+ fullDelta, fullRem := p/q, p%q
+ if fullRem < 0 {
+ fullDelta -= 1
+ fullRem += q
+ }
+ xRem -= q
+ for yi != y1i {
+ xDelta = fullDelta
+ xRem += fullRem
+ if xRem >= 0 {
+ xDelta += 1
+ xRem -= q
+ }
+ r.scan(yi, x, edge0, x+xDelta, edge1)
+ x, yi = x+xDelta, yi+yiDelta
+ r.setCell(int(x)/256, yi)
+ }
+ }
+ // Do the last scanline.
+ r.scan(yi, x, edge0, x1, y1f)
+ }
+ // The next lineTo starts from b.
+ r.a = b
+}
+
+// Add2 adds a quadratic segment to the current curve.
+func (r *Rasterizer) Add2(b, c Point) {
+ // Calculate nSplit (the number of recursive decompositions) based on how `curvy' it is.
+ // Specifically, how much the middle point b deviates from (a+c)/2.
+ dev := maxAbs(r.a.X-2*b.X+c.X, r.a.Y-2*b.Y+c.Y) / Fix32(r.splitScale2)
+ nsplit := 0
+ for dev > 0 {
+ dev /= 4
+ nsplit++
+ }
+ // dev is 32-bit, and nsplit++ every time we shift off 2 bits, so maxNsplit is 16.
+ const maxNsplit = 16
+ if nsplit > maxNsplit {
+ panic("freetype/raster: Add2 nsplit too large: " + strconv.Itoa(nsplit))
+ }
+ // Recursively decompose the curve nSplit levels deep.
+ var (
+ pStack [2*maxNsplit + 3]Point
+ sStack [maxNsplit + 1]int
+ i int
+ )
+ sStack[0] = nsplit
+ pStack[0] = c
+ pStack[1] = b
+ pStack[2] = r.a
+ for i >= 0 {
+ s := sStack[i]
+ p := pStack[2*i:]
+ if s > 0 {
+ // Split the quadratic curve p[:3] into an equivalent set of two shorter curves:
+ // p[:3] and p[2:5]. The new p[4] is the old p[2], and p[0] is unchanged.
+ mx := p[1].X
+ p[4].X = p[2].X
+ p[3].X = (p[4].X + mx) / 2
+ p[1].X = (p[0].X + mx) / 2
+ p[2].X = (p[1].X + p[3].X) / 2
+ my := p[1].Y
+ p[4].Y = p[2].Y
+ p[3].Y = (p[4].Y + my) / 2
+ p[1].Y = (p[0].Y + my) / 2
+ p[2].Y = (p[1].Y + p[3].Y) / 2
+ // The two shorter curves have one less split to do.
+ sStack[i] = s - 1
+ sStack[i+1] = s - 1
+ i++
+ } else {
+ // Replace the level-0 quadratic with a two-linear-piece approximation.
+ midx := (p[0].X + 2*p[1].X + p[2].X) / 4
+ midy := (p[0].Y + 2*p[1].Y + p[2].Y) / 4
+ r.Add1(Point{midx, midy})
+ r.Add1(p[0])
+ i--
+ }
+ }
+}
+
+// Add3 adds a cubic segment to the current curve.
+func (r *Rasterizer) Add3(b, c, d Point) {
+ // Calculate nSplit (the number of recursive decompositions) based on how `curvy' it is.
+ dev2 := maxAbs(r.a.X-3*(b.X+c.X)+d.X, r.a.Y-3*(b.Y+c.Y)+d.Y) / Fix32(r.splitScale2)
+ dev3 := maxAbs(r.a.X-2*b.X+d.X, r.a.Y-2*b.Y+d.Y) / Fix32(r.splitScale3)
+ nsplit := 0
+ for dev2 > 0 || dev3 > 0 {
+ dev2 /= 8
+ dev3 /= 4
+ nsplit++
+ }
+ // devN is 32-bit, and nsplit++ every time we shift off 2 bits, so maxNsplit is 16.
+ const maxNsplit = 16
+ if nsplit > maxNsplit {
+ panic("freetype/raster: Add3 nsplit too large: " + strconv.Itoa(nsplit))
+ }
+ // Recursively decompose the curve nSplit levels deep.
+ var (
+ pStack [3*maxNsplit + 4]Point
+ sStack [maxNsplit + 1]int
+ i int
+ )
+ sStack[0] = nsplit
+ pStack[0] = d
+ pStack[1] = c
+ pStack[2] = b
+ pStack[3] = r.a
+ for i >= 0 {
+ s := sStack[i]
+ p := pStack[3*i:]
+ if s > 0 {
+ // Split the cubic curve p[:4] into an equivalent set of two shorter curves:
+ // p[:4] and p[3:7]. The new p[6] is the old p[3], and p[0] is unchanged.
+ m01x := (p[0].X + p[1].X) / 2
+ m12x := (p[1].X + p[2].X) / 2
+ m23x := (p[2].X + p[3].X) / 2
+ p[6].X = p[3].X
+ p[5].X = m23x
+ p[1].X = m01x
+ p[2].X = (m01x + m12x) / 2
+ p[4].X = (m12x + m23x) / 2
+ p[3].X = (p[2].X + p[4].X) / 2
+ m01y := (p[0].Y + p[1].Y) / 2
+ m12y := (p[1].Y + p[2].Y) / 2
+ m23y := (p[2].Y + p[3].Y) / 2
+ p[6].Y = p[3].Y
+ p[5].Y = m23y
+ p[1].Y = m01y
+ p[2].Y = (m01y + m12y) / 2
+ p[4].Y = (m12y + m23y) / 2
+ p[3].Y = (p[2].Y + p[4].Y) / 2
+ // The two shorter curves have one less split to do.
+ sStack[i] = s - 1
+ sStack[i+1] = s - 1
+ i++
+ } else {
+ // Replace the level-0 cubic with a two-linear-piece approximation.
+ midx := (p[0].X + 3*(p[1].X+p[2].X) + p[3].X) / 8
+ midy := (p[0].Y + 3*(p[1].Y+p[2].Y) + p[3].Y) / 8
+ r.Add1(Point{midx, midy})
+ r.Add1(p[0])
+ i--
+ }
+ }
+}
+
+// AddPath adds the given Path.
+func (r *Rasterizer) AddPath(p Path) {
+ for i := 0; i < len(p); {
+ switch p[i] {
+ case 0:
+ r.Start(Point{p[i+1], p[i+2]})
+ i += 4
+ case 1:
+ r.Add1(Point{p[i+1], p[i+2]})
+ i += 4
+ case 2:
+ r.Add2(Point{p[i+1], p[i+2]}, Point{p[i+3], p[i+4]})
+ i += 6
+ case 3:
+ r.Add3(Point{p[i+1], p[i+2]}, Point{p[i+3], p[i+4]}, Point{p[i+5], p[i+6]})
+ i += 8
+ default:
+ panic("freetype/raster: bad path")
+ }
+ }
+}
+
+// AddStroke adds a stroked Path.
+func (r *Rasterizer) AddStroke(q Path, width Fix32, cr Capper, jr Joiner) {
+ Stroke(r, q, width, cr, jr)
+}
+
+// Converts an area value to a uint32 alpha value. A completely filled pixel
+// corresponds to an area of 256*256*2, and an alpha of 1<<32-1. The
+// conversion of area values greater than this depends on the winding rule:
+// even-odd or non-zero.
+func (r *Rasterizer) areaToAlpha(area int) uint32 {
+ // The C Freetype implementation (version 2.3.12) does "alpha := area>>1" without
+ // the +1. Round-to-nearest gives a more symmetric result than round-down.
+ // The C implementation also returns 8-bit alpha, not 32-bit alpha.
+ a := (area + 1) >> 1
+ if a < 0 {
+ a = -a
+ }
+ alpha := uint32(a)
+ if r.UseNonZeroWinding {
+ if alpha > 0xffff {
+ alpha = 0xffff
+ }
+ } else {
+ alpha &= 0x1ffff
+ if alpha > 0x10000 {
+ alpha = 0x20000 - alpha
+ } else if alpha == 0x10000 {
+ alpha = 0x0ffff
+ }
+ }
+ alpha |= alpha << 16
+ return alpha
+}
+
+// Rasterize converts r's accumulated curves into Spans for p. The Spans
+// passed to p are non-overlapping, and sorted by Y and then X. They all
+// have non-zero width (and 0 <= X0 < X1 <= r.width) and non-zero A, except
+// for the final Span, which has Y, X0, X1 and A all equal to zero.
+func (r *Rasterizer) Rasterize(p Painter) {
+ r.saveCell()
+ s := 0
+ for yi := 0; yi < len(r.cellIndex); yi++ {
+ xi, cover := 0, 0
+ for c := r.cellIndex[yi]; c != -1; c = r.cell[c].next {
+ if cover != 0 && r.cell[c].xi > xi {
+ alpha := r.areaToAlpha(cover * 256 * 2)
+ if alpha != 0 {
+ xi0, xi1 := xi, r.cell[c].xi
+ if xi0 < 0 {
+ xi0 = 0
+ }
+ if xi1 >= r.width {
+ xi1 = r.width
+ }
+ if xi0 < xi1 {
+ r.spanBuf[s] = Span{yi + r.Dy, xi0 + r.Dx, xi1 + r.Dx, alpha}
+ s++
+ }
+ }
+ }
+ cover += r.cell[c].cover
+ alpha := r.areaToAlpha(cover*256*2 - r.cell[c].area)
+ xi = r.cell[c].xi + 1
+ if alpha != 0 {
+ xi0, xi1 := r.cell[c].xi, xi
+ if xi0 < 0 {
+ xi0 = 0
+ }
+ if xi1 >= r.width {
+ xi1 = r.width
+ }
+ if xi0 < xi1 {
+ r.spanBuf[s] = Span{yi + r.Dy, xi0 + r.Dx, xi1 + r.Dx, alpha}
+ s++
+ }
+ }
+ if s > len(r.spanBuf)-2 {
+ p.Paint(r.spanBuf[:s], false)
+ s = 0
+ }
+ }
+ }
+ p.Paint(r.spanBuf[:s], true)
+}
+
+// Clear cancels any previous calls to r.Start or r.AddXxx.
+func (r *Rasterizer) Clear() {
+ r.a = Point{}
+ r.xi = 0
+ r.yi = 0
+ r.area = 0
+ r.cover = 0
+ r.cell = r.cell[:0]
+ for i := 0; i < len(r.cellIndex); i++ {
+ r.cellIndex[i] = -1
+ }
+}
+
+// SetBounds sets the maximum width and height of the rasterized image and
+// calls Clear. The width and height are in pixels, not Fix32 units.
+func (r *Rasterizer) SetBounds(width, height int) {
+ if width < 0 {
+ width = 0
+ }
+ if height < 0 {
+ height = 0
+ }
+ // Use the same ssN heuristic as the C Freetype implementation.
+ // The C implementation uses the values 32, 16, but those are in
+ // 26.6 fixed point units, and we use 24.8 fixed point everywhere.
+ ss2, ss3 := 128, 64
+ if width > 24 || height > 24 {
+ ss2, ss3 = 2*ss2, 2*ss3
+ if width > 120 || height > 120 {
+ ss2, ss3 = 2*ss2, 2*ss3
+ }
+ }
+ r.width = width
+ r.splitScale2 = ss2
+ r.splitScale3 = ss3
+ r.cell = r.cellBuf[:0]
+ if height > len(r.cellIndexBuf) {
+ r.cellIndex = make([]int, height)
+ } else {
+ r.cellIndex = r.cellIndexBuf[:height]
+ }
+ r.Clear()
+}
+
+// NewRasterizer creates a new Rasterizer with the given bounds.
+func NewRasterizer(width, height int) *Rasterizer {
+ r := new(Rasterizer)
+ r.SetBounds(width, height)
+ return r
+}
diff --git a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/stroke.go b/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/stroke.go
new file mode 100644
index 000000000..d49b1cee9
--- /dev/null
+++ b/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/raster/stroke.go
@@ -0,0 +1,466 @@
+// Copyright 2010 The Freetype-Go Authors. All rights reserved.
+// Use of this source code is governed by your choice of either the
+// FreeType License or the GNU General Public License version 2 (or
+// any later version), both of which can be found in the LICENSE file.
+
+package raster
+
+// Two points are considered practically equal if the square of the distance
+// between them is less than one quarter (i.e. 16384 / 65536 in Fix64).
+const epsilon = 16384
+
+// A Capper signifies how to begin or end a stroked path.
+type Capper interface {
+ // Cap adds a cap to p given a pivot point and the normal vector of a
+ // terminal segment. The normal's length is half of the stroke width.
+ Cap(p Adder, halfWidth Fix32, pivot, n1 Point)
+}
+
+// The CapperFunc type adapts an ordinary function to be a Capper.
+type CapperFunc func(Adder, Fix32, Point, Point)
+
+func (f CapperFunc) Cap(p Adder, halfWidth Fix32, pivot, n1 Point) {
+ f(p, halfWidth, pivot, n1)
+}
+
+// A Joiner signifies how to join interior nodes of a stroked path.
+type Joiner interface {
+ // Join adds a join to the two sides of a stroked path given a pivot
+ // point and the normal vectors of the trailing and leading segments.
+ // Both normals have length equal to half of the stroke width.
+ Join(lhs, rhs Adder, halfWidth Fix32, pivot, n0, n1 Point)
+}
+
+// The JoinerFunc type adapts an ordinary function to be a Joiner.
+type JoinerFunc func(lhs, rhs Adder, halfWidth Fix32, pivot, n0, n1 Point)
+
+func (f JoinerFunc) Join(lhs, rhs Adder, halfWidth Fix32, pivot, n0, n1 Point) {
+ f(lhs, rhs, halfWidth, pivot, n0, n1)
+}
+
+// RoundCapper adds round caps to a stroked path.
+var RoundCapper Capper = CapperFunc(roundCapper)
+
+func roundCapper(p Adder, halfWidth Fix32, pivot, n1 Point) {
+ // The cubic Bézier approximation to a circle involves the magic number
+ // (√2 - 1) * 4/3, which is approximately 141/256.
+ const k = 141
+ e := n1.Rot90CCW()
+ side := pivot.Add(e)
+ start, end := pivot.Sub(n1), pivot.Add(n1)
+ d, e := n1.Mul(k), e.Mul(k)
+ p.Add3(start.Add(e), side.Sub(d), side)
+ p.Add3(side.Add(d), end.Add(e), end)
+}
+
+// ButtCapper adds butt caps to a stroked path.
+var ButtCapper Capper = CapperFunc(buttCapper)
+
+func buttCapper(p Adder, halfWidth Fix32, pivot, n1 Point) {
+ p.Add1(pivot.Add(n1))
+}
+
+// SquareCapper adds square caps to a stroked path.
+var SquareCapper Capper = CapperFunc(squareCapper)
+
+func squareCapper(p Adder, halfWidth Fix32, pivot, n1 Point) {
+ e := n1.Rot90CCW()
+ side := pivot.Add(e)
+ p.Add1(side.Sub(n1))
+ p.Add1(side.Add(n1))
+ p.Add1(pivot.Add(n1))
+}
+
+// RoundJoiner adds round joins to a stroked path.
+var RoundJoiner Joiner = JoinerFunc(roundJoiner)
+
+func roundJoiner(lhs, rhs Adder, haflWidth Fix32, pivot, n0, n1 Point) {
+ dot := n0.Rot90CW().Dot(n1)
+ if dot >= 0 {
+ addArc(lhs, pivot, n0, n1)
+ rhs.Add1(pivot.Sub(n1))
+ } else {
+ lhs.Add1(pivot.Add(n1))
+ addArc(rhs, pivot, n0.Neg(), n1.Neg())
+ }
+}
+
+// BevelJoiner adds bevel joins to a stroked path.
+var BevelJoiner Joiner = JoinerFunc(bevelJoiner)
+
+func bevelJoiner(lhs, rhs Adder, haflWidth Fix32, pivot, n0, n1 Point) {
+ lhs.Add1(pivot.Add(n1))
+ rhs.Add1(pivot.Sub(n1))
+}
+
+// addArc adds a circular arc from pivot+n0 to pivot+n1 to p. The shorter of
+// the two possible arcs is taken, i.e. the one spanning <= 180 degrees.
+// The two vectors n0 and n1 must be of equal length.
+func addArc(p Adder, pivot, n0, n1 Point) {
+ // r2 is the square of the length of n0.
+ r2 := n0.Dot(n0)
+ if r2 < epsilon {
+ // The arc radius is so small that we collapse to a straight line.
+ p.Add1(pivot.Add(n1))
+ return
+ }
+ // We approximate the arc by 0, 1, 2 or 3 45-degree quadratic segments plus
+ // a final quadratic segment from s to n1. Each 45-degree segment has control
+ // points {1, 0}, {1, tan(π/8)} and {1/√2, 1/√2} suitably scaled, rotated and
+ // translated. tan(π/8) is approximately 106/256.
+ const tpo8 = 106
+ var s Point
+ // We determine which octant the angle between n0 and n1 is in via three dot products.
+ // m0, m1 and m2 are n0 rotated clockwise by 45, 90 and 135 degrees.
+ m0 := n0.Rot45CW()
+ m1 := n0.Rot90CW()
+ m2 := m0.Rot90CW()
+ if m1.Dot(n1) >= 0 {
+ if n0.Dot(n1) >= 0 {
+ if m2.Dot(n1) <= 0 {
+ // n1 is between 0 and 45 degrees clockwise of n0.
+ s = n0
+ } else {
+ // n1 is between 45 and 90 degrees clockwise of n0.
+ p.Add2(pivot.Add(n0).Add(m1.Mul(tpo8)), pivot.Add(m0))
+ s = m0
+ }
+ } else {
+ pm1, n0t := pivot.Add(m1), n0.Mul(tpo8)
+ p.Add2(pivot.Add(n0).Add(m1.Mul(tpo8)), pivot.Add(m0))
+ p.Add2(pm1.Add(n0t), pm1)
+ if m0.Dot(n1) >= 0 {
+ // n1 is between 90 and 135 degrees clockwise of n0.
+ s = m1
+ } else {
+ // n1 is between 135 and 180 degrees clockwise of n0.
+ p.Add2(pm1.Sub(n0t), pivot.Add(m2))
+ s = m2
+ }
+ }
+ } else {
+ if n0.Dot(n1) >= 0 {
+ if m0.Dot(n1) >= 0 {
+ // n1 is between 0 and 45 degrees counter-clockwise of n0.
+ s = n0
+ } else {
+ // n1 is between 45 and 90 degrees counter-clockwise of n0.
+ p.Add2(pivot.Add(n0).Sub(m1.Mul(tpo8)), pivot.Sub(m2))
+ s = m2.Neg()
+ }
+ } else {
+ pm1, n0t := pivot.Sub(m1), n0.Mul(tpo8)
+ p.Add2(pivot.Add(n0).Sub(m1.Mul(tpo8)), pivot.Sub(m2))
+ p.Add2(pm1.Add(n0t), pm1)
+ if m2.Dot(n1) <= 0 {
+ // n1 is between 90 and 135 degrees counter-clockwise of n0.
+ s = m1.Neg()
+ } else {
+ // n1 is between 135 and 180 degrees counter-clockwise of n0.
+ p.Add2(pm1.Sub(n0t), pivot.Sub(m0))
+ s = m0.Neg()
+ }
+ }
+ }
+ // The final quadratic segment has two endpoints s and n1 and the middle
+ // control point is a multiple of s.Add(n1), i.e. it is on the angle bisector
+ // of those two points. The multiple ranges between 128/256 and 150/256 as
+ // the angle between s and n1 ranges between 0 and 45 degrees.
+ // When the angle is 0 degrees (i.e. s and n1 are coincident) then s.Add(n1)
+ // is twice s and so the middle control point of the degenerate quadratic
+ // segment should be half s.Add(n1), and half = 128/256.
+ // When the angle is 45 degrees then 150/256 is the ratio of the lengths of
+ // the two vectors {1, tan(π/8)} and {1 + 1/√2, 1/√2}.
+ // d is the normalized dot product between s and n1. Since the angle ranges
+ // between 0 and 45 degrees then d ranges between 256/256 and 181/256.
+ d := 256 * s.Dot(n1) / r2
+ multiple := Fix32(150 - 22*(d-181)/(256-181))
+ p.Add2(pivot.Add(s.Add(n1).Mul(multiple)), pivot.Add(n1))
+}
+
+// midpoint returns the midpoint of two Points.
+func midpoint(a, b Point) Point {
+ return Point{(a.X + b.X) / 2, (a.Y + b.Y) / 2}
+}
+
+// angleGreaterThan45 returns whether the angle between two vectors is more
+// than 45 degrees.
+func angleGreaterThan45(v0, v1 Point) bool {
+ v := v0.Rot45CCW()
+ return v.Dot(v1) < 0 || v.Rot90CW().Dot(v1) < 0
+}
+
+// interpolate returns the point (1-t)*a + t*b.
+func interpolate(a, b Point, t Fix64) Point {
+ s := 65536 - t
+ x := s*Fix64(a.X) + t*Fix64(b.X)
+ y := s*Fix64(a.Y) + t*Fix64(b.Y)
+ return Point{Fix32(x >> 16), Fix32(y >> 16)}
+}
+
+// curviest2 returns the value of t for which the quadratic parametric curve
+// (1-t)²*a + 2*t*(1-t).b + t²*c has maximum curvature.
+//
+// The curvature of the parametric curve f(t) = (x(t), y(t)) is
+// |x′y″-y′x″| / (x′²+y′²)^(3/2).
+//
+// Let d = b-a and e = c-2*b+a, so that f′(t) = 2*d+2*e*t and f″(t) = 2*e.
+// The curvature's numerator is (2*dx+2*ex*t)*(2*ey)-(2*dy+2*ey*t)*(2*ex),
+// which simplifies to 4*dx*ey-4*dy*ex, which is constant with respect to t.
+//
+// Thus, curvature is extreme where the denominator is extreme, i.e. where
+// (x′²+y′²) is extreme. The first order condition is that
+// 2*x′*x″+2*y′*y″ = 0, or (dx+ex*t)*ex + (dy+ey*t)*ey = 0.
+// Solving for t gives t = -(dx*ex+dy*ey) / (ex*ex+ey*ey).
+func curviest2(a, b, c Point) Fix64 {
+ dx := int64(b.X - a.X)
+ dy := int64(b.Y - a.Y)
+ ex := int64(c.X - 2*b.X + a.X)
+ ey := int64(c.Y - 2*b.Y + a.Y)
+ if ex == 0 && ey == 0 {
+ return 32768
+ }
+ return Fix64(-65536 * (dx*ex + dy*ey) / (ex*ex + ey*ey))
+}
+
+// A stroker holds state for stroking a path.
+type stroker struct {
+ // p is the destination that records the stroked path.
+ p Adder
+ // u is the half-width of the stroke.
+ u Fix32
+ // cr and jr specify how to end and connect path segments.
+ cr Capper
+ jr Joiner
+ // r is the reverse path. Stroking a path involves constructing two
+ // parallel paths 2*u apart. The first path is added immediately to p,
+ // the second path is accumulated in r and eventually added in reverse.
+ r Path
+ // a is the most recent segment point. anorm is the segment normal of
+ // length u at that point.
+ a, anorm Point
+}
+
+// addNonCurvy2 adds a quadratic segment to the stroker, where the segment
+// defined by (k.a, b, c) achieves maximum curvature at either k.a or c.
+func (k *stroker) addNonCurvy2(b, c Point) {
+ // We repeatedly divide the segment at its middle until it is straight
+ // enough to approximate the stroke by just translating the control points.
+ // ds and ps are stacks of depths and points. t is the top of the stack.
+ const maxDepth = 5
+ var (
+ ds [maxDepth + 1]int
+ ps [2*maxDepth + 3]Point
+ t int
+ )
+ // Initially the ps stack has one quadratic segment of depth zero.
+ ds[0] = 0
+ ps[2] = k.a
+ ps[1] = b
+ ps[0] = c
+ anorm := k.anorm
+ var cnorm Point
+
+ for {
+ depth := ds[t]
+ a := ps[2*t+2]
+ b := ps[2*t+1]
+ c := ps[2*t+0]
+ ab := b.Sub(a)
+ bc := c.Sub(b)
+ abIsSmall := ab.Dot(ab) < Fix64(1<<16)
+ bcIsSmall := bc.Dot(bc) < Fix64(1<<16)
+ if abIsSmall && bcIsSmall {
+ // Approximate the segment by a circular arc.
+ cnorm = bc.Norm(k.u).Rot90CCW()
+ mac := midpoint(a, c)
+ addArc(k.p, mac, anorm, cnorm)
+ addArc(&k.r, mac, anorm.Neg(), cnorm.Neg())
+ } else if depth < maxDepth && angleGreaterThan45(ab, bc) {
+ // Divide the segment in two and push both halves on the stack.
+ mab := midpoint(a, b)
+ mbc := midpoint(b, c)
+ t++
+ ds[t+0] = depth + 1
+ ds[t-1] = depth + 1
+ ps[2*t+2] = a
+ ps[2*t+1] = mab
+ ps[2*t+0] = midpoint(mab, mbc)
+ ps[2*t-1] = mbc
+ continue
+ } else {
+ // Translate the control points.
+ bnorm := c.Sub(a).Norm(k.u).Rot90CCW()
+ cnorm = bc.Norm(k.u).Rot90CCW()
+ k.p.Add2(b.Add(bnorm), c.Add(cnorm))
+ k.r.Add2(b.Sub(bnorm), c.Sub(cnorm))
+ }
+ if t == 0 {
+ k.a, k.anorm = c, cnorm
+ return
+ }
+ t--
+ anorm = cnorm
+ }
+ panic("unreachable")
+}
+
+// Add1 adds a linear segment to the stroker.
+func (k *stroker) Add1(b Point) {
+ bnorm := b.Sub(k.a).Norm(k.u).Rot90CCW()
+ if len(k.r) == 0 {
+ k.p.Start(k.a.Add(bnorm))
+ k.r.Start(k.a.Sub(bnorm))
+ } else {
+ k.jr.Join(k.p, &k.r, k.u, k.a, k.anorm, bnorm)
+ }
+ k.p.Add1(b.Add(bnorm))
+ k.r.Add1(b.Sub(bnorm))
+ k.a, k.anorm = b, bnorm
+}
+
+// Add2 adds a quadratic segment to the stroker.
+func (k *stroker) Add2(b, c Point) {
+ ab := b.Sub(k.a)
+ bc := c.Sub(b)
+ abnorm := ab.Norm(k.u).Rot90CCW()
+ if len(k.r) == 0 {
+ k.p.Start(k.a.Add(abnorm))
+ k.r.Start(k.a.Sub(abnorm))
+ } else {
+ k.jr.Join(k.p, &k.r, k.u, k.a, k.anorm, abnorm)
+ }
+
+ // Approximate nearly-degenerate quadratics by linear segments.
+ abIsSmall := ab.Dot(ab) < epsilon
+ bcIsSmall := bc.Dot(bc) < epsilon
+ if abIsSmall || bcIsSmall {
+ acnorm := c.Sub(k.a).Norm(k.u).Rot90CCW()
+ k.p.Add1(c.Add(acnorm))
+ k.r.Add1(c.Sub(acnorm))
+ k.a, k.anorm = c, acnorm
+ return
+ }
+
+ // The quadratic segment (k.a, b, c) has a point of maximum curvature.
+ // If this occurs at an end point, we process the segment as a whole.
+ t := curviest2(k.a, b, c)
+ if t <= 0 || t >= 65536 {
+ k.addNonCurvy2(b, c)
+ return
+ }
+
+ // Otherwise, we perform a de Casteljau decomposition at the point of
+ // maximum curvature and process the two straighter parts.
+ mab := interpolate(k.a, b, t)
+ mbc := interpolate(b, c, t)
+ mabc := interpolate(mab, mbc, t)
+
+ // If the vectors ab and bc are close to being in opposite directions,
+ // then the decomposition can become unstable, so we approximate the
+ // quadratic segment by two linear segments joined by an arc.
+ bcnorm := bc.Norm(k.u).Rot90CCW()
+ if abnorm.Dot(bcnorm) < -Fix64(k.u)*Fix64(k.u)*2047/2048 {
+ pArc := abnorm.Dot(bc) < 0
+
+ k.p.Add1(mabc.Add(abnorm))
+ if pArc {
+ z := abnorm.Rot90CW()
+ addArc(k.p, mabc, abnorm, z)
+ addArc(k.p, mabc, z, bcnorm)
+ }
+ k.p.Add1(mabc.Add(bcnorm))
+ k.p.Add1(c.Add(bcnorm))
+
+ k.r.Add1(mabc.Sub(abnorm))
+ if !pArc {
+ z := abnorm.Rot90CW()
+ addArc(&k.r, mabc, abnorm.Neg(), z)
+ addArc(&k.r, mabc, z, bcnorm.Neg())
+ }
+ k.r.Add1(mabc.Sub(bcnorm))
+ k.r.Add1(c.Sub(bcnorm))
+
+ k.a, k.anorm = c, bcnorm
+ return
+ }
+
+ // Process the decomposed parts.
+ k.addNonCurvy2(mab, mabc)
+ k.addNonCurvy2(mbc, c)
+}
+
+// Add3 adds a cubic segment to the stroker.
+func (k *stroker) Add3(b, c, d Point) {
+ panic("freetype/raster: stroke unimplemented for cubic segments")
+}
+
+// stroke adds the stroked Path q to p, where q consists of exactly one curve.
+func (k *stroker) stroke(q Path) {
+ // Stroking is implemented by deriving two paths each k.u apart from q.
+ // The left-hand-side path is added immediately to k.p; the right-hand-side
+ // path is accumulated in k.r. Once we've finished adding the LHS to k.p,
+ // we add the RHS in reverse order.
+ k.r = make(Path, 0, len(q))
+ k.a = Point{q[1], q[2]}
+ for i := 4; i < len(q); {
+ switch q[i] {
+ case 1:
+ k.Add1(Point{q[i+1], q[i+2]})
+ i += 4
+ case 2:
+ k.Add2(Point{q[i+1], q[i+2]}, Point{q[i+3], q[i+4]})
+ i += 6
+ case 3:
+ k.Add3(Point{q[i+1], q[i+2]}, Point{q[i+3], q[i+4]}, Point{q[i+5], q[i+6]})
+ i += 8
+ default:
+ panic("freetype/raster: bad path")
+ }
+ }
+ if len(k.r) == 0 {
+ return
+ }
+ // TODO(nigeltao): if q is a closed curve then we should join the first and
+ // last segments instead of capping them.
+ k.cr.Cap(k.p, k.u, q.lastPoint(), k.anorm.Neg())
+ addPathReversed(k.p, k.r)
+ pivot := q.firstPoint()
+ k.cr.Cap(k.p, k.u, pivot, pivot.Sub(Point{k.r[1], k.r[2]}))
+}
+
+// Stroke adds q stroked with the given width to p. The result is typically
+// self-intersecting and should be rasterized with UseNonZeroWinding.
+// cr and jr may be nil, which defaults to a RoundCapper or RoundJoiner.
+func Stroke(p Adder, q Path, width Fix32, cr Capper, jr Joiner) {
+ if len(q) == 0 {
+ return
+ }
+ if cr == nil {
+ cr = RoundCapper
+ }
+ if jr == nil {
+ jr = RoundJoiner
+ }
+ if q[0] != 0 {
+ panic("freetype/raster: bad path")
+ }
+ s := stroker{p: p, u: width / 2, cr: cr, jr: jr}
+ i := 0
+ for j := 4; j < len(q); {
+ switch q[j] {
+ case 0:
+ s.stroke(q[i:j])
+ i, j = j, j+4
+ case 1:
+ j += 4
+ case 2:
+ j += 6
+ case 3:
+ j += 8
+ default:
+ panic("freetype/raster: bad path")
+ }
+ }
+ s.stroke(q[i:])
+}
diff --git a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/glyph.go b/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/glyph.go
new file mode 100644
index 000000000..b5f327851
--- /dev/null
+++ b/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/glyph.go
@@ -0,0 +1,530 @@
+// Copyright 2010 The Freetype-Go Authors. All rights reserved.
+// Use of this source code is governed by your choice of either the
+// FreeType License or the GNU General Public License version 2 (or
+// any later version), both of which can be found in the LICENSE file.
+
+package truetype
+
+// Hinting is the policy for snapping a glyph's contours to pixel boundaries.
+type Hinting int32
+
+const (
+ // NoHinting means to not perform any hinting.
+ NoHinting Hinting = iota
+ // FullHinting means to use the font's hinting instructions.
+ FullHinting
+
+ // TODO: implement VerticalHinting.
+)
+
+// 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
+ // The Flags' LSB means whether or not this Point is ``on'' the contour.
+ // Other bits are reserved for internal use.
+ Flags uint32
+}
+
+// A GlyphBuf holds a glyph's contours. A GlyphBuf can be re-used to load a
+// series of glyphs from a Font.
+type GlyphBuf struct {
+ // AdvanceWidth is the glyph's advance width.
+ AdvanceWidth int32
+ // B is the glyph's bounding box.
+ B Bounds
+ // Point contains all Points from all contours of the glyph. If
+ // hinting 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.
+ Point, Unhinted, InFontUnits []Point
+ // End is the point indexes of the end point of each countour. 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.
+ End []int
+
+ font *Font
+ scale int32
+ hinting Hinting
+ hinter hinter
+ // phantomPoints are the co-ordinates of the synthetic phantom points
+ // used for hinting and bounding box calculations.
+ phantomPoints [4]Point
+ // 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
+ // 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
+ // tmp is a scratch buffer.
+ tmp []Point
+}
+
+// Flags for decoding a glyph's contours. These flags are documented at
+// http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html.
+const (
+ flagOnCurve = 1 << iota
+ flagXShortVector
+ flagYShortVector
+ 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,
+// dependent on the value of the flag{X,Y}ShortVector bits.
+const (
+ flagThisXIsSame = flagPositiveXShortVector
+ 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 {
+ g.Point = g.Point[:0]
+ g.Unhinted = g.Unhinted[:0]
+ g.InFontUnits = g.InFontUnits[:0]
+ g.End = g.End[:0]
+ g.font = f
+ g.hinting = h
+ g.scale = scale
+ g.pp1x = 0
+ g.phantomPoints = [4]Point{}
+ g.metricsSet = false
+
+ if h != NoHinting {
+ if err := g.hinter.init(f, scale); err != nil {
+ return err
+ }
+ }
+ if err := g.load(0, i, true); err != nil {
+ return err
+ }
+ // TODO: this selection of either g.pp1x or g.phantomPoints[0].X isn't ideal,
+ // and should be cleaned up once we have all the testScaling tests passing,
+ // plus additional tests for Freetype-Go's bounding boxes matching C Freetype's.
+ pp1x := g.pp1x
+ if h != NoHinting {
+ pp1x = g.phantomPoints[0].X
+ }
+ if pp1x != 0 {
+ for i := range g.Point {
+ g.Point[i].X -= pp1x
+ }
+ }
+
+ advanceWidth := g.phantomPoints[1].X - g.phantomPoints[0].X
+ if h != NoHinting {
+ 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
+ break
+ }
+ }
+ }
+ }
+ advanceWidth = (advanceWidth + 32) &^ 63
+ }
+ g.AdvanceWidth = advanceWidth
+
+ // Set g.B to the 'control box', which is the bounding box of the Bézier
+ // curves' control points. This is easier to calculate, no smaller than
+ // and often equal to the tightest possible bounding box of the curves
+ // themselves. This approach is what C Freetype does. We can't just scale
+ // the nominal bounding box in the glyf data as the hinting process and
+ // phantom point adjustment may move points outside of that box.
+ if len(g.Point) == 0 {
+ g.B = Bounds{}
+ } else {
+ p := g.Point[0]
+ g.B.XMin = p.X
+ g.B.XMax = p.X
+ g.B.YMin = p.Y
+ g.B.YMax = p.Y
+ for _, p := range g.Point[1:] {
+ if g.B.XMin > p.X {
+ g.B.XMin = p.X
+ } else if g.B.XMax < p.X {
+ g.B.XMax = p.X
+ }
+ if g.B.YMin > p.Y {
+ g.B.YMin = p.Y
+ } else if g.B.YMax < p.Y {
+ g.B.YMax = p.Y
+ }
+ }
+ // Snap the box to the grid, if hinting is on.
+ if h != NoHinting {
+ g.B.XMin &^= 63
+ g.B.YMin &^= 63
+ g.B.XMax += 63
+ g.B.XMax &^= 63
+ g.B.YMax += 63
+ g.B.YMax &^= 63
+ }
+ }
+ return nil
+}
+
+func (g *GlyphBuf) load(recursion int32, 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")
+ }
+ // Find the relevant slice of g.font.glyf.
+ var g0, g1 uint32
+ if g.font.locaOffsetFormat == locaOffsetFormatShort {
+ g0 = 2 * uint32(u16(g.font.loca, 2*int(i)))
+ g1 = 2 * uint32(u16(g.font.loca, 2*int(i)+2))
+ } else {
+ g0 = u32(g.font.loca, 4*int(i))
+ g1 = u32(g.font.loca, 4*int(i)+4)
+ }
+
+ // 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)
+ 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)))
+ }
+
+ // Create the phantom points.
+ uhm, pp1x := g.font.unscaledHMetric(i), int32(0)
+ uvm := g.font.unscaledVMetric(i, boundsYMax)
+ g.phantomPoints = [4]Point{
+ {X: boundsXMin - uhm.LeftSideBearing},
+ {X: boundsXMin - uhm.LeftSideBearing + uhm.AdvanceWidth},
+ {X: uhm.AdvanceWidth / 2, Y: boundsYMax + uvm.TopSideBearing},
+ {X: uhm.AdvanceWidth / 2, Y: boundsYMax + uvm.TopSideBearing - uvm.AdvanceHeight},
+ }
+ if len(glyf) == 0 {
+ g.addPhantomsAndScale(len(g.Point), len(g.Point), true, true)
+ copy(g.phantomPoints[:], g.Point[len(g.Point)-4:])
+ g.Point = g.Point[:len(g.Point)-4]
+ return nil
+ }
+
+ // Load and hint the contours.
+ if ne < 0 {
+ if ne != -1 {
+ // http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html says that
+ // "the values -2, -3, and so forth, are reserved for future use."
+ return UnsupportedError("negative number of contours")
+ }
+ pp1x = g.font.scale(g.scale * (boundsXMin - uhm.LeftSideBearing))
+ if err := g.loadCompound(recursion, uhm, i, glyf, useMyMetrics); err != nil {
+ return err
+ }
+ } else {
+ np0, ne0 := len(g.Point), len(g.End)
+ program := g.loadSimple(glyf, ne)
+ g.addPhantomsAndScale(np0, np0, true, true)
+ pp1x = g.Point[len(g.Point)-4].X
+ if g.hinting != NoHinting {
+ if len(program) != 0 {
+ err := g.hinter.run(
+ program,
+ g.Point[np0:],
+ g.Unhinted[np0:],
+ g.InFontUnits[np0:],
+ g.End[ne0:],
+ )
+ if err != nil {
+ return err
+ }
+ }
+ // Drop the four phantom points.
+ g.InFontUnits = g.InFontUnits[:len(g.InFontUnits)-4]
+ g.Unhinted = g.Unhinted[:len(g.Unhinted)-4]
+ }
+ if useMyMetrics {
+ copy(g.phantomPoints[:], g.Point[len(g.Point)-4:])
+ }
+ g.Point = g.Point[:len(g.Point)-4]
+ if np0 != 0 {
+ // The hinting program expects the []End values to be indexed relative
+ // to the inner glyph, not the outer glyph, so we delay adding np0 until
+ // after the hinting program (if any) has run.
+ for i := ne0; i < len(g.End); i++ {
+ g.End[i] += np0
+ }
+ }
+ }
+ if useMyMetrics && !g.metricsSet {
+ g.metricsSet = true
+ g.pp1x = pp1x
+ }
+ return nil
+}
+
+// loadOffset is the initial offset for loadSimple and loadCompound. The first
+// 10 bytes are the number of contours and the bounding box.
+const loadOffset = 10
+
+func (g *GlyphBuf) loadSimple(glyf []byte, ne int) (program []byte) {
+ offset := loadOffset
+ for i := 0; i < ne; i++ {
+ g.End = append(g.End, 1+int(u16(glyf, offset)))
+ offset += 2
+ }
+
+ // Note the TrueType hinting instructions.
+ instrLen := int(u16(glyf, offset))
+ offset += 2
+ program = glyf[offset : offset+instrLen]
+ offset += instrLen
+
+ np0 := len(g.Point)
+ np1 := np0 + int(g.End[len(g.End)-1])
+
+ // Decode the flags.
+ for i := np0; i < np1; {
+ c := uint32(glyf[offset])
+ offset++
+ g.Point = append(g.Point, Point{Flags: c})
+ i++
+ if c&flagRepeat != 0 {
+ count := glyf[offset]
+ offset++
+ for ; count > 0; count-- {
+ g.Point = append(g.Point, Point{Flags: c})
+ i++
+ }
+ }
+ }
+
+ // Decode the co-ordinates.
+ var x int16
+ for i := np0; i < np1; i++ {
+ f := g.Point[i].Flags
+ if f&flagXShortVector != 0 {
+ dx := int16(glyf[offset])
+ offset++
+ if f&flagPositiveXShortVector == 0 {
+ x -= dx
+ } else {
+ x += dx
+ }
+ } else if f&flagThisXIsSame == 0 {
+ x += int16(u16(glyf, offset))
+ offset += 2
+ }
+ g.Point[i].X = int32(x)
+ }
+ var y int16
+ for i := np0; i < np1; i++ {
+ f := g.Point[i].Flags
+ if f&flagYShortVector != 0 {
+ dy := int16(glyf[offset])
+ offset++
+ if f&flagPositiveYShortVector == 0 {
+ y -= dy
+ } else {
+ y += dy
+ }
+ } else if f&flagThisYIsSame == 0 {
+ y += int16(u16(glyf, offset))
+ offset += 2
+ }
+ g.Point[i].Y = int32(y)
+ }
+
+ return program
+}
+
+func (g *GlyphBuf) loadCompound(recursion int32, uhm HMetric, i Index,
+ glyf []byte, useMyMetrics bool) error {
+
+ // Flags for decoding a compound glyph. These flags are documented at
+ // http://developer.apple.com/fonts/TTRefMan/RM06/Chap6glyf.html.
+ const (
+ flagArg1And2AreWords = 1 << iota
+ flagArgsAreXYValues
+ flagRoundXYToGrid
+ flagWeHaveAScale
+ flagUnused
+ flagMoreComponents
+ flagWeHaveAnXAndYScale
+ flagWeHaveATwoByTwo
+ flagWeHaveInstructions
+ flagUseMyMetrics
+ flagOverlapCompound
+ )
+ np0, ne0 := len(g.Point), len(g.End)
+ offset := loadOffset
+ for {
+ flags := u16(glyf, offset)
+ component := Index(u16(glyf, offset+2))
+ dx, dy, transform, hasTransform := int32(0), int32(0), [4]int32{}, false
+ if flags&flagArg1And2AreWords != 0 {
+ dx = int32(int16(u16(glyf, offset+4)))
+ dy = int32(int16(u16(glyf, offset+6)))
+ offset += 8
+ } else {
+ dx = int32(int16(int8(glyf[offset+4])))
+ dy = int32(int16(int8(glyf[offset+5])))
+ offset += 6
+ }
+ if flags&flagArgsAreXYValues == 0 {
+ return UnsupportedError("compound glyph transform vector")
+ }
+ if flags&(flagWeHaveAScale|flagWeHaveAnXAndYScale|flagWeHaveATwoByTwo) != 0 {
+ hasTransform = true
+ switch {
+ case flags&flagWeHaveAScale != 0:
+ transform[0] = int32(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)))
+ 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)))
+ offset += 8
+ }
+ }
+ savedPP := g.phantomPoints
+ np0 := len(g.Point)
+ componentUMM := useMyMetrics && (flags&flagUseMyMetrics != 0)
+ if err := g.load(recursion+1, component, componentUMM); err != nil {
+ return err
+ }
+ if flags&flagUseMyMetrics == 0 {
+ g.phantomPoints = savedPP
+ }
+ 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)
+ p.X, p.Y = newX, newY
+ }
+ }
+ dx = g.font.scale(g.scale * dx)
+ dy = g.font.scale(g.scale * dy)
+ if flags&flagRoundXYToGrid != 0 {
+ dx = (dx + 32) &^ 63
+ dy = (dy + 32) &^ 63
+ }
+ for j := np0; j < len(g.Point); j++ {
+ p := &g.Point[j]
+ p.X += dx
+ p.Y += dy
+ }
+ // TODO: also adjust g.InFontUnits and g.Unhinted?
+ if flags&flagMoreComponents == 0 {
+ break
+ }
+ }
+
+ instrLen := 0
+ if g.hinting != NoHinting && offset+2 <= len(glyf) {
+ instrLen = int(u16(glyf, offset))
+ offset += 2
+ }
+
+ g.addPhantomsAndScale(np0, len(g.Point), false, instrLen > 0)
+ points, ends := g.Point[np0:], g.End[ne0:]
+ g.Point = g.Point[:len(g.Point)-4]
+ for j := range points {
+ points[j].Flags &^= flagTouchedX | flagTouchedY
+ }
+
+ if instrLen == 0 {
+ if !g.metricsSet {
+ copy(g.phantomPoints[:], points[len(points)-4:])
+ }
+ return nil
+ }
+
+ // Hint the compound glyph.
+ program := glyf[offset : offset+instrLen]
+ // Temporarily adjust the ends to be relative to this compound glyph.
+ if np0 != 0 {
+ for i := range ends {
+ ends[i] -= np0
+ }
+ }
+ // Hinting instructions of a composite glyph completely refer to the
+ // (already) hinted subglyphs.
+ g.tmp = append(g.tmp[:0], points...)
+ if err := g.hinter.run(program, points, g.tmp, g.tmp, ends); err != nil {
+ return err
+ }
+ if np0 != 0 {
+ for i := range ends {
+ ends[i] += np0
+ }
+ }
+ if !g.metricsSet {
+ copy(g.phantomPoints[:], points[len(points)-4:])
+ }
+ return nil
+}
+
+func (g *GlyphBuf) addPhantomsAndScale(np0, np1 int, simple, adjust bool) {
+ // Add the four phantom points.
+ g.Point = append(g.Point, g.phantomPoints[:]...)
+ // Scale the points.
+ if simple && g.hinting != NoHinting {
+ g.InFontUnits = append(g.InFontUnits, g.Point[np1:]...)
+ }
+ for i := np1; i < len(g.Point); i++ {
+ p := &g.Point[i]
+ p.X = g.font.scale(g.scale * p.X)
+ p.Y = g.font.scale(g.scale * p.Y)
+ }
+ if g.hinting == NoHinting {
+ return
+ }
+ // Round the 1st phantom point to the grid, shifting all other points equally.
+ // Note that "all other points" starts from np0, not np1.
+ // TODO: delete this adjustment and the np0/np1 distinction, when
+ // we update the compatibility tests to C Freetype 2.5.3.
+ // See http://git.savannah.gnu.org/cgit/freetype/freetype2.git/commit/?id=05c786d990390a7ca18e62962641dac740bacb06
+ if adjust {
+ pp1x := g.Point[len(g.Point)-4].X
+ if dx := ((pp1x + 32) &^ 63) - pp1x; dx != 0 {
+ for i := np0; i < len(g.Point); i++ {
+ g.Point[i].X += dx
+ }
+ }
+ }
+ if simple {
+ g.Unhinted = append(g.Unhinted, g.Point[np1:]...)
+ }
+ // Round the 2nd and 4th phantom point to the grid.
+ p := &g.Point[len(g.Point)-3]
+ p.X = (p.X + 32) &^ 63
+ p = &g.Point[len(g.Point)-1]
+ p.Y = (p.Y + 32) &^ 63
+}
+
+// TODO: is this necessary? The zero-valued GlyphBuf is perfectly usable.
+
+// NewGlyphBuf returns a newly allocated GlyphBuf.
+func NewGlyphBuf() *GlyphBuf {
+ return &GlyphBuf{
+ Point: make([]Point, 0, 256),
+ End: make([]int, 0, 32),
+ }
+}
diff --git a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/hint.go b/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/hint.go
new file mode 100644
index 000000000..26c631436
--- /dev/null
+++ b/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/hint.go
@@ -0,0 +1,1764 @@
+// Copyright 2012 The Freetype-Go Authors. All rights reserved.
+// Use of this source code is governed by your choice of either the
+// FreeType License or the GNU General Public License version 2 (or
+// any later version), both of which can be found in the LICENSE file.
+
+package truetype
+
+// This file implements a Truetype bytecode interpreter.
+// The opcodes are described at https://developer.apple.com/fonts/TTRefMan/RM05/Chap5.html
+
+import (
+ "errors"
+ "math"
+)
+
+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
+ pc int
+ loopCount int32
+}
+
+// hinter implements bytecode hinting. A hinter can be re-used to hint a series
+// of glyphs from a Font.
+type hinter struct {
+ stack, store []int32
+
+ // 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.
+ font *Font
+ scale int32
+
+ // gs and defaultGS are the current and default graphics state. The
+ // default graphics state is the global default graphics state after
+ // the font's fpgm and prep programs have been run.
+ gs, defaultGS graphicsState
+
+ // points and ends are the twilight zone's points, glyph's points
+ // and glyph's contour boundaries.
+ points [numZone][numPointType][]Point
+ ends []int
+
+ // scaledCVT is the lazily initialized scaled Control Value Table.
+ scaledCVTInitialized bool
+ scaledCVT []f26dot6
+}
+
+// graphicsState is described at https://developer.apple.com/fonts/TTRefMan/RM04/Chap4.html
+type graphicsState struct {
+ // Projection vector, freedom vector and dual projection vector.
+ pv, fv, dv [2]f2dot14
+ // Reference points and zone pointers.
+ rp, zp [3]int32
+ // Control Value / Single Width Cut-In.
+ controlValueCutIn, singleWidthCutIn, singleWidth f26dot6
+ // Delta base / shift.
+ deltaBase, deltaShift int32
+ // Minimum distance.
+ minDist f26dot6
+ // Loop count.
+ loop int32
+ // Rounding policy.
+ roundPeriod, roundPhase, roundThreshold f26dot6
+ roundSuper45 bool
+ // Auto-flip.
+ autoFlip bool
+}
+
+var globalDefaultGS = graphicsState{
+ pv: [2]f2dot14{0x4000, 0}, // Unit vector along the X axis.
+ fv: [2]f2dot14{0x4000, 0},
+ dv: [2]f2dot14{0x4000, 0},
+ zp: [3]int32{1, 1, 1},
+ controlValueCutIn: (17 << 6) / 16, // 17/16 as an f26dot6.
+ deltaBase: 9,
+ deltaShift: 3,
+ minDist: 1 << 6, // 1 as an f26dot6.
+ loop: 1,
+ roundPeriod: 1 << 6, // 1 as an f26dot6.
+ roundThreshold: 1 << 5, // 1/2 as an f26dot6.
+ roundSuper45: false,
+ autoFlip: true,
+}
+
+func resetTwilightPoints(f *Font, p []Point) []Point {
+ if n := int(f.maxTwilightPoints) + 4; n <= cap(p) {
+ p = p[:n]
+ for i := range p {
+ p[i] = Point{}
+ }
+ } else {
+ p = make([]Point, n)
+ }
+ return p
+}
+
+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 {
+ h.font, rescale = f, true
+ if h.functions == nil {
+ h.functions = make(map[int32][]byte)
+ } else {
+ for k := range h.functions {
+ delete(h.functions, k)
+ }
+ }
+
+ if x := int(f.maxStackElements); x > len(h.stack) {
+ x += 255
+ x &^= 255
+ h.stack = make([]int32, x)
+ }
+ if x := int(f.maxStorage); x > len(h.store) {
+ x += 15
+ x &^= 15
+ h.store = make([]int32, x)
+ }
+ if len(f.fpgm) != 0 {
+ if err := h.run(f.fpgm, nil, nil, nil, nil); err != nil {
+ return err
+ }
+ }
+ }
+
+ if rescale {
+ h.scale = scale
+ h.scaledCVTInitialized = false
+
+ h.defaultGS = globalDefaultGS
+
+ if len(f.prep) != 0 {
+ if err := h.run(f.prep, nil, nil, nil, nil); err != nil {
+ return err
+ }
+ h.defaultGS = h.gs
+ // The MS rasterizer doesn't allow the following graphics state
+ // variables to be modified by the CVT program.
+ h.defaultGS.pv = globalDefaultGS.pv
+ h.defaultGS.fv = globalDefaultGS.fv
+ h.defaultGS.dv = globalDefaultGS.dv
+ h.defaultGS.rp = globalDefaultGS.rp
+ h.defaultGS.zp = globalDefaultGS.zp
+ h.defaultGS.loop = globalDefaultGS.loop
+ }
+ }
+ return nil
+}
+
+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")
+ }
+ var (
+ steps, pc, top int
+ opcode uint8
+
+ callStack [32]callStackEntry
+ callStackTop int
+ )
+
+ for 0 <= pc && pc < len(program) {
+ steps++
+ if steps == 100000 {
+ return errors.New("truetype: hinting: too many steps")
+ }
+ opcode = program[pc]
+ if top < int(popCount[opcode]) {
+ return errors.New("truetype: hinting: stack underflow")
+ }
+ switch opcode {
+
+ case opSVTCA0:
+ h.gs.pv = [2]f2dot14{0, 0x4000}
+ h.gs.fv = [2]f2dot14{0, 0x4000}
+ h.gs.dv = [2]f2dot14{0, 0x4000}
+
+ case opSVTCA1:
+ h.gs.pv = [2]f2dot14{0x4000, 0}
+ h.gs.fv = [2]f2dot14{0x4000, 0}
+ h.gs.dv = [2]f2dot14{0x4000, 0}
+
+ case opSPVTCA0:
+ h.gs.pv = [2]f2dot14{0, 0x4000}
+ h.gs.dv = [2]f2dot14{0, 0x4000}
+
+ case opSPVTCA1:
+ h.gs.pv = [2]f2dot14{0x4000, 0}
+ h.gs.dv = [2]f2dot14{0x4000, 0}
+
+ case opSFVTCA0:
+ h.gs.fv = [2]f2dot14{0, 0x4000}
+
+ case opSFVTCA1:
+ h.gs.fv = [2]f2dot14{0x4000, 0}
+
+ case opSPVTL0, opSPVTL1, opSFVTL0, opSFVTL1:
+ top -= 2
+ p1 := h.point(0, current, h.stack[top+0])
+ p2 := h.point(0, current, h.stack[top+1])
+ if p1 == nil || p2 == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ dx := f2dot14(p1.X - p2.X)
+ dy := f2dot14(p1.Y - p2.Y)
+ if dx == 0 && dy == 0 {
+ dx = 0x4000
+ } else if opcode&1 != 0 {
+ // Counter-clockwise rotation.
+ dx, dy = -dy, dx
+ }
+ v := normalize(dx, dy)
+ if opcode < opSFVTL0 {
+ h.gs.pv = v
+ h.gs.dv = v
+ } else {
+ h.gs.fv = v
+ }
+
+ case opSPVFS:
+ top -= 2
+ h.gs.pv = normalize(f2dot14(h.stack[top]), f2dot14(h.stack[top+1]))
+ h.gs.dv = h.gs.pv
+
+ case opSFVFS:
+ top -= 2
+ h.gs.fv = normalize(f2dot14(h.stack[top]), f2dot14(h.stack[top+1]))
+
+ case opGPV:
+ if top+1 >= len(h.stack) {
+ return errors.New("truetype: hinting: stack overflow")
+ }
+ h.stack[top+0] = int32(h.gs.pv[0])
+ h.stack[top+1] = int32(h.gs.pv[1])
+ top += 2
+
+ case opGFV:
+ if top+1 >= len(h.stack) {
+ return errors.New("truetype: hinting: stack overflow")
+ }
+ h.stack[top+0] = int32(h.gs.fv[0])
+ h.stack[top+1] = int32(h.gs.fv[1])
+ top += 2
+
+ case opSFVTPV:
+ h.gs.fv = h.gs.pv
+
+ case opISECT:
+ top -= 5
+ p := h.point(2, current, h.stack[top+0])
+ a0 := h.point(1, current, h.stack[top+1])
+ a1 := h.point(1, current, h.stack[top+2])
+ b0 := h.point(0, current, h.stack[top+3])
+ b1 := h.point(0, current, h.stack[top+4])
+ if p == nil || a0 == nil || a1 == nil || b0 == nil || b1 == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+
+ dbx := b1.X - b0.X
+ dby := b1.Y - b0.Y
+ dax := a1.X - a0.X
+ day := a1.Y - a0.Y
+ dx := b0.X - a0.X
+ dy := b0.Y - a0.Y
+ discriminant := mulDiv(int64(dax), int64(-dby), 0x40) +
+ mulDiv(int64(day), int64(dbx), 0x40)
+ dotProduct := mulDiv(int64(dax), int64(dbx), 0x40) +
+ mulDiv(int64(day), int64(dby), 0x40)
+ // The discriminant above is actually a cross product of vectors
+ // da and db. Together with the dot product, they can be used as
+ // surrogates for sine and cosine of the angle between the vectors.
+ // Indeed,
+ // dotproduct = |da||db|cos(angle)
+ // discriminant = |da||db|sin(angle)
+ // We use these equations to reject grazing intersections by
+ // thresholding abs(tan(angle)) at 1/19, corresponding to 3 degrees.
+ absDisc, absDotP := discriminant, dotProduct
+ if absDisc < 0 {
+ absDisc = -absDisc
+ }
+ if absDotP < 0 {
+ absDotP = -absDotP
+ }
+ if 19*absDisc > absDotP {
+ val := mulDiv(int64(dx), int64(-dby), 0x40) +
+ 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)
+ } else {
+ p.X = (a0.X + a1.X + b0.X + b1.X) / 4
+ p.Y = (a0.Y + a1.Y + b0.Y + b1.Y) / 4
+ }
+ p.Flags |= flagTouchedX | flagTouchedY
+
+ case opSRP0, opSRP1, opSRP2:
+ top--
+ h.gs.rp[opcode-opSRP0] = h.stack[top]
+
+ case opSZP0, opSZP1, opSZP2:
+ top--
+ h.gs.zp[opcode-opSZP0] = h.stack[top]
+
+ case opSZPS:
+ top--
+ h.gs.zp[0] = h.stack[top]
+ h.gs.zp[1] = h.stack[top]
+ h.gs.zp[2] = h.stack[top]
+
+ case opSLOOP:
+ top--
+ if h.stack[top] <= 0 {
+ return errors.New("truetype: hinting: invalid data")
+ }
+ h.gs.loop = h.stack[top]
+
+ case opRTG:
+ h.gs.roundPeriod = 1 << 6
+ h.gs.roundPhase = 0
+ h.gs.roundThreshold = 1 << 5
+ h.gs.roundSuper45 = false
+
+ case opRTHG:
+ h.gs.roundPeriod = 1 << 6
+ h.gs.roundPhase = 1 << 5
+ h.gs.roundThreshold = 1 << 5
+ h.gs.roundSuper45 = false
+
+ case opSMD:
+ top--
+ h.gs.minDist = f26dot6(h.stack[top])
+
+ case opELSE:
+ opcode = 1
+ goto ifelse
+
+ case opJMPR:
+ top--
+ pc += int(h.stack[top])
+ continue
+
+ case opSCVTCI:
+ top--
+ h.gs.controlValueCutIn = f26dot6(h.stack[top])
+
+ case opSSWCI:
+ top--
+ h.gs.singleWidthCutIn = f26dot6(h.stack[top])
+
+ case opSSW:
+ top--
+ h.gs.singleWidth = f26dot6(h.font.scale(h.scale * h.stack[top]))
+
+ case opDUP:
+ if top >= len(h.stack) {
+ return errors.New("truetype: hinting: stack overflow")
+ }
+ h.stack[top] = h.stack[top-1]
+ top++
+
+ case opPOP:
+ top--
+
+ case opCLEAR:
+ top = 0
+
+ case opSWAP:
+ h.stack[top-1], h.stack[top-2] = h.stack[top-2], h.stack[top-1]
+
+ case opDEPTH:
+ if top >= len(h.stack) {
+ return errors.New("truetype: hinting: stack overflow")
+ }
+ h.stack[top] = int32(top)
+ top++
+
+ case opCINDEX, opMINDEX:
+ x := int(h.stack[top-1])
+ if x <= 0 || x >= top {
+ return errors.New("truetype: hinting: invalid data")
+ }
+ h.stack[top-1] = h.stack[top-1-x]
+ if opcode == opMINDEX {
+ copy(h.stack[top-1-x:top-1], h.stack[top-x:top])
+ top--
+ }
+
+ case opALIGNPTS:
+ top -= 2
+ p := h.point(1, current, h.stack[top])
+ q := h.point(0, current, h.stack[top+1])
+ if p == nil || q == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ d := dotProduct(f26dot6(q.X-p.X), f26dot6(q.Y-p.Y), h.gs.pv) / 2
+ h.move(p, +d, true)
+ h.move(q, -d, true)
+
+ case opUTP:
+ top--
+ p := h.point(0, current, h.stack[top])
+ if p == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ p.Flags &^= flagTouchedX | flagTouchedY
+
+ case opLOOPCALL, opCALL:
+ if callStackTop >= len(callStack) {
+ return errors.New("truetype: hinting: call stack overflow")
+ }
+ top--
+ f, ok := h.functions[h.stack[top]]
+ if !ok {
+ return errors.New("truetype: hinting: undefined function")
+ }
+ callStack[callStackTop] = callStackEntry{program, pc, 1}
+ if opcode == opLOOPCALL {
+ top--
+ if h.stack[top] == 0 {
+ break
+ }
+ callStack[callStackTop].loopCount = h.stack[top]
+ }
+ callStackTop++
+ program, pc = f, 0
+ continue
+
+ case opFDEF:
+ // Save all bytecode up until the next ENDF.
+ startPC := pc + 1
+ fdefloop:
+ for {
+ pc++
+ if pc >= len(program) {
+ return errors.New("truetype: hinting: unbalanced FDEF")
+ }
+ switch program[pc] {
+ case opFDEF:
+ return errors.New("truetype: hinting: nested FDEF")
+ case opENDF:
+ top--
+ h.functions[h.stack[top]] = program[startPC : pc+1]
+ break fdefloop
+ default:
+ var ok bool
+ pc, ok = skipInstructionPayload(program, pc)
+ if !ok {
+ return errors.New("truetype: hinting: unbalanced FDEF")
+ }
+ }
+ }
+
+ case opENDF:
+ if callStackTop == 0 {
+ return errors.New("truetype: hinting: call stack underflow")
+ }
+ callStackTop--
+ callStack[callStackTop].loopCount--
+ if callStack[callStackTop].loopCount != 0 {
+ callStackTop++
+ pc = 0
+ continue
+ }
+ program, pc = callStack[callStackTop].program, callStack[callStackTop].pc
+
+ case opMDAP0, opMDAP1:
+ top--
+ i := h.stack[top]
+ p := h.point(0, current, i)
+ if p == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ 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, true)
+ h.gs.rp[0] = i
+ h.gs.rp[1] = i
+
+ case opIUP0, opIUP1:
+ iupY, mask := opcode == opIUP0, uint32(flagTouchedX)
+ if iupY {
+ mask = flagTouchedY
+ }
+ prevEnd := 0
+ for _, end := range h.ends {
+ for i := prevEnd; i < end; i++ {
+ for i < end && h.points[glyphZone][current][i].Flags&mask == 0 {
+ i++
+ }
+ if i == end {
+ break
+ }
+ firstTouched, curTouched := i, i
+ i++
+ for ; i < end; i++ {
+ if h.points[glyphZone][current][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 opSHP0, opSHP1:
+ if top < int(h.gs.loop) {
+ return errors.New("truetype: hinting: stack underflow")
+ }
+ _, _, d, ok := h.displacement(opcode&1 == 0)
+ if !ok {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ for ; h.gs.loop != 0; h.gs.loop-- {
+ top--
+ p := h.point(2, current, h.stack[top])
+ if p == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ h.move(p, d, true)
+ }
+ h.gs.loop = 1
+
+ case opSHC0, opSHC1:
+ top--
+ zonePointer, i, d, ok := h.displacement(opcode&1 == 0)
+ if !ok {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ if h.gs.zp[2] == 0 {
+ // TODO: implement this when we have a glyph that does this.
+ return errors.New("hinting: unimplemented SHC instruction")
+ }
+ contour := h.stack[top]
+ if contour < 0 || len(ends) <= int(contour) {
+ return errors.New("truetype: hinting: contour out of range")
+ }
+ j0, j1 := int32(0), int32(h.ends[contour])
+ if contour > 0 {
+ j0 = int32(h.ends[contour-1])
+ }
+ move := h.gs.zp[zonePointer] != h.gs.zp[2]
+ for j := j0; j < j1; j++ {
+ if move || j != i {
+ h.move(h.point(2, current, j), d, true)
+ }
+ }
+
+ case opSHZ0, opSHZ1:
+ top--
+ zonePointer, i, d, ok := h.displacement(opcode&1 == 0)
+ if !ok {
+ return errors.New("truetype: hinting: point out of range")
+ }
+
+ // As per C Freetype, SHZ doesn't move the phantom points, or mark
+ // the points as touched.
+ limit := int32(len(h.points[h.gs.zp[2]][current]))
+ if h.gs.zp[2] == glyphZone {
+ limit -= 4
+ }
+ for j := int32(0); j < limit; j++ {
+ if i != j || h.gs.zp[zonePointer] != h.gs.zp[2] {
+ h.move(h.point(2, current, j), d, false)
+ }
+ }
+
+ case opSHPIX:
+ top--
+ d := f26dot6(h.stack[top])
+ if top < int(h.gs.loop) {
+ return errors.New("truetype: hinting: stack underflow")
+ }
+ for ; h.gs.loop != 0; h.gs.loop-- {
+ top--
+ p := h.point(2, current, h.stack[top])
+ if p == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ h.move(p, d, true)
+ }
+ h.gs.loop = 1
+
+ case opIP:
+ if top < int(h.gs.loop) {
+ return errors.New("truetype: hinting: stack underflow")
+ }
+ pointType := inFontUnits
+ twilight := h.gs.zp[0] == 0 || h.gs.zp[1] == 0 || h.gs.zp[2] == 0
+ if twilight {
+ pointType = unhinted
+ }
+ p := h.point(1, pointType, h.gs.rp[2])
+ oldP := h.point(0, pointType, h.gs.rp[1])
+ oldRange := dotProduct(f26dot6(p.X-oldP.X), f26dot6(p.Y-oldP.Y), h.gs.dv)
+
+ p = h.point(1, current, h.gs.rp[2])
+ curP := h.point(0, current, h.gs.rp[1])
+ curRange := dotProduct(f26dot6(p.X-curP.X), f26dot6(p.Y-curP.Y), h.gs.pv)
+ for ; h.gs.loop != 0; h.gs.loop-- {
+ top--
+ i := h.stack[top]
+ p = h.point(2, pointType, i)
+ oldDist := dotProduct(f26dot6(p.X-oldP.X), f26dot6(p.Y-oldP.Y), h.gs.dv)
+ p = h.point(2, current, i)
+ curDist := dotProduct(f26dot6(p.X-curP.X), f26dot6(p.Y-curP.Y), h.gs.pv)
+ newDist := f26dot6(0)
+ if oldDist != 0 {
+ if oldRange != 0 {
+ newDist = f26dot6(mulDiv(int64(oldDist), int64(curRange), int64(oldRange)))
+ } else {
+ newDist = -oldDist
+ }
+ }
+ h.move(p, newDist-curDist, true)
+ }
+ h.gs.loop = 1
+
+ case opMSIRP0, opMSIRP1:
+ top -= 2
+ i := h.stack[top]
+ distance := f26dot6(h.stack[top+1])
+
+ // TODO: special case h.gs.zp[1] == 0 in C Freetype.
+ ref := h.point(0, current, h.gs.rp[0])
+ p := h.point(1, current, i)
+ if ref == nil || p == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ curDist := dotProduct(f26dot6(p.X-ref.X), f26dot6(p.Y-ref.Y), h.gs.pv)
+
+ // Set-RP0 bit.
+ if opcode == opMSIRP1 {
+ h.gs.rp[0] = i
+ }
+ h.gs.rp[1] = h.gs.rp[0]
+ h.gs.rp[2] = i
+
+ // Move the point.
+ h.move(p, distance-curDist, true)
+
+ case opALIGNRP:
+ if top < int(h.gs.loop) {
+ return errors.New("truetype: hinting: stack underflow")
+ }
+ ref := h.point(0, current, h.gs.rp[0])
+ if ref == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ for ; h.gs.loop != 0; h.gs.loop-- {
+ top--
+ p := h.point(1, current, h.stack[top])
+ if p == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ h.move(p, -dotProduct(f26dot6(p.X-ref.X), f26dot6(p.Y-ref.Y), h.gs.pv), true)
+ }
+ h.gs.loop = 1
+
+ case opRTDG:
+ h.gs.roundPeriod = 1 << 5
+ h.gs.roundPhase = 0
+ h.gs.roundThreshold = 1 << 4
+ h.gs.roundSuper45 = false
+
+ case opMIAP0, opMIAP1:
+ top -= 2
+ i := h.stack[top]
+ distance := h.getScaledCVT(h.stack[top+1])
+ 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)
+ *q = *p
+ }
+ p := h.point(0, current, i)
+ oldDist := dotProduct(f26dot6(p.X), f26dot6(p.Y), h.gs.pv)
+ if opcode == opMIAP1 {
+ if (distance - oldDist).abs() > h.gs.controlValueCutIn {
+ distance = oldDist
+ }
+ // TODO: metrics compensation.
+ distance = h.round(distance)
+ }
+ h.move(p, distance-oldDist, true)
+ h.gs.rp[0] = i
+ h.gs.rp[1] = i
+
+ case opNPUSHB:
+ opcode = 0
+ goto push
+
+ case opNPUSHW:
+ opcode = 0x80
+ goto push
+
+ case opWS:
+ top -= 2
+ i := int(h.stack[top])
+ if i < 0 || len(h.store) <= i {
+ return errors.New("truetype: hinting: invalid data")
+ }
+ h.store[i] = h.stack[top+1]
+
+ case opRS:
+ i := int(h.stack[top-1])
+ if i < 0 || len(h.store) <= i {
+ return errors.New("truetype: hinting: invalid data")
+ }
+ h.stack[top-1] = h.store[i]
+
+ case opWCVTP:
+ top -= 2
+ h.setScaledCVT(h.stack[top], f26dot6(h.stack[top+1]))
+
+ case opRCVT:
+ h.stack[top-1] = int32(h.getScaledCVT(h.stack[top-1]))
+
+ case opGC0, opGC1:
+ i := h.stack[top-1]
+ if opcode == opGC0 {
+ p := h.point(2, current, i)
+ h.stack[top-1] = int32(dotProduct(f26dot6(p.X), f26dot6(p.Y), h.gs.pv))
+ } else {
+ p := h.point(2, unhinted, i)
+ // Using dv as per C Freetype.
+ h.stack[top-1] = int32(dotProduct(f26dot6(p.X), f26dot6(p.Y), h.gs.dv))
+ }
+
+ case opSCFS:
+ top -= 2
+ i := h.stack[top]
+ p := h.point(2, current, i)
+ if p == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ c := dotProduct(f26dot6(p.X), f26dot6(p.Y), h.gs.pv)
+ h.move(p, f26dot6(h.stack[top+1])-c, true)
+ if h.gs.zp[2] != 0 {
+ break
+ }
+ q := h.point(2, unhinted, i)
+ if q == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ q.X = p.X
+ q.Y = p.Y
+
+ case opMD0, opMD1:
+ top--
+ pt, v, scale := pointType(0), [2]f2dot14{}, false
+ if opcode == opMD0 {
+ pt = current
+ v = h.gs.pv
+ } else if h.gs.zp[0] == 0 || h.gs.zp[1] == 0 {
+ pt = unhinted
+ v = h.gs.dv
+ } else {
+ pt = inFontUnits
+ v = h.gs.dv
+ scale = true
+ }
+ p := h.point(0, pt, h.stack[top-1])
+ q := h.point(1, pt, h.stack[top])
+ if p == nil || q == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ 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))
+ }
+ h.stack[top-1] = d
+
+ case opMPPEM, opMPS:
+ if top >= len(h.stack) {
+ return errors.New("truetype: hinting: stack overflow")
+ }
+ // For MPS, point size should be irrelevant; we return the PPEM.
+ h.stack[top] = h.scale >> 6
+ top++
+
+ case opFLIPON, opFLIPOFF:
+ h.gs.autoFlip = opcode == opFLIPON
+
+ case opDEBUG:
+ // No-op.
+
+ case opLT:
+ top--
+ h.stack[top-1] = bool2int32(h.stack[top-1] < h.stack[top])
+
+ case opLTEQ:
+ top--
+ h.stack[top-1] = bool2int32(h.stack[top-1] <= h.stack[top])
+
+ case opGT:
+ top--
+ h.stack[top-1] = bool2int32(h.stack[top-1] > h.stack[top])
+
+ case opGTEQ:
+ top--
+ h.stack[top-1] = bool2int32(h.stack[top-1] >= h.stack[top])
+
+ case opEQ:
+ top--
+ h.stack[top-1] = bool2int32(h.stack[top-1] == h.stack[top])
+
+ case opNEQ:
+ top--
+ h.stack[top-1] = bool2int32(h.stack[top-1] != h.stack[top])
+
+ case opODD, opEVEN:
+ i := h.round(f26dot6(h.stack[top-1])) >> 6
+ h.stack[top-1] = int32(i&1) ^ int32(opcode-opODD)
+
+ case opIF:
+ top--
+ if h.stack[top] == 0 {
+ opcode = 0
+ goto ifelse
+ }
+
+ case opEIF:
+ // No-op.
+
+ case opAND:
+ top--
+ h.stack[top-1] = bool2int32(h.stack[top-1] != 0 && h.stack[top] != 0)
+
+ case opOR:
+ top--
+ h.stack[top-1] = bool2int32(h.stack[top-1]|h.stack[top] != 0)
+
+ case opNOT:
+ h.stack[top-1] = bool2int32(h.stack[top-1] == 0)
+
+ case opDELTAP1:
+ goto delta
+
+ case opSDB:
+ top--
+ h.gs.deltaBase = h.stack[top]
+
+ case opSDS:
+ top--
+ h.gs.deltaShift = h.stack[top]
+
+ case opADD:
+ top--
+ h.stack[top-1] += h.stack[top]
+
+ case opSUB:
+ top--
+ h.stack[top-1] -= h.stack[top]
+
+ case opDIV:
+ top--
+ if h.stack[top] == 0 {
+ return errors.New("truetype: hinting: division by zero")
+ }
+ h.stack[top-1] = int32(f26dot6(h.stack[top-1]).div(f26dot6(h.stack[top])))
+
+ case opMUL:
+ top--
+ h.stack[top-1] = int32(f26dot6(h.stack[top-1]).mul(f26dot6(h.stack[top])))
+
+ case opABS:
+ if h.stack[top-1] < 0 {
+ h.stack[top-1] = -h.stack[top-1]
+ }
+
+ case opNEG:
+ h.stack[top-1] = -h.stack[top-1]
+
+ case opFLOOR:
+ h.stack[top-1] &^= 63
+
+ case opCEILING:
+ h.stack[top-1] += 63
+ h.stack[top-1] &^= 63
+
+ case opROUND00, opROUND01, opROUND10, opROUND11:
+ // The four flavors of opROUND are equivalent. See the comment below on
+ // opNROUND for the rationale.
+ h.stack[top-1] = int32(h.round(f26dot6(h.stack[top-1])))
+
+ case opNROUND00, opNROUND01, opNROUND10, opNROUND11:
+ // No-op. The spec says to add one of four "compensations for the engine
+ // characteristics", to cater for things like "different dot-size printers".
+ // https://developer.apple.com/fonts/TTRefMan/RM02/Chap2.html#engine_compensation
+ // This code does not implement engine compensation, as we don't expect to
+ // be used to output on dot-matrix printers.
+
+ case opWCVTF:
+ top -= 2
+ h.setScaledCVT(h.stack[top], f26dot6(h.font.scale(h.scale*h.stack[top+1])))
+
+ case opDELTAP2, opDELTAP3, opDELTAC1, opDELTAC2, opDELTAC3:
+ goto delta
+
+ case opSROUND, opS45ROUND:
+ top--
+ switch (h.stack[top] >> 6) & 0x03 {
+ case 0:
+ h.gs.roundPeriod = 1 << 5
+ case 1, 3:
+ h.gs.roundPeriod = 1 << 6
+ case 2:
+ h.gs.roundPeriod = 1 << 7
+ }
+ h.gs.roundSuper45 = opcode == opS45ROUND
+ if h.gs.roundSuper45 {
+ // The spec says to multiply by √2, but the C Freetype code says 1/√2.
+ // We go with 1/√2.
+ h.gs.roundPeriod *= 46341
+ h.gs.roundPeriod /= 65536
+ }
+ h.gs.roundPhase = h.gs.roundPeriod * f26dot6((h.stack[top]>>4)&0x03) / 4
+ if x := h.stack[top] & 0x0f; x != 0 {
+ h.gs.roundThreshold = h.gs.roundPeriod * f26dot6(x-4) / 8
+ } else {
+ h.gs.roundThreshold = h.gs.roundPeriod - 1
+ }
+
+ case opJROT:
+ top -= 2
+ if h.stack[top+1] != 0 {
+ pc += int(h.stack[top])
+ continue
+ }
+
+ case opJROF:
+ top -= 2
+ if h.stack[top+1] == 0 {
+ pc += int(h.stack[top])
+ continue
+ }
+
+ case opROFF:
+ h.gs.roundPeriod = 0
+ h.gs.roundPhase = 0
+ h.gs.roundThreshold = 0
+ h.gs.roundSuper45 = false
+
+ case opRUTG:
+ h.gs.roundPeriod = 1 << 6
+ h.gs.roundPhase = 0
+ h.gs.roundThreshold = 1<<6 - 1
+ h.gs.roundSuper45 = false
+
+ case opRDTG:
+ h.gs.roundPeriod = 1 << 6
+ h.gs.roundPhase = 0
+ h.gs.roundThreshold = 0
+ h.gs.roundSuper45 = false
+
+ case opSANGW, opAA:
+ // These ops are "anachronistic" and no longer used.
+ top--
+
+ case opFLIPPT:
+ if top < int(h.gs.loop) {
+ return errors.New("truetype: hinting: stack underflow")
+ }
+ points := h.points[glyphZone][current]
+ for ; h.gs.loop != 0; h.gs.loop-- {
+ top--
+ i := h.stack[top]
+ if i < 0 || len(points) <= int(i) {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ points[i].Flags ^= flagOnCurve
+ }
+ h.gs.loop = 1
+
+ case opFLIPRGON, opFLIPRGOFF:
+ top -= 2
+ i, j, points := h.stack[top], h.stack[top+1], h.points[glyphZone][current]
+ if i < 0 || len(points) <= int(i) || j < 0 || len(points) <= int(j) {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ for ; i <= j; i++ {
+ if opcode == opFLIPRGON {
+ points[i].Flags |= flagOnCurve
+ } else {
+ points[i].Flags &^= flagOnCurve
+ }
+ }
+
+ case opSCANCTRL:
+ // We do not support dropout control, as we always rasterize grayscale glyphs.
+ top--
+
+ case opSDPVTL0, opSDPVTL1:
+ top -= 2
+ for i := 0; i < 2; i++ {
+ pt := unhinted
+ if i != 0 {
+ pt = current
+ }
+ p := h.point(1, pt, h.stack[top])
+ q := h.point(2, pt, h.stack[top+1])
+ if p == nil || q == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ dx := f2dot14(p.X - q.X)
+ dy := f2dot14(p.Y - q.Y)
+ if dx == 0 && dy == 0 {
+ dx = 0x4000
+ } else if opcode&1 != 0 {
+ // Counter-clockwise rotation.
+ dx, dy = -dy, dx
+ }
+ if i == 0 {
+ h.gs.dv = normalize(dx, dy)
+ } else {
+ h.gs.pv = normalize(dx, dy)
+ }
+ }
+
+ case opGETINFO:
+ res := int32(0)
+ if h.stack[top-1]&(1<<0) != 0 {
+ // Set the engine version. We hard-code this to 35, the same as
+ // the C freetype code, which says that "Version~35 corresponds
+ // to MS rasterizer v.1.7 as used e.g. in Windows~98".
+ res |= 35
+ }
+ if h.stack[top-1]&(1<<5) != 0 {
+ // Set that we support grayscale.
+ res |= 1 << 12
+ }
+ // We set no other bits, as we do not support rotated or stretched glyphs.
+ h.stack[top-1] = res
+
+ case opIDEF:
+ // IDEF is for ancient versions of the bytecode interpreter, and is no longer used.
+ return errors.New("truetype: hinting: unsupported IDEF instruction")
+
+ case opROLL:
+ h.stack[top-1], h.stack[top-3], h.stack[top-2] =
+ h.stack[top-3], h.stack[top-2], h.stack[top-1]
+
+ case opMAX:
+ top--
+ if h.stack[top-1] < h.stack[top] {
+ h.stack[top-1] = h.stack[top]
+ }
+
+ case opMIN:
+ top--
+ if h.stack[top-1] > h.stack[top] {
+ h.stack[top-1] = h.stack[top]
+ }
+
+ case opSCANTYPE:
+ // We do not support dropout control, as we always rasterize grayscale glyphs.
+ top--
+
+ case opINSTCTRL:
+ // TODO: support instruction execution control? It seems rare, and even when
+ // nominally used (e.g. Source Sans Pro), it seems conditional on extreme or
+ // unusual rasterization conditions. For example, the code snippet at
+ // https://developer.apple.com/fonts/TTRefMan/RM05/Chap5.html#INSTCTRL
+ // uses INSTCTRL when grid-fitting a rotated or stretched glyph, but
+ // freetype-go does not support rotated or stretched glyphs.
+ top -= 2
+
+ default:
+ if opcode < opPUSHB000 {
+ return errors.New("truetype: hinting: unrecognized instruction")
+ }
+
+ if opcode < opMDRP00000 {
+ // PUSHxxxx opcode.
+
+ if opcode < opPUSHW000 {
+ opcode -= opPUSHB000 - 1
+ } else {
+ opcode -= opPUSHW000 - 1 - 0x80
+ }
+ goto push
+ }
+
+ if opcode < opMIRP00000 {
+ // MDRPxxxxx opcode.
+
+ top--
+ i := h.stack[top]
+ ref := h.point(0, current, h.gs.rp[0])
+ p := h.point(1, current, i)
+ if ref == nil || p == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+
+ oldDist := f26dot6(0)
+ if h.gs.zp[0] == 0 || h.gs.zp[1] == 0 {
+ p0 := h.point(1, unhinted, i)
+ p1 := h.point(0, unhinted, h.gs.rp[0])
+ oldDist = dotProduct(f26dot6(p0.X-p1.X), f26dot6(p0.Y-p1.Y), h.gs.dv)
+ } else {
+ 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)))
+ }
+
+ // Single-width cut-in test.
+ if x := (oldDist - h.gs.singleWidth).abs(); x < h.gs.singleWidthCutIn {
+ if oldDist >= 0 {
+ oldDist = +h.gs.singleWidth
+ } else {
+ oldDist = -h.gs.singleWidth
+ }
+ }
+
+ // Rounding bit.
+ // TODO: metrics compensation.
+ distance := oldDist
+ if opcode&0x04 != 0 {
+ distance = h.round(oldDist)
+ }
+
+ // Minimum distance bit.
+ if opcode&0x08 != 0 {
+ if oldDist >= 0 {
+ if distance < h.gs.minDist {
+ distance = h.gs.minDist
+ }
+ } else {
+ if distance > -h.gs.minDist {
+ distance = -h.gs.minDist
+ }
+ }
+ }
+
+ // Set-RP0 bit.
+ h.gs.rp[1] = h.gs.rp[0]
+ h.gs.rp[2] = i
+ if opcode&0x10 != 0 {
+ h.gs.rp[0] = i
+ }
+
+ // Move the point.
+ oldDist = dotProduct(f26dot6(p.X-ref.X), f26dot6(p.Y-ref.Y), h.gs.pv)
+ h.move(p, distance-oldDist, true)
+
+ } else {
+ // MIRPxxxxx opcode.
+
+ top -= 2
+ i := h.stack[top]
+ cvtDist := h.getScaledCVT(h.stack[top+1])
+ if (cvtDist - h.gs.singleWidth).abs() < h.gs.singleWidthCutIn {
+ if cvtDist >= 0 {
+ cvtDist = +h.gs.singleWidth
+ } else {
+ cvtDist = -h.gs.singleWidth
+ }
+ }
+
+ if h.gs.zp[1] == 0 {
+ // TODO: implement once we have a .ttf file that triggers
+ // this, so that we can step through C's freetype.
+ return errors.New("truetype: hinting: unimplemented twilight point adjustment")
+ }
+
+ ref := h.point(0, unhinted, h.gs.rp[0])
+ p := h.point(1, unhinted, i)
+ if ref == nil || p == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ oldDist := dotProduct(f26dot6(p.X-ref.X), f26dot6(p.Y-ref.Y), h.gs.dv)
+
+ ref = h.point(0, current, h.gs.rp[0])
+ p = h.point(1, current, i)
+ if ref == nil || p == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ curDist := dotProduct(f26dot6(p.X-ref.X), f26dot6(p.Y-ref.Y), h.gs.pv)
+
+ if h.gs.autoFlip && oldDist^cvtDist < 0 {
+ cvtDist = -cvtDist
+ }
+
+ // Rounding bit.
+ // TODO: metrics compensation.
+ distance := cvtDist
+ if opcode&0x04 != 0 {
+ // The CVT value is only used if close enough to oldDist.
+ if (h.gs.zp[0] == h.gs.zp[1]) &&
+ ((cvtDist - oldDist).abs() > h.gs.controlValueCutIn) {
+
+ distance = oldDist
+ }
+ distance = h.round(distance)
+ }
+
+ // Minimum distance bit.
+ if opcode&0x08 != 0 {
+ if oldDist >= 0 {
+ if distance < h.gs.minDist {
+ distance = h.gs.minDist
+ }
+ } else {
+ if distance > -h.gs.minDist {
+ distance = -h.gs.minDist
+ }
+ }
+ }
+
+ // Set-RP0 bit.
+ h.gs.rp[1] = h.gs.rp[0]
+ h.gs.rp[2] = i
+ if opcode&0x10 != 0 {
+ h.gs.rp[0] = i
+ }
+
+ // Move the point.
+ h.move(p, distance-curDist, true)
+ }
+ }
+ pc++
+ continue
+
+ ifelse:
+ // Skip past bytecode until the next ELSE (if opcode == 0) or the
+ // next EIF (for all opcodes). Opcode == 0 means that we have come
+ // from an IF. Opcode == 1 means that we have come from an ELSE.
+ {
+ ifelseloop:
+ for depth := 0; ; {
+ pc++
+ if pc >= len(program) {
+ return errors.New("truetype: hinting: unbalanced IF or ELSE")
+ }
+ switch program[pc] {
+ case opIF:
+ depth++
+ case opELSE:
+ if depth == 0 && opcode == 0 {
+ break ifelseloop
+ }
+ case opEIF:
+ depth--
+ if depth < 0 {
+ break ifelseloop
+ }
+ default:
+ var ok bool
+ pc, ok = skipInstructionPayload(program, pc)
+ if !ok {
+ return errors.New("truetype: hinting: unbalanced IF or ELSE")
+ }
+ }
+ }
+ pc++
+ continue
+ }
+
+ push:
+ // Push n elements from the program to the stack, where n is the low 7 bits of
+ // opcode. If the low 7 bits are zero, then n is the next byte from the program.
+ // The high bit being 0 means that the elements are zero-extended bytes.
+ // The high bit being 1 means that the elements are sign-extended words.
+ {
+ width := 1
+ if opcode&0x80 != 0 {
+ opcode &^= 0x80
+ width = 2
+ }
+ if opcode == 0 {
+ pc++
+ if pc >= len(program) {
+ return errors.New("truetype: hinting: insufficient data")
+ }
+ opcode = program[pc]
+ }
+ pc++
+ if top+int(opcode) > len(h.stack) {
+ return errors.New("truetype: hinting: stack overflow")
+ }
+ if pc+width*int(opcode) > len(program) {
+ return errors.New("truetype: hinting: insufficient data")
+ }
+ for ; opcode > 0; opcode-- {
+ if width == 1 {
+ h.stack[top] = int32(program[pc])
+ } else {
+ h.stack[top] = int32(int8(program[pc]))<<8 | int32(program[pc+1])
+ }
+ top++
+ pc += width
+ }
+ continue
+ }
+
+ delta:
+ {
+ if opcode >= opDELTAC1 && !h.scaledCVTInitialized {
+ h.initializeScaledCVT()
+ }
+ top--
+ n := h.stack[top]
+ if int32(top) < 2*n {
+ return errors.New("truetype: hinting: stack underflow")
+ }
+ for ; n > 0; n-- {
+ top -= 2
+ b := h.stack[top]
+ c := (b & 0xf0) >> 4
+ switch opcode {
+ case opDELTAP2, opDELTAC2:
+ c += 16
+ case opDELTAP3, opDELTAC3:
+ c += 32
+ }
+ c += h.gs.deltaBase
+ if ppem := (h.scale + 1<<5) >> 6; ppem != c {
+ continue
+ }
+ b = (b & 0x0f) - 8
+ if b >= 0 {
+ b++
+ }
+ b = b * 64 / (1 << uint32(h.gs.deltaShift))
+ if opcode >= opDELTAC1 {
+ a := h.stack[top+1]
+ if a < 0 || len(h.scaledCVT) <= int(a) {
+ return errors.New("truetype: hinting: index out of range")
+ }
+ h.scaledCVT[a] += f26dot6(b)
+ } else {
+ p := h.point(0, current, h.stack[top+1])
+ if p == nil {
+ return errors.New("truetype: hinting: point out of range")
+ }
+ h.move(p, f26dot6(b), true)
+ }
+ }
+ pc++
+ continue
+ }
+ }
+ return nil
+}
+
+func (h *hinter) initializeScaledCVT() {
+ h.scaledCVTInitialized = true
+ if n := len(h.font.cvt) / 2; n <= cap(h.scaledCVT) {
+ h.scaledCVT = h.scaledCVT[:n]
+ } else {
+ if n < 32 {
+ n = 32
+ }
+ h.scaledCVT = make([]f26dot6, len(h.font.cvt)/2, n)
+ }
+ 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))))
+ }
+}
+
+// getScaledCVT returns the scaled value from the font's Control Value Table.
+func (h *hinter) getScaledCVT(i int32) f26dot6 {
+ if !h.scaledCVTInitialized {
+ h.initializeScaledCVT()
+ }
+ if i < 0 || len(h.scaledCVT) <= int(i) {
+ return 0
+ }
+ return h.scaledCVT[i]
+}
+
+// setScaledCVT overrides the scaled value from the font's Control Value Table.
+func (h *hinter) setScaledCVT(i int32, v f26dot6) {
+ if !h.scaledCVTInitialized {
+ h.initializeScaledCVT()
+ }
+ if i < 0 || len(h.scaledCVT) <= int(i) {
+ return
+ }
+ h.scaledCVT[i] = v
+}
+
+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) 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)
+ if touch {
+ p.Flags |= flagTouchedX
+ }
+ return
+ }
+
+ fvy := int64(h.gs.fv[1])
+ pvy := int64(h.gs.pv[1])
+ if fvy == 0x4000 && pvy == 0x4000 {
+ p.Y += int32(distance)
+ if touch {
+ p.Flags |= flagTouchedY
+ }
+ return
+ }
+
+ fvDotPv := (fvx*pvx + fvy*pvy) >> 14
+
+ if fvx != 0 {
+ p.X += int32(mulDiv(fvx, int64(distance), fvDotPv))
+ if touch {
+ p.Flags |= flagTouchedX
+ }
+ }
+
+ if fvy != 0 {
+ p.Y += int32(mulDiv(fvy, int64(distance), fvDotPv))
+ if touch {
+ p.Flags |= flagTouchedY
+ }
+ }
+}
+
+func (h *hinter) iupInterp(interpY bool, p1, p2, ref1, ref2 int) {
+ if p1 > p2 {
+ return
+ }
+ if ref1 >= len(h.points[glyphZone][current]) ||
+ ref2 >= len(h.points[glyphZone][current]) {
+ return
+ }
+
+ var ifu1, ifu2 int32
+ if interpY {
+ ifu1 = h.points[glyphZone][inFontUnits][ref1].Y
+ ifu2 = h.points[glyphZone][inFontUnits][ref2].Y
+ } else {
+ ifu1 = h.points[glyphZone][inFontUnits][ref1].X
+ ifu2 = h.points[glyphZone][inFontUnits][ref2].X
+ }
+ if ifu1 > ifu2 {
+ ifu1, ifu2 = ifu2, ifu1
+ ref1, ref2 = ref2, ref1
+ }
+
+ var unh1, unh2, delta1, delta2 int32
+ if interpY {
+ 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.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.points[glyphZone][unhinted][i].Y
+ } else {
+ xy = h.points[glyphZone][unhinted][i].X
+ }
+
+ if xy <= unh1 {
+ xy += delta1
+ } else {
+ xy += delta2
+ }
+
+ if interpY {
+ h.points[glyphZone][current][i].Y = xy
+ } else {
+ h.points[glyphZone][current][i].X = xy
+ }
+ }
+ return
+ }
+
+ scale, scaleOK := int64(0), false
+ for i := p1; i <= p2; i++ {
+ if interpY {
+ xy = h.points[glyphZone][unhinted][i].Y
+ ifuXY = h.points[glyphZone][inFontUnits][i].Y
+ } else {
+ xy = h.points[glyphZone][unhinted][i].X
+ ifuXY = h.points[glyphZone][inFontUnits][i].X
+ }
+
+ if xy <= unh1 {
+ xy += delta1
+ } else if xy >= unh2 {
+ xy += delta2
+ } else {
+ if !scaleOK {
+ scaleOK = true
+ scale = mulDiv(int64(unh2+delta2-unh1-delta1), 0x10000, int64(ifu2-ifu1))
+ }
+ numer := int64(ifuXY-ifu1) * scale
+ if numer >= 0 {
+ numer += 0x8000
+ } else {
+ numer -= 0x8000
+ }
+ xy = unh1 + delta1 + int32(numer/0x10000)
+ }
+
+ if interpY {
+ h.points[glyphZone][current][i].Y = xy
+ } else {
+ h.points[glyphZone][current][i].X = xy
+ }
+ }
+}
+
+func (h *hinter) iupShift(interpY bool, p1, p2, p int) {
+ var delta int32
+ if interpY {
+ delta = h.points[glyphZone][current][p].Y - h.points[glyphZone][unhinted][p].Y
+ } else {
+ delta = h.points[glyphZone][current][p].X - h.points[glyphZone][unhinted][p].X
+ }
+ if delta == 0 {
+ return
+ }
+ for i := p1; i < p2; i++ {
+ if i == p {
+ continue
+ }
+ if interpY {
+ h.points[glyphZone][current][i].Y += delta
+ } else {
+ h.points[glyphZone][current][i].X += delta
+ }
+ }
+}
+
+func (h *hinter) displacement(useZP1 bool) (zonePointer uint32, i int32, d f26dot6, ok bool) {
+ zonePointer, i = uint32(0), h.gs.rp[1]
+ if useZP1 {
+ zonePointer, i = 1, h.gs.rp[2]
+ }
+ p := h.point(zonePointer, current, i)
+ q := h.point(zonePointer, unhinted, i)
+ if p == nil || q == nil {
+ return 0, 0, 0, false
+ }
+ d = dotProduct(f26dot6(p.X-q.X), f26dot6(p.Y-q.Y), h.gs.pv)
+ return zonePointer, i, d, true
+}
+
+// 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) {
+ switch program[pc] {
+ case opNPUSHB:
+ pc++
+ if pc >= len(program) {
+ return 0, false
+ }
+ pc += int(program[pc])
+ case opNPUSHW:
+ pc++
+ if pc >= len(program) {
+ return 0, false
+ }
+ pc += 2 * int(program[pc])
+ case opPUSHB000, opPUSHB001, opPUSHB010, opPUSHB011,
+ opPUSHB100, opPUSHB101, opPUSHB110, opPUSHB111:
+ pc += int(program[pc] - (opPUSHB000 - 1))
+ case opPUSHW000, opPUSHW001, opPUSHW010, opPUSHW011,
+ opPUSHW100, opPUSHW101, opPUSHW110, opPUSHW111:
+ pc += 2 * int(program[pc]-(opPUSHW000-1))
+ }
+ return pc, true
+}
+
+// f2dot14 is a 2.14 fixed point number.
+type f2dot14 int16
+
+func normalize(x, y f2dot14) [2]f2dot14 {
+ fx, fy := float64(x), float64(y)
+ l := 0x4000 / math.Hypot(fx, fy)
+ fx *= l
+ if fx >= 0 {
+ fx += 0.5
+ } else {
+ fx -= 0.5
+ }
+ fy *= l
+ if fy >= 0 {
+ fy += 0.5
+ } else {
+ fy -= 0.5
+ }
+ return [2]f2dot14{f2dot14(fx), f2dot14(fy)}
+}
+
+// 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))
+}
+
+// mul returns x*y in 26.6 fixed point arithmetic.
+func (x f26dot6) mul(y f26dot6) f26dot6 {
+ return f26dot6((int64(x)*int64(y) + 1<<5) >> 6)
+}
+
+// dotProduct returns the dot product of [x, y] and q. It is almost the same as
+// px := int64(x)
+// py := int64(y)
+// qx := int64(q[0])
+// qy := int64(q[1])
+// return f26dot6((px*qx + py*qy + 1<<13) >> 14)
+// except that the computation is done with 32-bit integers to produce exactly
+// the same rounding behavior as C Freetype.
+func dotProduct(x, y f26dot6, q [2]f2dot14) f26dot6 {
+ // Compute x*q[0] as 64-bit value.
+ l := uint32((int32(x) & 0xFFFF) * int32(q[0]))
+ m := (int32(x) >> 16) * int32(q[0])
+
+ lo1 := l + (uint32(m) << 16)
+ hi1 := (m >> 16) + (int32(l) >> 31) + bool2int32(lo1 < l)
+
+ // Compute y*q[1] as 64-bit value.
+ l = uint32((int32(y) & 0xFFFF) * int32(q[1]))
+ m = (int32(y) >> 16) * int32(q[1])
+
+ lo2 := l + (uint32(m) << 16)
+ hi2 := (m >> 16) + (int32(l) >> 31) + bool2int32(lo2 < l)
+
+ // Add them.
+ lo := lo1 + lo2
+ hi := hi1 + hi2 + bool2int32(lo < lo1)
+
+ // Divide the result by 2^14 with rounding.
+ s := hi >> 31
+ l = lo + uint32(s)
+ hi += s + bool2int32(l < lo)
+ lo = l
+
+ l = lo + 0x2000
+ hi += bool2int32(l < lo)
+
+ return f26dot6((uint32(hi) << 18) | (l >> 14))
+}
+
+// mulDiv returns x*y/z, rounded to the nearest integer.
+func mulDiv(x, y, z int64) int64 {
+ xy := x * y
+ if z < 0 {
+ xy, z = -xy, -z
+ }
+ if xy >= 0 {
+ xy += z / 2
+ } else {
+ xy -= z / 2
+ }
+ return xy / z
+}
+
+// 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 {
+ if h.gs.roundPeriod == 0 {
+ // Rounding is off.
+ return x
+ }
+ if x >= 0 {
+ ret := x - h.gs.roundPhase + h.gs.roundThreshold
+ if h.gs.roundSuper45 {
+ ret /= h.gs.roundPeriod
+ ret *= h.gs.roundPeriod
+ } else {
+ ret &= -h.gs.roundPeriod
+ }
+ if x != 0 && ret < 0 {
+ ret = 0
+ }
+ return ret + h.gs.roundPhase
+ }
+ ret := -x - h.gs.roundPhase + h.gs.roundThreshold
+ if h.gs.roundSuper45 {
+ ret /= h.gs.roundPeriod
+ ret *= h.gs.roundPeriod
+ } else {
+ ret &= -h.gs.roundPeriod
+ }
+ if ret < 0 {
+ ret = 0
+ }
+ return -ret - h.gs.roundPhase
+}
+
+func bool2int32(b bool) int32 {
+ if b {
+ return 1
+ }
+ return 0
+}
diff --git a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/hint_test.go b/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/hint_test.go
new file mode 100644
index 000000000..c8b8d604d
--- /dev/null
+++ b/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/hint_test.go
@@ -0,0 +1,673 @@
+// Copyright 2012 The Freetype-Go Authors. All rights reserved.
+// Use of this source code is governed by your choice of either the
+// FreeType License or the GNU General Public License version 2 (or
+// any later version), both of which can be found in the LICENSE file.
+
+package truetype
+
+import (
+ "reflect"
+ "strings"
+ "testing"
+)
+
+func TestBytecode(t *testing.T) {
+ testCases := []struct {
+ desc string
+ prog []byte
+ want []int32
+ errStr string
+ }{
+ {
+ "underflow",
+ []byte{
+ opDUP,
+ },
+ nil,
+ "underflow",
+ },
+ {
+ "infinite loop",
+ []byte{
+ opPUSHW000, // [-1]
+ 0xff,
+ 0xff,
+ opDUP, // [-1, -1]
+ opJMPR, // [-1]
+ },
+ nil,
+ "too many steps",
+ },
+ {
+ "unbalanced if/else",
+ []byte{
+ opPUSHB000, // [0]
+ 0,
+ opIF,
+ },
+ nil,
+ "unbalanced",
+ },
+ {
+ "vector set/gets",
+ []byte{
+ opSVTCA1, // []
+ opGPV, // [0x4000, 0]
+ opSVTCA0, // [0x4000, 0]
+ opGFV, // [0x4000, 0, 0, 0x4000]
+ opNEG, // [0x4000, 0, 0, -0x4000]
+ opSPVFS, // [0x4000, 0]
+ opSFVTPV, // [0x4000, 0]
+ opPUSHB000, // [0x4000, 0, 1]
+ 1,
+ opGFV, // [0x4000, 0, 1, 0, -0x4000]
+ opPUSHB000, // [0x4000, 0, 1, 0, -0x4000, 2]
+ 2,
+ },
+ []int32{0x4000, 0, 1, 0, -0x4000, 2},
+ "",
+ },
+ {
+ "jumps",
+ []byte{
+ opPUSHB001, // [10, 2]
+ 10,
+ 2,
+ opJMPR, // [10]
+ opDUP, // not executed
+ opDUP, // [10, 10]
+ opPUSHB010, // [10, 10, 20, 2, 1]
+ 20,
+ 2,
+ 1,
+ opJROT, // [10, 10, 20]
+ opDUP, // not executed
+ opDUP, // [10, 10, 20, 20]
+ opPUSHB010, // [10, 10, 20, 20, 30, 2, 1]
+ 30,
+ 2,
+ 1,
+ opJROF, // [10, 10, 20, 20, 30]
+ opDUP, // [10, 10, 20, 20, 30, 30]
+ opDUP, // [10, 10, 20, 20, 30, 30, 30]
+ },
+ []int32{10, 10, 20, 20, 30, 30, 30},
+ "",
+ },
+ {
+ "stack ops",
+ []byte{
+ opPUSHB010, // [10, 20, 30]
+ 10,
+ 20,
+ 30,
+ opCLEAR, // []
+ opPUSHB010, // [40, 50, 60]
+ 40,
+ 50,
+ 60,
+ opSWAP, // [40, 60, 50]
+ opDUP, // [40, 60, 50, 50]
+ opDUP, // [40, 60, 50, 50, 50]
+ opPOP, // [40, 60, 50, 50]
+ opDEPTH, // [40, 60, 50, 50, 4]
+ opCINDEX, // [40, 60, 50, 50, 40]
+ opPUSHB000, // [40, 60, 50, 50, 40, 4]
+ 4,
+ opMINDEX, // [40, 50, 50, 40, 60]
+ },
+ []int32{40, 50, 50, 40, 60},
+ "",
+ },
+ {
+ "push ops",
+ []byte{
+ opPUSHB000, // [255]
+ 255,
+ opPUSHW001, // [255, -2, 253]
+ 255,
+ 254,
+ 0,
+ 253,
+ opNPUSHB, // [1, -2, 253, 1, 2]
+ 2,
+ 1,
+ 2,
+ opNPUSHW, // [1, -2, 253, 1, 2, 0x0405, 0x0607, 0x0809]
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ },
+ []int32{255, -2, 253, 1, 2, 0x0405, 0x0607, 0x0809},
+ "",
+ },
+ {
+ "store ops",
+ []byte{
+ opPUSHB011, // [1, 22, 3, 44]
+ 1,
+ 22,
+ 3,
+ 44,
+ opWS, // [1, 22]
+ opWS, // []
+ opPUSHB000, // [3]
+ 3,
+ opRS, // [44]
+ },
+ []int32{44},
+ "",
+ },
+ {
+ "comparison ops",
+ []byte{
+ opPUSHB001, // [10, 20]
+ 10,
+ 20,
+ opLT, // [1]
+ opPUSHB001, // [1, 10, 20]
+ 10,
+ 20,
+ opLTEQ, // [1, 1]
+ opPUSHB001, // [1, 1, 10, 20]
+ 10,
+ 20,
+ opGT, // [1, 1, 0]
+ opPUSHB001, // [1, 1, 0, 10, 20]
+ 10,
+ 20,
+ opGTEQ, // [1, 1, 0, 0]
+ opEQ, // [1, 1, 1]
+ opNEQ, // [1, 0]
+ },
+ []int32{1, 0},
+ "",
+ },
+ {
+ "odd/even",
+ // Calculate odd(2+31/64), odd(2+32/64), even(2), even(1).
+ []byte{
+ opPUSHB000, // [159]
+ 159,
+ opODD, // [0]
+ opPUSHB000, // [0, 160]
+ 160,
+ opODD, // [0, 1]
+ opPUSHB000, // [0, 1, 128]
+ 128,
+ opEVEN, // [0, 1, 1]
+ opPUSHB000, // [0, 1, 1, 64]
+ 64,
+ opEVEN, // [0, 1, 1, 0]
+ },
+ []int32{0, 1, 1, 0},
+ "",
+ },
+ {
+ "if true",
+ []byte{
+ opPUSHB001, // [255, 1]
+ 255,
+ 1,
+ opIF,
+ opPUSHB000, // [255, 2]
+ 2,
+ opEIF,
+ opPUSHB000, // [255, 2, 254]
+ 254,
+ },
+ []int32{255, 2, 254},
+ "",
+ },
+ {
+ "if false",
+ []byte{
+ opPUSHB001, // [255, 0]
+ 255,
+ 0,
+ opIF,
+ opPUSHB000, // [255]
+ 2,
+ opEIF,
+ opPUSHB000, // [255, 254]
+ 254,
+ },
+ []int32{255, 254},
+ "",
+ },
+ {
+ "if/else true",
+ []byte{
+ opPUSHB000, // [1]
+ 1,
+ opIF,
+ opPUSHB000, // [2]
+ 2,
+ opELSE,
+ opPUSHB000, // not executed
+ 3,
+ opEIF,
+ },
+ []int32{2},
+ "",
+ },
+ {
+ "if/else false",
+ []byte{
+ opPUSHB000, // [0]
+ 0,
+ opIF,
+ opPUSHB000, // not executed
+ 2,
+ opELSE,
+ opPUSHB000, // [3]
+ 3,
+ opEIF,
+ },
+ []int32{3},
+ "",
+ },
+ {
+ "if/else true if/else false",
+ // 0x58 is the opcode for opIF. The literal 0x58s below are pushed data.
+ []byte{
+ opPUSHB010, // [255, 0, 1]
+ 255,
+ 0,
+ 1,
+ opIF,
+ opIF,
+ opPUSHB001, // not executed
+ 0x58,
+ 0x58,
+ opELSE,
+ opPUSHW000, // [255, 0x5858]
+ 0x58,
+ 0x58,
+ opEIF,
+ opELSE,
+ opIF,
+ opNPUSHB, // not executed
+ 3,
+ 0x58,
+ 0x58,
+ 0x58,
+ opELSE,
+ opNPUSHW, // not executed
+ 2,
+ 0x58,
+ 0x58,
+ 0x58,
+ 0x58,
+ opEIF,
+ opEIF,
+ opPUSHB000, // [255, 0x5858, 254]
+ 254,
+ },
+ []int32{255, 0x5858, 254},
+ "",
+ },
+ {
+ "if/else false if/else true",
+ // 0x58 is the opcode for opIF. The literal 0x58s below are pushed data.
+ []byte{
+ opPUSHB010, // [255, 1, 0]
+ 255,
+ 1,
+ 0,
+ opIF,
+ opIF,
+ opPUSHB001, // not executed
+ 0x58,
+ 0x58,
+ opELSE,
+ opPUSHW000, // not executed
+ 0x58,
+ 0x58,
+ opEIF,
+ opELSE,
+ opIF,
+ opNPUSHB, // [255, 0x58, 0x58, 0x58]
+ 3,
+ 0x58,
+ 0x58,
+ 0x58,
+ opELSE,
+ opNPUSHW, // not executed
+ 2,
+ 0x58,
+ 0x58,
+ 0x58,
+ 0x58,
+ opEIF,
+ opEIF,
+ opPUSHB000, // [255, 0x58, 0x58, 0x58, 254]
+ 254,
+ },
+ []int32{255, 0x58, 0x58, 0x58, 254},
+ "",
+ },
+ {
+ "logical ops",
+ []byte{
+ opPUSHB010, // [0, 10, 20]
+ 0,
+ 10,
+ 20,
+ opAND, // [0, 1]
+ opOR, // [1]
+ opNOT, // [0]
+ },
+ []int32{0},
+ "",
+ },
+ {
+ "arithmetic ops",
+ // Calculate abs((-(1 - (2*3)))/2 + 1/64).
+ // The answer is 5/2 + 1/64 in ideal numbers, or 161 in 26.6 fixed point math.
+ []byte{
+ opPUSHB010, // [64, 128, 192]
+ 1 << 6,
+ 2 << 6,
+ 3 << 6,
+ opMUL, // [64, 384]
+ opSUB, // [-320]
+ opNEG, // [320]
+ opPUSHB000, // [320, 128]
+ 2 << 6,
+ opDIV, // [160]
+ opPUSHB000, // [160, 1]
+ 1,
+ opADD, // [161]
+ opABS, // [161]
+ },
+ []int32{161},
+ "",
+ },
+ {
+ "floor, ceiling",
+ []byte{
+ opPUSHB000, // [96]
+ 96,
+ opFLOOR, // [64]
+ opPUSHB000, // [64, 96]
+ 96,
+ opCEILING, // [64, 128]
+ },
+ []int32{64, 128},
+ "",
+ },
+ {
+ "rounding",
+ // Round 1.40625 (which is 90/64) under various rounding policies.
+ // See figure 20 of https://developer.apple.com/fonts/TTRefMan/RM02/Chap2.html#rounding
+ []byte{
+ opROFF, // []
+ opPUSHB000, // [90]
+ 90,
+ opROUND00, // [90]
+ opRTG, // [90]
+ opPUSHB000, // [90, 90]
+ 90,
+ opROUND00, // [90, 64]
+ opRTHG, // [90, 64]
+ opPUSHB000, // [90, 64, 90]
+ 90,
+ opROUND00, // [90, 64, 96]
+ opRDTG, // [90, 64, 96]
+ opPUSHB000, // [90, 64, 96, 90]
+ 90,
+ opROUND00, // [90, 64, 96, 64]
+ opRUTG, // [90, 64, 96, 64]
+ opPUSHB000, // [90, 64, 96, 64, 90]
+ 90,
+ opROUND00, // [90, 64, 96, 64, 128]
+ opRTDG, // [90, 64, 96, 64, 128]
+ opPUSHB000, // [90, 64, 96, 64, 128, 90]
+ 90,
+ opROUND00, // [90, 64, 96, 64, 128, 96]
+ },
+ []int32{90, 64, 96, 64, 128, 96},
+ "",
+ },
+ {
+ "super-rounding",
+ // See figure 20 of https://developer.apple.com/fonts/TTRefMan/RM02/Chap2.html#rounding
+ // and the sign preservation steps of the "Order of rounding operations" section.
+ []byte{
+ opPUSHB000, // [0x58]
+ 0x58,
+ opSROUND, // []
+ opPUSHW000, // [-81]
+ 0xff,
+ 0xaf,
+ opROUND00, // [-80]
+ opPUSHW000, // [-80, -80]
+ 0xff,
+ 0xb0,
+ opROUND00, // [-80, -80]
+ opPUSHW000, // [-80, -80, -17]
+ 0xff,
+ 0xef,
+ opROUND00, // [-80, -80, -16]
+ opPUSHW000, // [-80, -80, -16, -16]
+ 0xff,
+ 0xf0,
+ opROUND00, // [-80, -80, -16, -16]
+ opPUSHB000, // [-80, -80, -16, -16, 0]
+ 0,
+ opROUND00, // [-80, -80, -16, -16, 16]
+ opPUSHB000, // [-80, -80, -16, -16, 16, 16]
+ 16,
+ opROUND00, // [-80, -80, -16, -16, 16, 16]
+ opPUSHB000, // [-80, -80, -16, -16, 16, 16, 47]
+ 47,
+ opROUND00, // [-80, -80, -16, -16, 16, 16, 16]
+ opPUSHB000, // [-80, -80, -16, -16, 16, 16, 16, 48]
+ 48,
+ opROUND00, // [-80, -80, -16, -16, 16, 16, 16, 80]
+ },
+ []int32{-80, -80, -16, -16, 16, 16, 16, 80},
+ "",
+ },
+ {
+ "roll",
+ []byte{
+ opPUSHB010, // [1, 2, 3]
+ 1,
+ 2,
+ 3,
+ opROLL, // [2, 3, 1]
+ },
+ []int32{2, 3, 1},
+ "",
+ },
+ {
+ "max/min",
+ []byte{
+ opPUSHW001, // [-2, -3]
+ 0xff,
+ 0xfe,
+ 0xff,
+ 0xfd,
+ opMAX, // [-2]
+ opPUSHW001, // [-2, -4, -5]
+ 0xff,
+ 0xfc,
+ 0xff,
+ 0xfb,
+ opMIN, // [-2, -5]
+ },
+ []int32{-2, -5},
+ "",
+ },
+ {
+ "functions",
+ []byte{
+ opPUSHB011, // [3, 7, 0, 3]
+ 3,
+ 7,
+ 0,
+ 3,
+
+ opFDEF, // Function #3 (not called)
+ opPUSHB000,
+ 98,
+ opENDF,
+
+ opFDEF, // Function #0
+ opDUP,
+ opADD,
+ opENDF,
+
+ opFDEF, // Function #7
+ opPUSHB001,
+ 10,
+ 0,
+ opCALL,
+ opDUP,
+ opENDF,
+
+ opFDEF, // Function #3 (again)
+ opPUSHB000,
+ 99,
+ opENDF,
+
+ opPUSHB001, // [2, 0]
+ 2,
+ 0,
+ opCALL, // [4]
+ opPUSHB000, // [4, 3]
+ 3,
+ opLOOPCALL, // [99, 99, 99, 99]
+ opPUSHB000, // [99, 99, 99, 99, 7]
+ 7,
+ opCALL, // [99, 99, 99, 99, 20, 20]
+ },
+ []int32{99, 99, 99, 99, 20, 20},
+ "",
+ },
+ }
+
+ for _, tc := range testCases {
+ h := &hinter{}
+ h.init(&Font{
+ maxStorage: 32,
+ maxStackElements: 100,
+ }, 768)
+ err, errStr := h.run(tc.prog, nil, nil, nil, nil), ""
+ if err != nil {
+ errStr = err.Error()
+ }
+ if tc.errStr != "" {
+ if errStr == "" {
+ t.Errorf("%s: got no error, want %q", tc.desc, tc.errStr)
+ } else if !strings.Contains(errStr, tc.errStr) {
+ t.Errorf("%s: got error %q, want one containing %q", tc.desc, errStr, tc.errStr)
+ }
+ continue
+ }
+ if errStr != "" {
+ t.Errorf("%s: got error %q, want none", tc.desc, errStr)
+ continue
+ }
+ got := h.stack[:len(tc.want)]
+ if !reflect.DeepEqual(got, tc.want) {
+ t.Errorf("%s: got %v, want %v", tc.desc, got, tc.want)
+ continue
+ }
+ }
+}
+
+// TestMove tests that the hinter.move method matches the output of the C
+// Freetype implementation.
+func TestMove(t *testing.T) {
+ h, p := hinter{}, Point{}
+ testCases := []struct {
+ pvX, pvY, fvX, fvY f2dot14
+ wantX, wantY int32
+ }{
+ {+0x4000, +0x0000, +0x4000, +0x0000, +1000, +0},
+ {+0x4000, +0x0000, -0x4000, +0x0000, +1000, +0},
+ {-0x4000, +0x0000, +0x4000, +0x0000, -1000, +0},
+ {-0x4000, +0x0000, -0x4000, +0x0000, -1000, +0},
+ {+0x0000, +0x4000, +0x0000, +0x4000, +0, +1000},
+ {+0x0000, +0x4000, +0x0000, -0x4000, +0, +1000},
+ {+0x4000, +0x0000, +0x2d41, +0x2d41, +1000, +1000},
+ {+0x4000, +0x0000, -0x2d41, +0x2d41, +1000, -1000},
+ {+0x4000, +0x0000, +0x2d41, -0x2d41, +1000, -1000},
+ {+0x4000, +0x0000, -0x2d41, -0x2d41, +1000, +1000},
+ {-0x4000, +0x0000, +0x2d41, +0x2d41, -1000, -1000},
+ {-0x4000, +0x0000, -0x2d41, +0x2d41, -1000, +1000},
+ {-0x4000, +0x0000, +0x2d41, -0x2d41, -1000, +1000},
+ {-0x4000, +0x0000, -0x2d41, -0x2d41, -1000, -1000},
+ {+0x376d, +0x2000, +0x2d41, +0x2d41, +732, +732},
+ {-0x376d, +0x2000, +0x2d41, +0x2d41, -2732, -2732},
+ {+0x376d, +0x2000, +0x2d41, -0x2d41, +2732, -2732},
+ {-0x376d, +0x2000, +0x2d41, -0x2d41, -732, +732},
+ {-0x376d, -0x2000, +0x2d41, +0x2d41, -732, -732},
+ {+0x376d, +0x2000, +0x4000, +0x0000, +1155, +0},
+ {+0x376d, +0x2000, +0x0000, +0x4000, +0, +2000},
+ }
+ for _, tc := range testCases {
+ p = Point{}
+ h.gs.pv = [2]f2dot14{tc.pvX, tc.pvY}
+ h.gs.fv = [2]f2dot14{tc.fvX, tc.fvY}
+ h.move(&p, 1000, true)
+ tx := p.Flags&flagTouchedX != 0
+ ty := p.Flags&flagTouchedY != 0
+ wantTX := tc.fvX != 0
+ wantTY := tc.fvY != 0
+ if p.X != tc.wantX || p.Y != tc.wantY || tx != wantTX || ty != wantTY {
+ t.Errorf("pv=%v, fv=%v\ngot %d, %d, %t, %t\nwant %d, %d, %t, %t",
+ h.gs.pv, h.gs.fv, p.X, p.Y, tx, ty, tc.wantX, tc.wantY, wantTX, wantTY)
+ continue
+ }
+
+ // Check that p is aligned with the freedom vector.
+ a := int64(p.X) * int64(tc.fvY)
+ b := int64(p.Y) * int64(tc.fvX)
+ if a != b {
+ t.Errorf("pv=%v, fv=%v, p=%v not aligned with fv", h.gs.pv, h.gs.fv, p)
+ continue
+ }
+
+ // Check that the projected p is 1000 away from the origin.
+ dotProd := (int64(p.X)*int64(tc.pvX) + int64(p.Y)*int64(tc.pvY) + 1<<13) >> 14
+ if dotProd != 1000 {
+ t.Errorf("pv=%v, fv=%v, p=%v not 1000 from origin", h.gs.pv, h.gs.fv, p)
+ continue
+ }
+ }
+}
+
+// TestNormalize tests that the normalize function matches the output of the C
+// Freetype implementation.
+func TestNormalize(t *testing.T) {
+ testCases := [][2]f2dot14{
+ {-15895, 3974},
+ {-15543, 5181},
+ {-14654, 7327},
+ {-11585, 11585},
+ {0, 16384},
+ {11585, 11585},
+ {14654, 7327},
+ {15543, 5181},
+ {15895, 3974},
+ {16066, 3213},
+ {16161, 2694},
+ {16219, 2317},
+ {16257, 2032},
+ {16284, 1809},
+ }
+ for i, want := range testCases {
+ got := normalize(f2dot14(i)-4, 1)
+ if got != want {
+ t.Errorf("i=%d: got %v, want %v", i, got, want)
+ }
+ }
+}
diff --git a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/opcodes.go b/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/opcodes.go
new file mode 100644
index 000000000..1880e1e63
--- /dev/null
+++ b/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/opcodes.go
@@ -0,0 +1,289 @@
+// Copyright 2012 The Freetype-Go Authors. All rights reserved.
+// Use of this source code is governed by your choice of either the
+// FreeType License or the GNU General Public License version 2 (or
+// any later version), both of which can be found in the LICENSE file.
+
+package truetype
+
+// The Truetype opcodes are summarized at
+// https://developer.apple.com/fonts/TTRefMan/RM07/appendixA.html
+
+const (
+ opSVTCA0 = 0x00 // Set freedom and projection Vectors To Coordinate Axis
+ opSVTCA1 = 0x01 // .
+ opSPVTCA0 = 0x02 // Set Projection Vector To Coordinate Axis
+ opSPVTCA1 = 0x03 // .
+ opSFVTCA0 = 0x04 // Set Freedom Vector to Coordinate Axis
+ opSFVTCA1 = 0x05 // .
+ opSPVTL0 = 0x06 // Set Projection Vector To Line
+ opSPVTL1 = 0x07 // .
+ opSFVTL0 = 0x08 // Set Freedom Vector To Line
+ opSFVTL1 = 0x09 // .
+ opSPVFS = 0x0a // Set Projection Vector From Stack
+ opSFVFS = 0x0b // Set Freedom Vector From Stack
+ opGPV = 0x0c // Get Projection Vector
+ opGFV = 0x0d // Get Freedom Vector
+ opSFVTPV = 0x0e // Set Freedom Vector To Projection Vector
+ opISECT = 0x0f // moves point p to the InterSECTion of two lines
+ opSRP0 = 0x10 // Set Reference Point 0
+ opSRP1 = 0x11 // Set Reference Point 1
+ opSRP2 = 0x12 // Set Reference Point 2
+ opSZP0 = 0x13 // Set Zone Pointer 0
+ opSZP1 = 0x14 // Set Zone Pointer 1
+ opSZP2 = 0x15 // Set Zone Pointer 2
+ opSZPS = 0x16 // Set Zone PointerS
+ opSLOOP = 0x17 // Set LOOP variable
+ opRTG = 0x18 // Round To Grid
+ opRTHG = 0x19 // Round To Half Grid
+ opSMD = 0x1a // Set Minimum Distance
+ opELSE = 0x1b // ELSE clause
+ opJMPR = 0x1c // JuMP Relative
+ opSCVTCI = 0x1d // Set Control Value Table Cut-In
+ opSSWCI = 0x1e // Set Single Width Cut-In
+ opSSW = 0x1f // Set Single Width
+ opDUP = 0x20 // DUPlicate top stack element
+ opPOP = 0x21 // POP top stack element
+ opCLEAR = 0x22 // CLEAR the stack
+ opSWAP = 0x23 // SWAP the top two elements on the stack
+ opDEPTH = 0x24 // DEPTH of the stack
+ opCINDEX = 0x25 // Copy the INDEXed element to the top of the stack
+ opMINDEX = 0x26 // Move the INDEXed element to the top of the stack
+ opALIGNPTS = 0x27 // ALIGN PoinTS
+ op_0x28 = 0x28 // deprecated
+ opUTP = 0x29 // UnTouch Point
+ opLOOPCALL = 0x2a // LOOP and CALL function
+ opCALL = 0x2b // CALL function
+ opFDEF = 0x2c // Function DEFinition
+ opENDF = 0x2d // END Function definition
+ opMDAP0 = 0x2e // Move Direct Absolute Point
+ opMDAP1 = 0x2f // .
+ opIUP0 = 0x30 // Interpolate Untouched Points through the outline
+ opIUP1 = 0x31 // .
+ opSHP0 = 0x32 // SHift Point using reference point
+ opSHP1 = 0x33 // .
+ opSHC0 = 0x34 // SHift Contour using reference point
+ opSHC1 = 0x35 // .
+ opSHZ0 = 0x36 // SHift Zone using reference point
+ opSHZ1 = 0x37 // .
+ opSHPIX = 0x38 // SHift point by a PIXel amount
+ opIP = 0x39 // Interpolate Point
+ opMSIRP0 = 0x3a // Move Stack Indirect Relative Point
+ opMSIRP1 = 0x3b // .
+ opALIGNRP = 0x3c // ALIGN to Reference Point
+ opRTDG = 0x3d // Round To Double Grid
+ opMIAP0 = 0x3e // Move Indirect Absolute Point
+ opMIAP1 = 0x3f // .
+ opNPUSHB = 0x40 // PUSH N Bytes
+ opNPUSHW = 0x41 // PUSH N Words
+ opWS = 0x42 // Write Store
+ opRS = 0x43 // Read Store
+ opWCVTP = 0x44 // Write Control Value Table in Pixel units
+ opRCVT = 0x45 // Read Control Value Table entry
+ opGC0 = 0x46 // Get Coordinate projected onto the projection vector
+ opGC1 = 0x47 // .
+ opSCFS = 0x48 // Sets Coordinate From the Stack using projection vector and freedom vector
+ opMD0 = 0x49 // Measure Distance
+ opMD1 = 0x4a // .
+ opMPPEM = 0x4b // Measure Pixels Per EM
+ opMPS = 0x4c // Measure Point Size
+ opFLIPON = 0x4d // set the auto FLIP Boolean to ON
+ opFLIPOFF = 0x4e // set the auto FLIP Boolean to OFF
+ opDEBUG = 0x4f // DEBUG call
+ opLT = 0x50 // Less Than
+ opLTEQ = 0x51 // Less Than or EQual
+ opGT = 0x52 // Greater Than
+ opGTEQ = 0x53 // Greater Than or EQual
+ opEQ = 0x54 // EQual
+ opNEQ = 0x55 // Not EQual
+ opODD = 0x56 // ODD
+ opEVEN = 0x57 // EVEN
+ opIF = 0x58 // IF test
+ opEIF = 0x59 // End IF
+ opAND = 0x5a // logical AND
+ opOR = 0x5b // logical OR
+ opNOT = 0x5c // logical NOT
+ opDELTAP1 = 0x5d // DELTA exception P1
+ opSDB = 0x5e // Set Delta Base in the graphics state
+ opSDS = 0x5f // Set Delta Shift in the graphics state
+ opADD = 0x60 // ADD
+ opSUB = 0x61 // SUBtract
+ opDIV = 0x62 // DIVide
+ opMUL = 0x63 // MULtiply
+ opABS = 0x64 // ABSolute value
+ opNEG = 0x65 // NEGate
+ opFLOOR = 0x66 // FLOOR
+ opCEILING = 0x67 // CEILING
+ opROUND00 = 0x68 // ROUND value
+ opROUND01 = 0x69 // .
+ opROUND10 = 0x6a // .
+ opROUND11 = 0x6b // .
+ opNROUND00 = 0x6c // No ROUNDing of value
+ opNROUND01 = 0x6d // .
+ opNROUND10 = 0x6e // .
+ opNROUND11 = 0x6f // .
+ opWCVTF = 0x70 // Write Control Value Table in Funits
+ opDELTAP2 = 0x71 // DELTA exception P2
+ opDELTAP3 = 0x72 // DELTA exception P3
+ opDELTAC1 = 0x73 // DELTA exception C1
+ opDELTAC2 = 0x74 // DELTA exception C2
+ opDELTAC3 = 0x75 // DELTA exception C3
+ opSROUND = 0x76 // Super ROUND
+ opS45ROUND = 0x77 // Super ROUND 45 degrees
+ opJROT = 0x78 // Jump Relative On True
+ opJROF = 0x79 // Jump Relative On False
+ opROFF = 0x7a // Round OFF
+ op_0x7b = 0x7b // deprecated
+ opRUTG = 0x7c // Round Up To Grid
+ opRDTG = 0x7d // Round Down To Grid
+ opSANGW = 0x7e // Set ANGle Weight
+ opAA = 0x7f // Adjust Angle
+ opFLIPPT = 0x80 // FLIP PoinT
+ opFLIPRGON = 0x81 // FLIP RanGe ON
+ opFLIPRGOFF = 0x82 // FLIP RanGe OFF
+ op_0x83 = 0x83 // deprecated
+ op_0x84 = 0x84 // deprecated
+ opSCANCTRL = 0x85 // SCAN conversion ConTRoL
+ opSDPVTL0 = 0x86 // Set Dual Projection Vector To Line
+ opSDPVTL1 = 0x87 // .
+ opGETINFO = 0x88 // GET INFOrmation
+ opIDEF = 0x89 // Instruction DEFinition
+ opROLL = 0x8a // ROLL the top three stack elements
+ opMAX = 0x8b // MAXimum of top two stack elements
+ opMIN = 0x8c // MINimum of top two stack elements
+ opSCANTYPE = 0x8d // SCANTYPE
+ opINSTCTRL = 0x8e // INSTRuction execution ConTRoL
+ op_0x8f = 0x8f
+ op_0x90 = 0x90
+ op_0x91 = 0x91
+ op_0x92 = 0x92
+ op_0x93 = 0x93
+ op_0x94 = 0x94
+ op_0x95 = 0x95
+ op_0x96 = 0x96
+ op_0x97 = 0x97
+ op_0x98 = 0x98
+ op_0x99 = 0x99
+ op_0x9a = 0x9a
+ op_0x9b = 0x9b
+ op_0x9c = 0x9c
+ op_0x9d = 0x9d
+ op_0x9e = 0x9e
+ op_0x9f = 0x9f
+ op_0xa0 = 0xa0
+ op_0xa1 = 0xa1
+ op_0xa2 = 0xa2
+ op_0xa3 = 0xa3
+ op_0xa4 = 0xa4
+ op_0xa5 = 0xa5
+ op_0xa6 = 0xa6
+ op_0xa7 = 0xa7
+ op_0xa8 = 0xa8
+ op_0xa9 = 0xa9
+ op_0xaa = 0xaa
+ op_0xab = 0xab
+ op_0xac = 0xac
+ op_0xad = 0xad
+ op_0xae = 0xae
+ op_0xaf = 0xaf
+ opPUSHB000 = 0xb0 // PUSH Bytes
+ opPUSHB001 = 0xb1 // .
+ opPUSHB010 = 0xb2 // .
+ opPUSHB011 = 0xb3 // .
+ opPUSHB100 = 0xb4 // .
+ opPUSHB101 = 0xb5 // .
+ opPUSHB110 = 0xb6 // .
+ opPUSHB111 = 0xb7 // .
+ opPUSHW000 = 0xb8 // PUSH Words
+ opPUSHW001 = 0xb9 // .
+ opPUSHW010 = 0xba // .
+ opPUSHW011 = 0xbb // .
+ opPUSHW100 = 0xbc // .
+ opPUSHW101 = 0xbd // .
+ opPUSHW110 = 0xbe // .
+ opPUSHW111 = 0xbf // .
+ 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 // Move Indirect Relative Point
+ opMIRP00001 = 0xe1 // .
+ opMIRP00010 = 0xe2 // .
+ opMIRP00011 = 0xe3 // .
+ opMIRP00100 = 0xe4 // .
+ opMIRP00101 = 0xe5 // .
+ opMIRP00110 = 0xe6 // .
+ opMIRP00111 = 0xe7 // .
+ opMIRP01000 = 0xe8 // .
+ opMIRP01001 = 0xe9 // .
+ opMIRP01010 = 0xea // .
+ opMIRP01011 = 0xeb // .
+ opMIRP01100 = 0xec // .
+ opMIRP01101 = 0xed // .
+ opMIRP01110 = 0xee // .
+ opMIRP01111 = 0xef // .
+ opMIRP10000 = 0xf0 // .
+ opMIRP10001 = 0xf1 // .
+ opMIRP10010 = 0xf2 // .
+ opMIRP10011 = 0xf3 // .
+ opMIRP10100 = 0xf4 // .
+ opMIRP10101 = 0xf5 // .
+ opMIRP10110 = 0xf6 // .
+ opMIRP10111 = 0xf7 // .
+ opMIRP11000 = 0xf8 // .
+ opMIRP11001 = 0xf9 // .
+ opMIRP11010 = 0xfa // .
+ opMIRP11011 = 0xfb // .
+ opMIRP11100 = 0xfc // .
+ opMIRP11101 = 0xfd // .
+ opMIRP11110 = 0xfe // .
+ opMIRP11111 = 0xff // .
+)
+
+// popCount is the number of stack elements that each opcode pops.
+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, 2, 2, 2, 2, 2, 2, 0, 0, 0, 5, // 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, 2, 0, 1, 2, 1, 1, 0, 1, 1, // 0x20 - 0x2f
+ 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 2, 2, 0, 0, 2, 2, // 0x30 - 0x3f
+ 0, 0, 2, 1, 2, 1, 1, 1, 2, 2, 2, 0, 0, 0, 0, 0, // 0x40 - 0x4f
+ 2, 2, 2, 2, 2, 2, 1, 1, 1, 0, 2, 2, 1, 1, 1, 1, // 0x50 - 0x5f
+ 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60 - 0x6f
+ 2, 1, 1, 1, 1, 1, 1, 1, 2, 2, 0, 0, 0, 0, 1, 1, // 0x70 - 0x7f
+ 0, 2, 2, 0, 0, 1, 2, 2, 1, 1, 3, 2, 2, 1, 2, 0, // 0x80 - 0x8f
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x90 - 0x9f
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xa0 - 0xaf
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0xb0 - 0xbf
+ 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
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xe0 - 0xef
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xf0 - 0xff
+}
diff --git a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/truetype.go b/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/truetype.go
new file mode 100644
index 000000000..96ceef547
--- /dev/null
+++ b/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/truetype.go
@@ -0,0 +1,554 @@
+// Copyright 2010 The Freetype-Go Authors. All rights reserved.
+// Use of this source code is governed by your choice of either the
+// FreeType License or the GNU General Public License version 2 (or
+// any later version), both of which can be found in the LICENSE file.
+
+// Package truetype provides a parser for the TTF and TTC file formats.
+// Those formats are documented at http://developer.apple.com/fonts/TTRefMan/
+// and http://www.microsoft.com/typography/otspec/
+//
+// 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.
+//
+// To measure a TrueType font in ideal FUnit space, use scale equal to
+// font.FUnitsPerEm().
+package truetype
+
+import (
+ "fmt"
+)
+
+// An Index is a Font's index of a rune.
+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
+}
+
+// An HMetric holds the horizontal metrics of a single glyph.
+type HMetric struct {
+ AdvanceWidth, LeftSideBearing int32
+}
+
+// A VMetric holds the vertical metrics of a single glyph.
+type VMetric struct {
+ AdvanceHeight, TopSideBearing int32
+}
+
+// A FormatError reports that the input is not a valid TrueType font.
+type FormatError string
+
+func (e FormatError) Error() string {
+ return "freetype: invalid TrueType format: " + string(e)
+}
+
+// An UnsupportedError reports that the input uses a valid but unimplemented
+// TrueType feature.
+type UnsupportedError string
+
+func (e UnsupportedError) Error() string {
+ return "freetype: unsupported TrueType feature: " + string(e)
+}
+
+// u32 returns the big-endian uint32 at b[i:].
+func u32(b []byte, i int) uint32 {
+ return uint32(b[i])<<24 | uint32(b[i+1])<<16 | uint32(b[i+2])<<8 | uint32(b[i+3])
+}
+
+// u16 returns the big-endian uint16 at b[i:].
+func u16(b []byte, i int) uint16 {
+ return uint16(b[i])<<8 | uint16(b[i+1])
+}
+
+// readTable returns a slice of the TTF data given by a table's directory entry.
+func readTable(ttf []byte, offsetLength []byte) ([]byte, error) {
+ offset := int(u32(offsetLength, 0))
+ if offset < 0 {
+ return nil, FormatError(fmt.Sprintf("offset too large: %d", uint32(offset)))
+ }
+ length := int(u32(offsetLength, 4))
+ if length < 0 {
+ return nil, FormatError(fmt.Sprintf("length too large: %d", uint32(length)))
+ }
+ end := offset + length
+ if end < 0 || end > len(ttf) {
+ return nil, FormatError(fmt.Sprintf("offset + length too large: %d", uint32(offset)+uint32(length)))
+ }
+ return ttf[offset:end], nil
+}
+
+const (
+ locaOffsetFormatUnknown int = iota
+ locaOffsetFormatShort
+ locaOffsetFormatLong
+)
+
+// A cm holds a parsed cmap entry.
+type cm struct {
+ start, end, delta, offset uint32
+}
+
+// A Font represents a Truetype font.
+type Font struct {
+ // Tables sliced from the TTF data. The different tables are documented
+ // at http://developer.apple.com/fonts/TTRefMan/RM06/Chap6.html
+ cmap, cvt, fpgm, glyf, hdmx, head, hhea, hmtx, kern, loca, maxp, os2, prep, vmtx []byte
+
+ cmapIndexes []byte
+
+ // Cached values derived from the raw ttf data.
+ cm []cm
+ locaOffsetFormat int
+ nGlyph, nHMetric, nKern int
+ fUnitsPerEm int32
+ bounds Bounds
+ // Values from the maxp section.
+ maxTwilightPoints, maxStorage, maxFunctionDefs, maxStackElements uint16
+}
+
+func (f *Font) parseCmap() error {
+ const (
+ cmapFormat4 = 4
+ cmapFormat12 = 12
+ languageIndependent = 0
+
+ // A 32-bit encoding consists of a most-significant 16-bit Platform ID and a
+ // least-significant 16-bit Platform Specific ID. The magic numbers are
+ // specified at https://www.microsoft.com/typography/otspec/name.htm
+ unicodeEncoding = 0x00000003 // PID = 0 (Unicode), PSID = 3 (Unicode 2.0)
+ microsoftSymbolEncoding = 0x00030000 // PID = 3 (Microsoft), PSID = 0 (Symbol)
+ microsoftUCS2Encoding = 0x00030001 // PID = 3 (Microsoft), PSID = 1 (UCS-2)
+ microsoftUCS4Encoding = 0x0003000a // PID = 3 (Microsoft), PSID = 10 (UCS-4)
+ )
+
+ if len(f.cmap) < 4 {
+ return FormatError("cmap too short")
+ }
+ nsubtab := int(u16(f.cmap, 2))
+ if len(f.cmap) < 8*nsubtab+4 {
+ return FormatError("cmap too short")
+ }
+ offset, found, x := 0, false, 4
+ for i := 0; i < nsubtab; i++ {
+ // We read the 16-bit Platform ID and 16-bit Platform Specific ID as a single uint32.
+ // All values are big-endian.
+ pidPsid, o := u32(f.cmap, x), u32(f.cmap, x+4)
+ x += 8
+ // We prefer the Unicode cmap encoding. Failing to find that, we fall
+ // back onto the Microsoft cmap encoding.
+ if pidPsid == unicodeEncoding {
+ offset, found = int(o), true
+ break
+
+ } else if pidPsid == microsoftSymbolEncoding ||
+ pidPsid == microsoftUCS2Encoding ||
+ pidPsid == microsoftUCS4Encoding {
+
+ offset, found = int(o), true
+ // We don't break out of the for loop, so that Unicode can override Microsoft.
+ }
+ }
+ if !found {
+ return UnsupportedError("cmap encoding")
+ }
+ if offset <= 0 || offset > len(f.cmap) {
+ return FormatError("bad cmap offset")
+ }
+
+ cmapFormat := u16(f.cmap, offset)
+ switch cmapFormat {
+ case cmapFormat4:
+ language := u16(f.cmap, offset+4)
+ if language != languageIndependent {
+ return UnsupportedError(fmt.Sprintf("language: %d", language))
+ }
+ segCountX2 := int(u16(f.cmap, offset+6))
+ if segCountX2%2 == 1 {
+ return FormatError(fmt.Sprintf("bad segCountX2: %d", segCountX2))
+ }
+ segCount := segCountX2 / 2
+ offset += 14
+ f.cm = make([]cm, segCount)
+ for i := 0; i < segCount; i++ {
+ f.cm[i].end = uint32(u16(f.cmap, offset))
+ offset += 2
+ }
+ offset += 2
+ for i := 0; i < segCount; i++ {
+ f.cm[i].start = uint32(u16(f.cmap, offset))
+ offset += 2
+ }
+ for i := 0; i < segCount; i++ {
+ f.cm[i].delta = uint32(u16(f.cmap, offset))
+ offset += 2
+ }
+ for i := 0; i < segCount; i++ {
+ f.cm[i].offset = uint32(u16(f.cmap, offset))
+ offset += 2
+ }
+ f.cmapIndexes = f.cmap[offset:]
+ return nil
+
+ case cmapFormat12:
+ if u16(f.cmap, offset+2) != 0 {
+ return FormatError(fmt.Sprintf("cmap format: % x", f.cmap[offset:offset+4]))
+ }
+ length := u32(f.cmap, offset+4)
+ language := u32(f.cmap, offset+8)
+ if language != languageIndependent {
+ return UnsupportedError(fmt.Sprintf("language: %d", language))
+ }
+ nGroups := u32(f.cmap, offset+12)
+ if length != 12*nGroups+16 {
+ return FormatError("inconsistent cmap length")
+ }
+ offset += 16
+ f.cm = make([]cm, nGroups)
+ for i := uint32(0); i < nGroups; i++ {
+ f.cm[i].start = u32(f.cmap, offset+0)
+ f.cm[i].end = u32(f.cmap, offset+4)
+ f.cm[i].delta = u32(f.cmap, offset+8) - f.cm[i].start
+ offset += 12
+ }
+ return nil
+ }
+ return UnsupportedError(fmt.Sprintf("cmap format: %d", cmapFormat))
+}
+
+func (f *Font) parseHead() error {
+ if len(f.head) != 54 {
+ 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)))
+ switch i := u16(f.head, 50); i {
+ case 0:
+ f.locaOffsetFormat = locaOffsetFormatShort
+ case 1:
+ f.locaOffsetFormat = locaOffsetFormatLong
+ default:
+ return FormatError(fmt.Sprintf("bad indexToLocFormat: %d", i))
+ }
+ return nil
+}
+
+func (f *Font) parseHhea() error {
+ if len(f.hhea) != 36 {
+ return FormatError(fmt.Sprintf("bad hhea length: %d", len(f.hhea)))
+ }
+ f.nHMetric = int(u16(f.hhea, 34))
+ if 4*f.nHMetric+2*(f.nGlyph-f.nHMetric) != len(f.hmtx) {
+ return FormatError(fmt.Sprintf("bad hmtx length: %d", len(f.hmtx)))
+ }
+ return nil
+}
+
+func (f *Font) parseKern() error {
+ // Apple's TrueType documentation (http://developer.apple.com/fonts/TTRefMan/RM06/Chap6kern.html) says:
+ // "Previous versions of the 'kern' table defined both the version and nTables fields in the header
+ // as UInt16 values and not UInt32 values. Use of the older format on the Mac OS is discouraged
+ // (although AAT can sense an old kerning table and still make correct use of it). Microsoft
+ // Windows still uses the older format for the 'kern' table and will not recognize the newer one.
+ // Fonts targeted for the Mac OS only should use the new format; fonts targeted for both the Mac OS
+ // and Windows should use the old format."
+ // Since we expect that almost all fonts aim to be Windows-compatible, we only parse the "older" format,
+ // just like the C Freetype implementation.
+ if len(f.kern) == 0 {
+ if f.nKern != 0 {
+ return FormatError("bad kern table length")
+ }
+ return nil
+ }
+ if len(f.kern) < 18 {
+ return FormatError("kern data too short")
+ }
+ version, offset := u16(f.kern, 0), 2
+ if version != 0 {
+ return UnsupportedError(fmt.Sprintf("kern version: %d", version))
+ }
+ n, offset := u16(f.kern, offset), offset+2
+ if n != 1 {
+ return UnsupportedError(fmt.Sprintf("kern nTables: %d", n))
+ }
+ offset += 2
+ length, offset := int(u16(f.kern, offset)), offset+2
+ coverage, offset := u16(f.kern, offset), offset+2
+ if coverage != 0x0001 {
+ // We only support horizontal kerning.
+ return UnsupportedError(fmt.Sprintf("kern coverage: 0x%04x", coverage))
+ }
+ f.nKern, offset = int(u16(f.kern, offset)), offset+2
+ if 6*f.nKern != length-14 {
+ return FormatError("bad kern table length")
+ }
+ return nil
+}
+
+func (f *Font) parseMaxp() error {
+ if len(f.maxp) != 32 {
+ return FormatError(fmt.Sprintf("bad maxp length: %d", len(f.maxp)))
+ }
+ f.nGlyph = int(u16(f.maxp, 4))
+ f.maxTwilightPoints = u16(f.maxp, 16)
+ f.maxStorage = u16(f.maxp, 18)
+ f.maxFunctionDefs = u16(f.maxp, 20)
+ f.maxStackElements = u16(f.maxp, 24)
+ return nil
+}
+
+// scale returns x divided by f.fUnitsPerEm, rounded to the nearest integer.
+func (f *Font) scale(x int32) int32 {
+ if x >= 0 {
+ x += f.fUnitsPerEm / 2
+ } else {
+ x -= f.fUnitsPerEm / 2
+ }
+ return x / f.fUnitsPerEm
+}
+
+// Bounds returns the union of a Font's glyphs' bounds.
+func (f *Font) Bounds(scale int32) Bounds {
+ b := f.bounds
+ b.XMin = f.scale(scale * b.XMin)
+ b.YMin = f.scale(scale * b.YMin)
+ b.XMax = f.scale(scale * b.XMax)
+ b.YMax = f.scale(scale * b.YMax)
+ return b
+}
+
+// FUnitsPerEm returns the number of FUnits in a Font's em-square's side.
+func (f *Font) FUnitsPerEm() int32 {
+ return f.fUnitsPerEm
+}
+
+// Index returns a Font's index for the given rune.
+func (f *Font) Index(x rune) Index {
+ c := uint32(x)
+ for i, j := 0, len(f.cm); i < j; {
+ h := i + (j-i)/2
+ cm := &f.cm[h]
+ if c < cm.start {
+ j = h
+ } else if cm.end < c {
+ i = h + 1
+ } else if cm.offset == 0 {
+ return Index(c + cm.delta)
+ } else {
+ offset := int(cm.offset) + 2*(h-len(f.cm)+int(c-cm.start))
+ return Index(u16(f.cmapIndexes, offset))
+ }
+ }
+ return 0
+}
+
+// unscaledHMetric returns the unscaled horizontal metrics for the glyph with
+// the given index.
+func (f *Font) unscaledHMetric(i Index) (h HMetric) {
+ j := int(i)
+ if j < 0 || f.nGlyph <= j {
+ return 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))),
+ }
+ }
+ return HMetric{
+ AdvanceWidth: int32(u16(f.hmtx, 4*j)),
+ LeftSideBearing: int32(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 {
+ h := f.unscaledHMetric(i)
+ h.AdvanceWidth = f.scale(scale * h.AdvanceWidth)
+ h.LeftSideBearing = f.scale(scale * h.LeftSideBearing)
+ return h
+}
+
+// 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) {
+ 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))),
+ }
+ }
+ // The OS/2 table has grown over time.
+ // https://developer.apple.com/fonts/TTRefMan/RM06/Chap6OS2.html
+ // says that it was originally 68 bytes. Optional fields, including
+ // 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)))
+ return VMetric{
+ AdvanceHeight: sTypoAscender - sTypoDescender,
+ TopSideBearing: sTypoAscender - yMax,
+ }
+ }
+ return VMetric{
+ AdvanceHeight: 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 {
+ // TODO: should 0 be bounds.YMax?
+ v := f.unscaledVMetric(i, 0)
+ v.AdvanceHeight = f.scale(scale * v.AdvanceHeight)
+ v.TopSideBearing = f.scale(scale * v.TopSideBearing)
+ return v
+}
+
+// Kerning returns the kerning for the given glyph pair.
+func (f *Font) Kerning(scale int32, i0, i1 Index) int32 {
+ if f.nKern == 0 {
+ return 0
+ }
+ g := uint32(i0)<<16 | uint32(i1)
+ lo, hi := 0, f.nKern
+ for lo < hi {
+ i := (lo + hi) / 2
+ ig := u32(f.kern, 18+6*i)
+ if ig < g {
+ lo = i + 1
+ } else if ig > g {
+ hi = i
+ } else {
+ return f.scale(scale * int32(int16(u16(f.kern, 22+6*i))))
+ }
+ }
+ return 0
+}
+
+// Parse returns a new Font for the given TTF or TTC data.
+//
+// For TrueType Collections, the first font in the collection is parsed.
+func Parse(ttf []byte) (font *Font, err error) {
+ return parse(ttf, 0)
+}
+
+func parse(ttf []byte, offset int) (font *Font, err error) {
+ if len(ttf)-offset < 12 {
+ err = FormatError("TTF data is too short")
+ return
+ }
+ originalOffset := offset
+ magic, offset := u32(ttf, offset), offset+4
+ switch magic {
+ case 0x00010000:
+ // No-op.
+ case 0x74746366: // "ttcf" as a big-endian uint32.
+ if originalOffset != 0 {
+ err = FormatError("recursive TTC")
+ return
+ }
+ ttcVersion, offset := u32(ttf, offset), offset+4
+ if ttcVersion != 0x00010000 {
+ // TODO: support TTC version 2.0, once I have such a .ttc file to test with.
+ err = FormatError("bad TTC version")
+ return
+ }
+ numFonts, offset := int(u32(ttf, offset)), offset+4
+ if numFonts <= 0 {
+ err = FormatError("bad number of TTC fonts")
+ return
+ }
+ if len(ttf[offset:])/4 < numFonts {
+ err = FormatError("TTC offset table is too short")
+ return
+ }
+ // TODO: provide an API to select which font in a TrueType collection to return,
+ // not just the first one. This may require an API to parse a TTC's name tables,
+ // so users of this package can select the font in a TTC by name.
+ offset = int(u32(ttf, offset))
+ if offset <= 0 || offset > len(ttf) {
+ err = FormatError("bad TTC offset")
+ return
+ }
+ return parse(ttf, offset)
+ default:
+ err = FormatError("bad TTF version")
+ return
+ }
+ n, offset := int(u16(ttf, offset)), offset+2
+ if len(ttf) < 16*n+12 {
+ err = FormatError("TTF data is too short")
+ return
+ }
+ f := new(Font)
+ // Assign the table slices.
+ for i := 0; i < n; i++ {
+ x := 16*i + 12
+ switch string(ttf[x : x+4]) {
+ case "cmap":
+ f.cmap, err = readTable(ttf, ttf[x+8:x+16])
+ case "cvt ":
+ f.cvt, err = readTable(ttf, ttf[x+8:x+16])
+ case "fpgm":
+ f.fpgm, err = readTable(ttf, ttf[x+8:x+16])
+ case "glyf":
+ f.glyf, err = readTable(ttf, ttf[x+8:x+16])
+ case "hdmx":
+ f.hdmx, err = readTable(ttf, ttf[x+8:x+16])
+ case "head":
+ f.head, err = readTable(ttf, ttf[x+8:x+16])
+ case "hhea":
+ f.hhea, err = readTable(ttf, ttf[x+8:x+16])
+ case "hmtx":
+ f.hmtx, err = readTable(ttf, ttf[x+8:x+16])
+ case "kern":
+ f.kern, err = readTable(ttf, ttf[x+8:x+16])
+ case "loca":
+ f.loca, err = readTable(ttf, ttf[x+8:x+16])
+ case "maxp":
+ f.maxp, err = readTable(ttf, ttf[x+8:x+16])
+ case "OS/2":
+ f.os2, err = readTable(ttf, ttf[x+8:x+16])
+ case "prep":
+ f.prep, err = readTable(ttf, ttf[x+8:x+16])
+ case "vmtx":
+ f.vmtx, err = readTable(ttf, ttf[x+8:x+16])
+ }
+ if err != nil {
+ return
+ }
+ }
+ // Parse and sanity-check the TTF data.
+ if err = f.parseHead(); err != nil {
+ return
+ }
+ if err = f.parseMaxp(); err != nil {
+ return
+ }
+ if err = f.parseCmap(); err != nil {
+ return
+ }
+ if err = f.parseKern(); err != nil {
+ return
+ }
+ if err = f.parseHhea(); err != nil {
+ return
+ }
+ font = f
+ return
+}
diff --git a/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/truetype_test.go b/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/truetype_test.go
new file mode 100644
index 000000000..9ef6ec8d2
--- /dev/null
+++ b/Godeps/_workspace/src/code.google.com/p/freetype-go/freetype/truetype/truetype_test.go
@@ -0,0 +1,366 @@
+// Copyright 2012 The Freetype-Go Authors. All rights reserved.
+// Use of this source code is governed by your choice of either the
+// FreeType License or the GNU General Public License version 2 (or
+// any later version), both of which can be found in the LICENSE file.
+
+package truetype
+
+import (
+ "bufio"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "strconv"
+ "strings"
+ "testing"
+)
+
+func parseTestdataFont(name string) (font *Font, testdataIsOptional bool, err error) {
+ b, err := ioutil.ReadFile(fmt.Sprintf("../../testdata/%s.ttf", name))
+ if err != nil {
+ // The "x-foo" fonts are optional tests, as they are not checked
+ // in for copyright or file size reasons.
+ return nil, strings.HasPrefix(name, "x-"), fmt.Errorf("%s: ReadFile: %v", name, err)
+ }
+ font, err = Parse(b)
+ if err != nil {
+ return nil, true, fmt.Errorf("%s: Parse: %v", name, err)
+ }
+ return font, false, nil
+}
+
+// TestParse tests that the luxisr.ttf metrics and glyphs are parsed correctly.
+// The numerical values can be manually verified by examining luxisr.ttx.
+func TestParse(t *testing.T) {
+ font, _, err := parseTestdataFont("luxisr")
+ if err != nil {
+ t.Fatal(err)
+ }
+ if got, want := font.FUnitsPerEm(), int32(2048); got != want {
+ t.Errorf("FUnitsPerEm: got %v, want %v", got, want)
+ }
+ fupe := font.FUnitsPerEm()
+ if got, want := font.Bounds(fupe), (Bounds{-441, -432, 2024, 2033}); got != want {
+ t.Errorf("Bounds: got %v, want %v", got, want)
+ }
+
+ i0 := font.Index('A')
+ i1 := font.Index('V')
+ if i0 != 36 || i1 != 57 {
+ t.Fatalf("Index: i0, i1 = %d, %d, want 36, 57", i0, i1)
+ }
+ if got, want := font.HMetric(fupe, i0), (HMetric{1366, 19}); got != want {
+ t.Errorf("HMetric: got %v, want %v", got, want)
+ }
+ 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 {
+ t.Errorf("Kerning: got %v, want %v", got, want)
+ }
+
+ g := NewGlyphBuf()
+ err = g.Load(font, fupe, i0, NoHinting)
+ if err != nil {
+ t.Fatalf("Load: %v", err)
+ }
+ g0 := &GlyphBuf{
+ B: g.B,
+ Point: g.Point,
+ End: g.End,
+ }
+ g1 := &GlyphBuf{
+ B: Bounds{19, 0, 1342, 1480},
+ Point: []Point{
+ {19, 0, 51},
+ {581, 1480, 1},
+ {789, 1480, 51},
+ {1342, 0, 1},
+ {1116, 0, 35},
+ {962, 410, 3},
+ {368, 410, 33},
+ {214, 0, 3},
+ {428, 566, 19},
+ {904, 566, 33},
+ {667, 1200, 3},
+ },
+ End: []int{8, 11},
+ }
+ if got, want := fmt.Sprint(g0), fmt.Sprint(g1); got != want {
+ t.Errorf("GlyphBuf:\ngot %v\nwant %v", got, want)
+ }
+}
+
+func TestIndex(t *testing.T) {
+ testCases := map[string]map[rune]Index{
+ "luxisr": {
+ ' ': 3,
+ '!': 4,
+ 'A': 36,
+ 'V': 57,
+ 'É': 101,
+ 'fl': 193,
+ '\u22c5': 385,
+ '中': 0,
+ },
+
+ // The x-etc test cases use those versions of the .ttf files provided
+ // by Ubuntu 14.04. See testdata/make-other-hinting-txts.sh for details.
+
+ "x-arial-bold": {
+ ' ': 3,
+ '+': 14,
+ '0': 19,
+ '_': 66,
+ 'w': 90,
+ '~': 97,
+ 'Ä': 98,
+ 'fl': 192,
+ '½': 242,
+ 'σ': 305,
+ 'λ': 540,
+ 'ỹ': 1275,
+ '\u04e9': 1319,
+ '中': 0,
+ },
+ "x-deja-vu-sans-oblique": {
+ ' ': 3,
+ '*': 13,
+ 'Œ': 276,
+ 'ω': 861,
+ '‡': 2571,
+ '⊕': 3110,
+ 'fl': 4728,
+ '\ufb03': 4729,
+ '\ufffd': 4813,
+ // TODO: '\U0001f640': ???,
+ '中': 0,
+ },
+ "x-droid-sans-japanese": {
+ ' ': 0,
+ '\u3000': 3,
+ '\u3041': 25,
+ '\u30fe': 201,
+ '\uff61': 202,
+ '\uff67': 208,
+ '\uff9e': 263,
+ '\uff9f': 264,
+ '\u4e00': 265,
+ '\u557e': 1000,
+ '\u61b6': 2024,
+ '\u6ede': 3177,
+ '\u7505': 3555,
+ '\u81e3': 4602,
+ '\u81e5': 4603,
+ '\u81e7': 4604,
+ '\u81e8': 4605,
+ '\u81ea': 4606,
+ '\u81ed': 4607,
+ '\u81f3': 4608,
+ '\u81f4': 4609,
+ '\u91c7': 5796,
+ '\u9fa0': 6620,
+ '\u203e': 12584,
+ },
+ "x-times-new-roman": {
+ ' ': 3,
+ ':': 29,
+ 'fl': 192,
+ 'Ŀ': 273,
+ '♠': 388,
+ 'Ŗ': 451,
+ 'Σ': 520,
+ '\u200D': 745,
+ 'Ẽ': 1216,
+ '\u04e9': 1319,
+ '中': 0,
+ },
+ }
+ for name, wants := range testCases {
+ font, testdataIsOptional, err := parseTestdataFont(name)
+ if err != nil {
+ if testdataIsOptional {
+ t.Log(err)
+ } else {
+ t.Fatal(err)
+ }
+ continue
+ }
+ for r, want := range wants {
+ if got := font.Index(r); got != want {
+ t.Errorf("%s: Index of %q, aka %U: got %d, want %d", name, r, r, got, want)
+ }
+ }
+ }
+}
+
+type scalingTestData struct {
+ advanceWidth int32
+ bounds Bounds
+ points []Point
+}
+
+// scalingTestParse parses a line of points like
+// 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) {
+ t, i := "", strings.Index(s, " ")
+ if i != -1 {
+ s, t = s[:i], s[i+1:]
+ }
+ x, _ := strconv.Atoi(s)
+ return t, int32(x)
+ }
+
+ i := strings.Index(line, ";")
+ prefix, line := line[:i], line[i+1:]
+
+ prefix, ret.advanceWidth = next(prefix)
+ prefix, ret.bounds.XMin = next(prefix)
+ prefix, ret.bounds.YMin = next(prefix)
+ prefix, ret.bounds.XMax = next(prefix)
+ prefix, ret.bounds.YMax = next(prefix)
+
+ ret.points = make([]Point, 0, 1+strings.Count(line, ","))
+ for len(line) > 0 {
+ s := line
+ if i := strings.Index(line, ","); i != -1 {
+ s, line = line[:i], line[i+1:]
+ for len(line) > 0 && line[0] == ' ' {
+ line = line[1:]
+ }
+ } else {
+ line = ""
+ }
+ s, x := next(s)
+ s, y := next(s)
+ s, f := next(s)
+ ret.points = append(ret.points, Point{X: x, Y: y, Flags: uint32(f)})
+ }
+ return ret
+}
+
+// scalingTestEquals is equivalent to, but faster than, calling
+// reflect.DeepEquals(a, b), and also returns the index of the first non-equal
+// element. It also treats a nil []Point and an empty non-nil []Point as equal.
+// a and b must have equal length.
+func scalingTestEquals(a, b []Point) (index int, equals bool) {
+ for i, p := range a {
+ if p != b[i] {
+ return i, false
+ }
+ }
+ return 0, true
+}
+
+var scalingTestCases = []struct {
+ name string
+ size int32
+}{
+ {"luxisr", 12},
+ {"x-arial-bold", 11},
+ {"x-deja-vu-sans-oblique", 17},
+ {"x-droid-sans-japanese", 9},
+ {"x-times-new-roman", 13},
+}
+
+func testScaling(t *testing.T, h Hinting) {
+ for _, tc := range scalingTestCases {
+ font, testdataIsOptional, err := parseTestdataFont(tc.name)
+ if err != nil {
+ if testdataIsOptional {
+ t.Log(err)
+ } else {
+ t.Error(err)
+ }
+ continue
+ }
+ hintingStr := "sans"
+ if h != NoHinting {
+ hintingStr = "with"
+ }
+ f, err := os.Open(fmt.Sprintf(
+ "../../testdata/%s-%dpt-%s-hinting.txt", tc.name, tc.size, hintingStr))
+ if err != nil {
+ t.Errorf("%s: Open: %v", tc.name, err)
+ continue
+ }
+ defer f.Close()
+
+ wants := []scalingTestData{}
+ scanner := bufio.NewScanner(f)
+ if scanner.Scan() {
+ major, minor, patch := 0, 0, 0
+ _, err := fmt.Sscanf(scanner.Text(), "freetype version %d.%d.%d", &major, &minor, &patch)
+ if err != nil {
+ t.Errorf("%s: version information: %v", tc.name, err)
+ }
+ if (major < 2) || (major == 2 && minor < 5) || (major == 2 && minor == 5 && patch < 1) {
+ t.Errorf("%s: need freetype version >= 2.5.1.\n"+
+ "Try setting LD_LIBRARY_PATH=/path/to/freetype_built_from_src/objs/.libs/\n"+
+ "and re-running testdata/make-other-hinting-txts.sh",
+ tc.name)
+ continue
+ }
+ } else {
+ t.Errorf("%s: no version information", tc.name)
+ continue
+ }
+ for scanner.Scan() {
+ wants = append(wants, scalingTestParse(scanner.Text()))
+ }
+ if err := scanner.Err(); err != nil && err != io.EOF {
+ t.Errorf("%s: Scanner: %v", tc.name, err)
+ continue
+ }
+
+ glyphBuf := NewGlyphBuf()
+ for i, want := range wants {
+ if err = glyphBuf.Load(font, tc.size*64, Index(i), h); err != nil {
+ t.Errorf("%s: glyph #%d: Load: %v", tc.name, i, err)
+ continue
+ }
+ got := scalingTestData{
+ advanceWidth: glyphBuf.AdvanceWidth,
+ bounds: glyphBuf.B,
+ points: glyphBuf.Point,
+ }
+
+ if got.advanceWidth != want.advanceWidth {
+ t.Errorf("%s: glyph #%d advance width:\ngot %v\nwant %v",
+ tc.name, i, got.advanceWidth, want.advanceWidth)
+ continue
+ }
+
+ if got.bounds != want.bounds {
+ t.Errorf("%s: glyph #%d bounds:\ngot %v\nwant %v",
+ tc.name, i, got.bounds, want.bounds)
+ continue
+ }
+
+ for i := range got.points {
+ got.points[i].Flags &= 0x01
+ }
+ if len(got.points) != len(want.points) {
+ t.Errorf("%s: glyph #%d:\ngot %v\nwant %v\ndifferent slice lengths: %d versus %d",
+ tc.name, i, got.points, want.points, len(got.points), len(want.points))
+ continue
+ }
+ if j, equals := scalingTestEquals(got.points, want.points); !equals {
+ t.Errorf("%s: glyph #%d:\ngot %v\nwant %v\nat index %d: %v versus %v",
+ tc.name, i, got.points, want.points, j, got.points[j], want.points[j])
+ continue
+ }
+ }
+ }
+}
+
+func TestScalingSansHinting(t *testing.T) {
+ testScaling(t, NoHinting)
+}
+
+func TestScalingWithHinting(t *testing.T) {
+ testScaling(t, FullHinting)
+}