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