font/sfnt: add a ppem arg to Font.LoadGlyph.

This lets us load a glyph at e.g. 12 pixels per em.

Change-Id: I048b3db89af8670782953a8361afe0e6373df9b0
Reviewed-on: https://go-review.googlesource.com/37175
Reviewed-by: David Crawshaw <crawshaw@golang.org>
diff --git a/font/sfnt/example_test.go b/font/sfnt/example_test.go
new file mode 100644
index 0000000..f9a6697
--- /dev/null
+++ b/font/sfnt/example_test.go
@@ -0,0 +1,125 @@
+// Copyright 2017 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package sfnt_test
+
+import (
+	"image"
+	"image/draw"
+	"log"
+	"os"
+
+	"golang.org/x/image/font/gofont/goregular"
+	"golang.org/x/image/font/sfnt"
+	"golang.org/x/image/math/fixed"
+	"golang.org/x/image/vector"
+)
+
+func ExampleRasterizeGlyph() {
+	const (
+		ppem    = 32
+		width   = 24
+		height  = 32
+		originX = 0
+		originY = 28
+	)
+
+	f, err := sfnt.Parse(goregular.TTF)
+	if err != nil {
+		log.Fatalf("Parse: %v", err)
+	}
+	var b sfnt.Buffer
+	x, err := f.GlyphIndex(&b, 'G')
+	if err != nil {
+		log.Fatalf("GlyphIndex: %v", err)
+	}
+	if x == 0 {
+		log.Fatalf("GlyphIndex: no glyph index found for the rune 'G'")
+	}
+	segments, err := f.LoadGlyph(&b, x, fixed.I(ppem), nil)
+
+	r := vector.NewRasterizer(width, height)
+	r.DrawOp = draw.Src
+	for _, seg := range segments {
+		// The divisions by 64 below is because the seg.Args values have type
+		// fixed.Int26_6, a 26.6 fixed point number, and 1<<6 == 64.
+		switch seg.Op {
+		case sfnt.SegmentOpMoveTo:
+			r.MoveTo(
+				originX+float32(seg.Args[0])/64,
+				originY-float32(seg.Args[1])/64,
+			)
+		case sfnt.SegmentOpLineTo:
+			r.LineTo(
+				originX+float32(seg.Args[0])/64,
+				originY-float32(seg.Args[1])/64,
+			)
+		case sfnt.SegmentOpQuadTo:
+			r.QuadTo(
+				originX+float32(seg.Args[0])/64,
+				originY-float32(seg.Args[1])/64,
+				originX+float32(seg.Args[2])/64,
+				originY-float32(seg.Args[3])/64,
+			)
+		case sfnt.SegmentOpCubeTo:
+			r.CubeTo(
+				originX+float32(seg.Args[0])/64,
+				originY-float32(seg.Args[1])/64,
+				originX+float32(seg.Args[2])/64,
+				originY-float32(seg.Args[3])/64,
+				originX+float32(seg.Args[4])/64,
+				originY-float32(seg.Args[5])/64,
+			)
+		}
+	}
+	// TODO: call ClosePath? Once overall or once per contour (i.e. MoveTo)?
+
+	dst := image.NewAlpha(image.Rect(0, 0, width, height))
+	r.Draw(dst, dst.Bounds(), image.Opaque, image.Point{})
+
+	const asciiArt = ".++8"
+	buf := make([]byte, 0, height*(width+1))
+	for y := 0; y < height; y++ {
+		for x := 0; x < width; x++ {
+			a := dst.AlphaAt(x, y).A
+			buf = append(buf, asciiArt[a>>6])
+		}
+		buf = append(buf, '\n')
+	}
+	os.Stdout.Write(buf)
+
+	// Output:
+	// ........................
+	// ........................
+	// ........................
+	// ........................
+	// ..........+++++++++.....
+	// .......+8888888888888+..
+	// ......8888888888888888..
+	// ....+8888+........++88..
+	// ....8888................
+	// ...8888.................
+	// ..+888+.................
+	// ..+888..................
+	// ..888+..................
+	// .+888+..................
+	// .+888...................
+	// .+888...................
+	// .+888...................
+	// .+888..........+++++++..
+	// .+888..........8888888..
+	// .+888+.........+++8888..
+	// ..888+............+888..
+	// ..8888............+888..
+	// ..+888+...........+888..
+	// ...8888+..........+888..
+	// ...+8888+.........+888..
+	// ....+88888+.......+888..
+	// .....+8888888888888888..
+	// .......+888888888888++..
+	// ..........++++++++......
+	// ........................
+	// ........................
+	// ........................
+}
diff --git a/font/sfnt/postscript.go b/font/sfnt/postscript.go
index d5d6d9a..8142bdc 100644
--- a/font/sfnt/postscript.go
+++ b/font/sfnt/postscript.go
@@ -653,8 +653,8 @@
 	p.type2Charstrings.segments = append(p.type2Charstrings.segments, Segment{
 		Op: SegmentOpMoveTo,
 		Args: [6]fixed.Int26_6{
-			0: fixed.Int26_6(p.type2Charstrings.x) << 6,
-			1: fixed.Int26_6(p.type2Charstrings.y) << 6,
+			0: fixed.Int26_6(p.type2Charstrings.x),
+			1: fixed.Int26_6(p.type2Charstrings.y),
 		},
 	})
 }
@@ -663,8 +663,8 @@
 	p.type2Charstrings.segments = append(p.type2Charstrings.segments, Segment{
 		Op: SegmentOpLineTo,
 		Args: [6]fixed.Int26_6{
-			0: fixed.Int26_6(p.type2Charstrings.x) << 6,
-			1: fixed.Int26_6(p.type2Charstrings.y) << 6,
+			0: fixed.Int26_6(p.type2Charstrings.x),
+			1: fixed.Int26_6(p.type2Charstrings.y),
 		},
 	})
 }
@@ -685,12 +685,12 @@
 	p.type2Charstrings.segments = append(p.type2Charstrings.segments, Segment{
 		Op: SegmentOpCubeTo,
 		Args: [6]fixed.Int26_6{
-			0: fixed.Int26_6(xa) << 6,
-			1: fixed.Int26_6(ya) << 6,
-			2: fixed.Int26_6(xb) << 6,
-			3: fixed.Int26_6(yb) << 6,
-			4: fixed.Int26_6(xc) << 6,
-			5: fixed.Int26_6(yc) << 6,
+			0: fixed.Int26_6(xa),
+			1: fixed.Int26_6(ya),
+			2: fixed.Int26_6(xb),
+			3: fixed.Int26_6(yb),
+			4: fixed.Int26_6(xc),
+			5: fixed.Int26_6(yc),
 		},
 	})
 }
diff --git a/font/sfnt/proprietary_test.go b/font/sfnt/proprietary_test.go
index f4f803e..d25c62a 100644
--- a/font/sfnt/proprietary_test.go
+++ b/font/sfnt/proprietary_test.go
@@ -35,6 +35,8 @@
 	"io/ioutil"
 	"path/filepath"
 	"testing"
+
+	"golang.org/x/image/math/fixed"
 )
 
 var (
@@ -132,6 +134,7 @@
 	if err != nil {
 		t.Fatalf("Parse: %v", err)
 	}
+	ppem := fixed.Int26_6(f.UnitsPerEm()) << 6
 
 	numGlyphs := f.NumGlyphs()
 	if numGlyphs < minNumGlyphs {
@@ -144,7 +147,7 @@
 		iMax = firstUnsupportedGlyph
 	}
 	for i, numErrors := 0, 0; i < iMax; i++ {
-		if _, err := f.LoadGlyph(&buf, GlyphIndex(i), nil); err != nil {
+		if _, err := f.LoadGlyph(&buf, GlyphIndex(i), ppem, nil); err != nil {
 			t.Errorf("LoadGlyph(%d): %v", i, err)
 			numErrors++
 		}
diff --git a/font/sfnt/sfnt.go b/font/sfnt/sfnt.go
index 995b287..b181688 100644
--- a/font/sfnt/sfnt.go
+++ b/font/sfnt/sfnt.go
@@ -131,6 +131,17 @@
 // display resolution (DPI) and font size (e.g. a 12 point font).
 type Units int32
 
+// scale returns x divided by unitsPerEm, rounded to the nearest fixed.Int26_6
+// value (1/64th of a pixel).
+func scale(x fixed.Int26_6, unitsPerEm Units) fixed.Int26_6 {
+	if x >= 0 {
+		x += fixed.Int26_6(unitsPerEm) / 2
+	} else {
+		x -= fixed.Int26_6(unitsPerEm) / 2
+	}
+	return x / fixed.Int26_6(unitsPerEm)
+}
+
 func u16(b []byte) uint16 {
 	_ = b[1] // Bounds check hint to compiler.
 	return uint16(b[0])<<8 | uint16(b[1])<<0
@@ -274,6 +285,12 @@
 //
 // The Font methods that don't take a *Buffer argument are always safe to call
 // concurrently.
+//
+// Some methods provide lengths or co-ordinates, e.g. bounds, font metrics and
+// control points. All of these methods take a ppem parameter, which is the
+// number of pixels in 1 em, expressed as a 26.6 fixed point value. For
+// example, if 1 em is 10 pixels then ppem is fixed.I(10), which equals
+// fixed.Int26_6(10 << 6).
 type Font struct {
 	src source
 
@@ -623,15 +640,16 @@
 
 // LoadGlyphOptions are the options to the Font.LoadGlyph method.
 type LoadGlyphOptions struct {
-	// TODO: scale / transform / hinting.
+	// TODO: transform / hinting.
 }
 
-// LoadGlyph returns the vector segments for the x'th glyph.
+// LoadGlyph returns the vector segments for the x'th glyph. ppem is the number
+// of pixels in 1 em.
 //
 // If b is non-nil, the segments become invalid to use once b is re-used.
 //
 // It returns ErrNotFound if the glyph index is out of range.
-func (f *Font) LoadGlyph(b *Buffer, x GlyphIndex, opts *LoadGlyphOptions) ([]Segment, error) {
+func (f *Font) LoadGlyph(b *Buffer, x GlyphIndex, ppem fixed.Int26_6, opts *LoadGlyphOptions) ([]Segment, error) {
 	if b == nil {
 		b = &Buffer{}
 	}
@@ -656,7 +674,19 @@
 		b.segments = segments
 	}
 
-	// TODO: look at opts to scale / transform / hint the Buffer.segments.
+	// Scale the segments. If we want to support hinting, we'll have to push
+	// the scaling computation into the PostScript / TrueType specific glyph
+	// loading code, such as the appendGlyfSegments body, since TrueType
+	// hinting bytecode works on the scaled glyph vectors. For now, though,
+	// it's simpler to scale as a post-processing step.
+	for i := range b.segments {
+		s := &b.segments[i]
+		for j := range s.Args {
+			s.Args[j] = scale(s.Args[j]*ppem, f.cached.unitsPerEm)
+		}
+	}
+
+	// TODO: look at opts to transform / hint the Buffer.segments.
 
 	return b.segments, nil
 }
diff --git a/font/sfnt/sfnt_test.go b/font/sfnt/sfnt_test.go
index ca30321..6f6fff9 100644
--- a/font/sfnt/sfnt_test.go
+++ b/font/sfnt/sfnt_test.go
@@ -6,6 +6,7 @@
 
 import (
 	"bytes"
+	"fmt"
 	"io/ioutil"
 	"path/filepath"
 	"testing"
@@ -24,6 +25,16 @@
 	}
 }
 
+func moveTo26_6(xa, ya fixed.Int26_6) Segment {
+	return Segment{
+		Op: SegmentOpMoveTo,
+		Args: [6]fixed.Int26_6{
+			0: xa,
+			1: ya,
+		},
+	}
+}
+
 func lineTo(xa, ya int) Segment {
 	return Segment{
 		Op: SegmentOpLineTo,
@@ -34,6 +45,16 @@
 	}
 }
 
+func lineTo26_6(xa, ya fixed.Int26_6) Segment {
+	return Segment{
+		Op: SegmentOpLineTo,
+		Args: [6]fixed.Int26_6{
+			0: xa,
+			1: ya,
+		},
+	}
+}
+
 func quadTo(xa, ya, xb, yb int) Segment {
 	return Segment{
 		Op: SegmentOpQuadTo,
@@ -60,6 +81,20 @@
 	}
 }
 
+func checkSegmentsEqual(got, want []Segment) error {
+	if len(got) != len(want) {
+		return fmt.Errorf("got %d elements, want %d\noverall:\ngot  %v\nwant %v",
+			len(got), len(want), got, want)
+	}
+	for i, g := range got {
+		if w := want[i]; g != w {
+			return fmt.Errorf("element %d:\ngot  %v\nwant %v\noverall:\ngot  %v\nwant %v",
+				i, g, w, got, want)
+		}
+	}
+	return nil
+}
+
 func TestTrueTypeParse(t *testing.T) {
 	f, err := Parse(goregular.TTF)
 	if err != nil {
@@ -389,38 +424,30 @@
 func testSegments(t *testing.T, filename string, wants [][]Segment) {
 	data, err := ioutil.ReadFile(filepath.FromSlash("../testdata/" + filename))
 	if err != nil {
-		t.Fatal(err)
+		t.Fatalf("ReadFile: %v", err)
 	}
 	f, err := Parse(data)
 	if err != nil {
-		t.Fatal(err)
+		t.Fatalf("Parse: %v", err)
 	}
+	ppem := fixed.Int26_6(f.UnitsPerEm()) << 6
 
 	if ng := f.NumGlyphs(); ng != len(wants) {
 		t.Fatalf("NumGlyphs: got %d, want %d", ng, len(wants))
 	}
 	var b Buffer
-loop:
 	for i, want := range wants {
-		got, err := f.LoadGlyph(&b, GlyphIndex(i), nil)
+		got, err := f.LoadGlyph(&b, GlyphIndex(i), ppem, nil)
 		if err != nil {
 			t.Errorf("i=%d: LoadGlyph: %v", i, err)
 			continue
 		}
-		if len(got) != len(want) {
-			t.Errorf("i=%d: got %d elements, want %d\noverall:\ngot  %v\nwant %v",
-				i, len(got), len(want), got, want)
+		if err := checkSegmentsEqual(got, want); err != nil {
+			t.Errorf("i=%d: %v", i, err)
 			continue
 		}
-		for j, g := range got {
-			if w := want[j]; g != w {
-				t.Errorf("i=%d: element %d:\ngot  %v\nwant %v\noverall:\ngot  %v\nwant %v",
-					i, j, g, w, got, want)
-				continue loop
-			}
-		}
 	}
-	if _, err := f.LoadGlyph(nil, 0xffff, nil); err != ErrNotFound {
+	if _, err := f.LoadGlyph(nil, 0xffff, ppem, nil); err != ErrNotFound {
 		t.Errorf("LoadGlyph(..., 0xffff, ...):\ngot  %v\nwant %v", err, ErrNotFound)
 	}
 
@@ -432,6 +459,60 @@
 	}
 }
 
+func TestPPEM(t *testing.T) {
+	data, err := ioutil.ReadFile(filepath.FromSlash("../testdata/glyfTest.ttf"))
+	if err != nil {
+		t.Fatalf("ReadFile: %v", err)
+	}
+	f, err := Parse(data)
+	if err != nil {
+		t.Fatalf("Parse: %v", err)
+	}
+	var b Buffer
+	x, err := f.GlyphIndex(&b, '1')
+	if err != nil {
+		t.Fatalf("GlyphIndex: %v", err)
+	}
+	if x == 0 {
+		t.Fatalf("GlyphIndex: no glyph index found for the rune '1'")
+	}
+
+	testCases := []struct {
+		ppem fixed.Int26_6
+		want []Segment
+	}{{
+		ppem: fixed.I(12),
+		want: []Segment{
+			moveTo26_6(77, 0),
+			lineTo26_6(77, 614),
+			lineTo26_6(230, 614),
+			lineTo26_6(230, 0),
+			lineTo26_6(77, 0),
+		},
+	}, {
+		ppem: fixed.I(2048),
+		want: []Segment{
+			moveTo(205, 0),
+			lineTo(205, 1638),
+			lineTo(614, 1638),
+			lineTo(614, 0),
+			lineTo(205, 0),
+		},
+	}}
+
+	for i, tc := range testCases {
+		got, err := f.LoadGlyph(&b, x, tc.ppem, nil)
+		if err != nil {
+			t.Errorf("i=%d: LoadGlyph: %v", i, err)
+			continue
+		}
+		if err := checkSegmentsEqual(got, tc.want); err != nil {
+			t.Errorf("i=%d: %v", i, err)
+			continue
+		}
+	}
+}
+
 func TestGlyphName(t *testing.T) {
 	f, err := Parse(goregular.TTF)
 	if err != nil {
diff --git a/font/sfnt/truetype.go b/font/sfnt/truetype.go
index be90154..4111feb 100644
--- a/font/sfnt/truetype.go
+++ b/font/sfnt/truetype.go
@@ -357,9 +357,18 @@
 			return true
 		}
 
+		// Convert the tuple (g.x, g.y) to a fixed.Point26_6, since the latter
+		// is what's held in a Segment. The input (g.x, g.y) is a pair of int16
+		// values, measured in font units, since that is what the underlying
+		// format provides. The output is a pair of fixed.Int26_6 values. A
+		// fixed.Int26_6 usually represents a 26.6 fixed number of pixels, but
+		// this here is just a straight numerical conversion, with no scaling
+		// factor. A later step scales the Segment.Args values by such a factor
+		// to convert e.g. 1792 font units to 10.5 pixels at 2048 font units
+		// per em and 12 ppem (pixels per em).
 		p := fixed.Point26_6{
-			X: fixed.Int26_6(g.x) << 6,
-			Y: fixed.Int26_6(g.y) << 6,
+			X: fixed.Int26_6(g.x),
+			Y: fixed.Int26_6(g.y),
 		}
 
 		if !g.firstOnCurveValid {