font/sfnt: flip the Y axis for LoadGlyph's Segments.

The underlying font format's Y axis increases up. The Go standard
graphics libraries' Y axis increases down. This change makes the Go API
consistent with the other Go libraries.

Also change Segment.Args from [6]fixed.Int26_6 to [3]fixed.Point26_6 to
emphasize that the Args are consistent with other fixed.Point26_6 use.

Change-Id: Idd7b89eb4d86890dea477ac2ef96ff8f6b1dee8d
Reviewed-on: https://go-review.googlesource.com/39072
Reviewed-by: David Crawshaw <crawshaw@golang.org>
diff --git a/font/sfnt/example_test.go b/font/sfnt/example_test.go
index 1743156..63379eb 100644
--- a/font/sfnt/example_test.go
+++ b/font/sfnt/example_test.go
@@ -50,29 +50,29 @@
 		switch seg.Op {
 		case sfnt.SegmentOpMoveTo:
 			r.MoveTo(
-				originX+float32(seg.Args[0])/64,
-				originY-float32(seg.Args[1])/64,
+				originX+float32(seg.Args[0].X)/64,
+				originY+float32(seg.Args[0].Y)/64,
 			)
 		case sfnt.SegmentOpLineTo:
 			r.LineTo(
-				originX+float32(seg.Args[0])/64,
-				originY-float32(seg.Args[1])/64,
+				originX+float32(seg.Args[0].X)/64,
+				originY+float32(seg.Args[0].Y)/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,
+				originX+float32(seg.Args[0].X)/64,
+				originY+float32(seg.Args[0].Y)/64,
+				originX+float32(seg.Args[1].X)/64,
+				originY+float32(seg.Args[1].Y)/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,
+				originX+float32(seg.Args[0].X)/64,
+				originY+float32(seg.Args[0].Y)/64,
+				originX+float32(seg.Args[1].X)/64,
+				originY+float32(seg.Args[1].Y)/64,
+				originX+float32(seg.Args[2].X)/64,
+				originY+float32(seg.Args[2].Y)/64,
 			)
 		}
 	}
diff --git a/font/sfnt/postscript.go b/font/sfnt/postscript.go
index 183f0cb..2f746c4 100644
--- a/font/sfnt/postscript.go
+++ b/font/sfnt/postscript.go
@@ -999,46 +999,39 @@
 func t2CAppendMoveto(p *psInterpreter) {
 	p.type2Charstrings.b.segments = append(p.type2Charstrings.b.segments, Segment{
 		Op: SegmentOpMoveTo,
-		Args: [6]fixed.Int26_6{
-			0: fixed.Int26_6(p.type2Charstrings.x),
-			1: fixed.Int26_6(p.type2Charstrings.y),
-		},
+		Args: [3]fixed.Point26_6{{
+			X: fixed.Int26_6(p.type2Charstrings.x),
+			Y: fixed.Int26_6(p.type2Charstrings.y),
+		}},
 	})
 }
 
 func t2CAppendLineto(p *psInterpreter) {
 	p.type2Charstrings.b.segments = append(p.type2Charstrings.b.segments, Segment{
 		Op: SegmentOpLineTo,
-		Args: [6]fixed.Int26_6{
-			0: fixed.Int26_6(p.type2Charstrings.x),
-			1: fixed.Int26_6(p.type2Charstrings.y),
-		},
+		Args: [3]fixed.Point26_6{{
+			X: fixed.Int26_6(p.type2Charstrings.x),
+			Y: fixed.Int26_6(p.type2Charstrings.y),
+		}},
 	})
 }
 
 func t2CAppendCubeto(p *psInterpreter, dxa, dya, dxb, dyb, dxc, dyc int32) {
 	p.type2Charstrings.x += dxa
 	p.type2Charstrings.y += dya
-	xa := p.type2Charstrings.x
-	ya := p.type2Charstrings.y
+	xa := fixed.Int26_6(p.type2Charstrings.x)
+	ya := fixed.Int26_6(p.type2Charstrings.y)
 	p.type2Charstrings.x += dxb
 	p.type2Charstrings.y += dyb
-	xb := p.type2Charstrings.x
-	yb := p.type2Charstrings.y
+	xb := fixed.Int26_6(p.type2Charstrings.x)
+	yb := fixed.Int26_6(p.type2Charstrings.y)
 	p.type2Charstrings.x += dxc
 	p.type2Charstrings.y += dyc
-	xc := p.type2Charstrings.x
-	yc := p.type2Charstrings.y
+	xc := fixed.Int26_6(p.type2Charstrings.x)
+	yc := fixed.Int26_6(p.type2Charstrings.y)
 	p.type2Charstrings.b.segments = append(p.type2Charstrings.b.segments, Segment{
-		Op: SegmentOpCubeTo,
-		Args: [6]fixed.Int26_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),
-		},
+		Op:   SegmentOpCubeTo,
+		Args: [3]fixed.Point26_6{{X: xa, Y: ya}, {X: xb, Y: yb}, {X: xc, Y: yc}},
 	})
 }
 
diff --git a/font/sfnt/sfnt.go b/font/sfnt/sfnt.go
index 73ec6c9..6668606 100644
--- a/font/sfnt/sfnt.go
+++ b/font/sfnt/sfnt.go
@@ -902,6 +902,8 @@
 //
 // If b is non-nil, the segments become invalid to use once b is re-used.
 //
+// In the returned Segments' (x, y) coordinates, the Y axis increases down.
+//
 // It returns ErrNotFound if the glyph index is out of range.
 func (f *Font) LoadGlyph(b *Buffer, x GlyphIndex, ppem fixed.Int26_6, opts *LoadGlyphOptions) ([]Segment, error) {
 	if b == nil {
@@ -932,10 +934,14 @@
 	// 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.
+	//
+	// We also flip the Y coordinates. OpenType's Y axis increases up. Go's
+	// standard graphics libraries' Y axis increases down.
 	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)
+		a := &b.segments[i].Args
+		for j := range a {
+			a[j].X = +scale(a[j].X*ppem, f.cached.unitsPerEm)
+			a[j].Y = -scale(a[j].Y*ppem, f.cached.unitsPerEm)
 		}
 	}
 
@@ -1194,8 +1200,10 @@
 
 // Segment is a segment of a vector path.
 type Segment struct {
-	Op   SegmentOp
-	Args [6]fixed.Int26_6
+	// Op is the operator.
+	Op SegmentOp
+	// Args is up to three (x, y) coordinates. The Y axis increases down.
+	Args [3]fixed.Point26_6
 }
 
 // SegmentOp is a vector path segment's operator.
@@ -1209,30 +1217,31 @@
 )
 
 // translateArgs applies a translation to args.
-func translateArgs(args *[6]fixed.Int26_6, dx, dy fixed.Int26_6) {
-	args[0] += dx
-	args[1] += dy
-	args[2] += dx
-	args[3] += dy
-	args[4] += dx
-	args[5] += dy
+func translateArgs(args *[3]fixed.Point26_6, dx, dy fixed.Int26_6) {
+	args[0].X += dx
+	args[0].Y += dy
+	args[1].X += dx
+	args[1].Y += dy
+	args[2].X += dx
+	args[2].Y += dy
 }
 
 // transformArgs applies an affine transformation to args. The t?? arguments
 // are 2.14 fixed point values.
-func transformArgs(args *[6]fixed.Int26_6, txx, txy, tyx, tyy int16, dx, dy fixed.Int26_6) {
-	args[0], args[1] = tform(txx, txy, tyx, tyy, dx, dy, args[0], args[1])
-	args[2], args[3] = tform(txx, txy, tyx, tyy, dx, dy, args[2], args[3])
-	args[4], args[5] = tform(txx, txy, tyx, tyy, dx, dy, args[4], args[5])
+func transformArgs(args *[3]fixed.Point26_6, txx, txy, tyx, tyy int16, dx, dy fixed.Int26_6) {
+	args[0] = tform(txx, txy, tyx, tyy, dx, dy, args[0])
+	args[1] = tform(txx, txy, tyx, tyy, dx, dy, args[1])
+	args[2] = tform(txx, txy, tyx, tyy, dx, dy, args[2])
 }
 
-func tform(txx, txy, tyx, tyy int16, dx, dy, x, y fixed.Int26_6) (newX, newY fixed.Int26_6) {
+func tform(txx, txy, tyx, tyy int16, dx, dy fixed.Int26_6, p fixed.Point26_6) fixed.Point26_6 {
 	const half = 1 << 13
-	newX = dx +
-		fixed.Int26_6((int64(x)*int64(txx)+half)>>14) +
-		fixed.Int26_6((int64(y)*int64(tyx)+half)>>14)
-	newY = dy +
-		fixed.Int26_6((int64(x)*int64(txy)+half)>>14) +
-		fixed.Int26_6((int64(y)*int64(tyy)+half)>>14)
-	return newX, newY
+	return fixed.Point26_6{
+		X: dx +
+			fixed.Int26_6((int64(p.X)*int64(txx)+half)>>14) +
+			fixed.Int26_6((int64(p.Y)*int64(tyx)+half)>>14),
+		Y: dy +
+			fixed.Int26_6((int64(p.X)*int64(txy)+half)>>14) +
+			fixed.Int26_6((int64(p.Y)*int64(tyy)+half)>>14),
+	}
 }
diff --git a/font/sfnt/sfnt_test.go b/font/sfnt/sfnt_test.go
index c0a7fb0..2bb508b 100644
--- a/font/sfnt/sfnt_test.go
+++ b/font/sfnt/sfnt_test.go
@@ -15,31 +15,35 @@
 	"golang.org/x/image/math/fixed"
 )
 
+func pt(x, y fixed.Int26_6) fixed.Point26_6 {
+	return fixed.Point26_6{X: x, Y: y}
+}
+
 func moveTo(xa, ya fixed.Int26_6) Segment {
 	return Segment{
 		Op:   SegmentOpMoveTo,
-		Args: [6]fixed.Int26_6{xa, ya},
+		Args: [3]fixed.Point26_6{pt(xa, ya)},
 	}
 }
 
 func lineTo(xa, ya fixed.Int26_6) Segment {
 	return Segment{
 		Op:   SegmentOpLineTo,
-		Args: [6]fixed.Int26_6{xa, ya},
+		Args: [3]fixed.Point26_6{pt(xa, ya)},
 	}
 }
 
 func quadTo(xa, ya, xb, yb fixed.Int26_6) Segment {
 	return Segment{
 		Op:   SegmentOpQuadTo,
-		Args: [6]fixed.Int26_6{xa, ya, xb, yb},
+		Args: [3]fixed.Point26_6{pt(xa, ya), pt(xb, yb)},
 	}
 }
 
 func cubeTo(xa, ya, xb, yb, xc, yc fixed.Int26_6) Segment {
 	return Segment{
 		Op:   SegmentOpCubeTo,
-		Args: [6]fixed.Int26_6{xa, ya, xb, yb, xc, yc},
+		Args: [3]fixed.Point26_6{pt(xa, ya), pt(xb, yb), pt(xc, yc)},
 	}
 }
 
@@ -54,6 +58,16 @@
 }
 
 func checkSegmentsEqual(got, want []Segment) error {
+	// Flip got's Y axis. The test cases' coordinates are given with the Y axis
+	// increasing up, as that is what the ttx tool gives, and is the model for
+	// the underlying font format. The Go API returns coordinates with the Y
+	// axis increasing down, the same as the standard graphics libraries.
+	for i := range got {
+		for j := range got[i].Args {
+			got[i].Args[j].Y *= -1
+		}
+	}
+
 	if len(got) != len(want) {
 		return fmt.Errorf("got %d elements, want %d\noverall:\ngot  %v\nwant %v",
 			len(got), len(want), got, want)
diff --git a/font/sfnt/truetype.go b/font/sfnt/truetype.go
index 4181961..25455e5 100644
--- a/font/sfnt/truetype.go
+++ b/font/sfnt/truetype.go
@@ -414,44 +414,28 @@
 	case !g.firstOffCurveValid && !g.lastOffCurveValid:
 		g.closed = true
 		g.seg = Segment{
-			Op: SegmentOpLineTo,
-			Args: [6]fixed.Int26_6{
-				g.firstOnCurve.X,
-				g.firstOnCurve.Y,
-			},
+			Op:   SegmentOpLineTo,
+			Args: [3]fixed.Point26_6{g.firstOnCurve},
 		}
 	case !g.firstOffCurveValid && g.lastOffCurveValid:
 		g.closed = true
 		g.seg = Segment{
-			Op: SegmentOpQuadTo,
-			Args: [6]fixed.Int26_6{
-				g.lastOffCurve.X,
-				g.lastOffCurve.Y,
-				g.firstOnCurve.X,
-				g.firstOnCurve.Y,
-			},
+			Op:   SegmentOpQuadTo,
+			Args: [3]fixed.Point26_6{g.lastOffCurve, g.firstOnCurve},
 		}
 	case g.firstOffCurveValid && !g.lastOffCurveValid:
 		g.closed = true
 		g.seg = Segment{
-			Op: SegmentOpQuadTo,
-			Args: [6]fixed.Int26_6{
-				g.firstOffCurve.X,
-				g.firstOffCurve.Y,
-				g.firstOnCurve.X,
-				g.firstOnCurve.Y,
-			},
+			Op:   SegmentOpQuadTo,
+			Args: [3]fixed.Point26_6{g.firstOffCurve, g.firstOnCurve},
 		}
 	case g.firstOffCurveValid && g.lastOffCurveValid:
-		mid := midPoint(g.lastOffCurve, g.firstOffCurve)
 		g.lastOffCurveValid = false
 		g.seg = Segment{
 			Op: SegmentOpQuadTo,
-			Args: [6]fixed.Int26_6{
-				g.lastOffCurve.X,
-				g.lastOffCurve.Y,
-				mid.X,
-				mid.Y,
+			Args: [3]fixed.Point26_6{
+				g.lastOffCurve,
+				midPoint(g.lastOffCurve, g.firstOffCurve),
 			},
 		}
 	}
@@ -484,11 +468,8 @@
 				g.firstOnCurve = p
 				g.firstOnCurveValid = true
 				g.seg = Segment{
-					Op: SegmentOpMoveTo,
-					Args: [6]fixed.Int26_6{
-						p.X,
-						p.Y,
-					},
+					Op:   SegmentOpMoveTo,
+					Args: [3]fixed.Point26_6{p},
 				}
 				return true
 			} else if !g.firstOffCurveValid {
@@ -496,17 +477,13 @@
 				g.firstOffCurveValid = true
 				continue
 			} else {
-				midp := midPoint(g.firstOffCurve, p)
-				g.firstOnCurve = midp
+				g.firstOnCurve = midPoint(g.firstOffCurve, p)
 				g.firstOnCurveValid = true
 				g.lastOffCurve = p
 				g.lastOffCurveValid = true
 				g.seg = Segment{
-					Op: SegmentOpMoveTo,
-					Args: [6]fixed.Int26_6{
-						midp.X,
-						midp.Y,
-					},
+					Op:   SegmentOpMoveTo,
+					Args: [3]fixed.Point26_6{g.firstOnCurve},
 				}
 				return true
 			}
@@ -518,25 +495,19 @@
 				continue
 			} else {
 				g.seg = Segment{
-					Op: SegmentOpLineTo,
-					Args: [6]fixed.Int26_6{
-						p.X,
-						p.Y,
-					},
+					Op:   SegmentOpLineTo,
+					Args: [3]fixed.Point26_6{p},
 				}
 				return true
 			}
 
 		} else {
 			if !g.on {
-				midp := midPoint(g.lastOffCurve, p)
 				g.seg = Segment{
 					Op: SegmentOpQuadTo,
-					Args: [6]fixed.Int26_6{
-						g.lastOffCurve.X,
-						g.lastOffCurve.Y,
-						midp.X,
-						midp.Y,
+					Args: [3]fixed.Point26_6{
+						g.lastOffCurve,
+						midPoint(g.lastOffCurve, p),
 					},
 				}
 				g.lastOffCurve = p
@@ -544,13 +515,8 @@
 				return true
 			} else {
 				g.seg = Segment{
-					Op: SegmentOpQuadTo,
-					Args: [6]fixed.Int26_6{
-						g.lastOffCurve.X,
-						g.lastOffCurve.Y,
-						p.X,
-						p.Y,
-					},
+					Op:   SegmentOpQuadTo,
+					Args: [3]fixed.Point26_6{g.lastOffCurve, p},
 				}
 				g.lastOffCurveValid = false
 				return true