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>
This commit is contained in:
parent
f36ba34967
commit
ce0faa1867
|
@ -50,29 +50,29 @@ func ExampleRasterizeGlyph() {
|
|||
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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -999,46 +999,39 @@ func t2CMask(p *psInterpreter) error {
|
|||
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}},
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -902,6 +902,8 @@ type LoadGlyphOptions struct {
|
|||
//
|
||||
// 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 @@ func (f *Font) LoadGlyph(b *Buffer, x GlyphIndex, ppem fixed.Int26_6, opts *Load
|
|||
// 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 @@ func (b *Buffer) view(src *source, offset, length int) ([]byte, error) {
|
|||
|
||||
// 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 @@ const (
|
|||
)
|
||||
|
||||
// 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),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,31 +15,35 @@ import (
|
|||
"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 transform(txx, txy, tyx, tyy int16, dx, dy fixed.Int26_6, s Segment) Segmen
|
|||
}
|
||||
|
||||
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)
|
||||
|
|
|
@ -414,44 +414,28 @@ func (g *glyfIter) close() {
|
|||
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 @@ func (g *glyfIter) nextSegment() (ok bool) {
|
|||
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 @@ func (g *glyfIter) nextSegment() (ok bool) {
|
|||
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 @@ func (g *glyfIter) nextSegment() (ok bool) {
|
|||
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 @@ func (g *glyfIter) nextSegment() (ok bool) {
|
|||
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
|
||||
|
|
Loading…
Reference in New Issue
Block a user