提交 3cc74868 编写于 作者: N Nigel Tao

Use fixed.Rectangle26_6 instead of truetype.Bounds.

The previous "the endpoints are inclusive" comment seems confusing. It's true
that the bounding box's max X equals the right-most coordinate, which suggests
<= instead of <, but that node's coordinate is itself exclusive. Consider the
solid 1-pixel square: (0, 0), (64, 0), (64, 64), (0, 64) in fixed.Point26_6
coordinates. The right-most coordinate is 64, and the bounding box's max X
equals 64, but rasterizing that square only affects sub-pixels up to but not
including 64.

Instead, it seems accurate to follow the fixed.Rectangle26_6 description, in
that the max values are exclusive.
上级 7a598da6
...@@ -23,12 +23,12 @@ import ( ...@@ -23,12 +23,12 @@ import (
var fontfile = flag.String("fontfile", "../../testdata/luxisr.ttf", "filename of the ttf font") var fontfile = flag.String("fontfile", "../../testdata/luxisr.ttf", "filename of the ttf font")
func printBounds(b truetype.Bounds) { func printBounds(b fixed.Rectangle26_6) {
fmt.Printf("XMin:%d YMin:%d XMax:%d YMax:%d\n", b.XMin, b.YMin, b.XMax, b.YMax) fmt.Printf("Min.X:%d Min.Y:%d Max.X:%d Max.Y:%d\n", b.Min.X, b.Min.Y, b.Max.X, b.Max.Y)
} }
func printGlyph(g *truetype.GlyphBuf) { func printGlyph(g *truetype.GlyphBuf) {
printBounds(g.B) printBounds(g.Bounds)
fmt.Print("Points:\n---\n") fmt.Print("Points:\n---\n")
e := 0 e := 0
for i, p := range g.Point { for i, p := range g.Point {
......
...@@ -165,10 +165,10 @@ func (c *Context) rasterize(glyph truetype.Index, fx, fy fixed.Int26_6) ( ...@@ -165,10 +165,10 @@ func (c *Context) rasterize(glyph truetype.Index, fx, fy fixed.Int26_6) (
return 0, nil, image.Point{}, err return 0, nil, image.Point{}, err
} }
// Calculate the integer-pixel bounds for the glyph. // Calculate the integer-pixel bounds for the glyph.
xmin := int(fx+c.glyphBuf.B.XMin) >> 6 xmin := int(fx+c.glyphBuf.Bounds.Min.X) >> 6
ymin := int(fy-c.glyphBuf.B.YMax) >> 6 ymin := int(fy-c.glyphBuf.Bounds.Max.Y) >> 6
xmax := int(fx+c.glyphBuf.B.XMax+0x3f) >> 6 xmax := int(fx+c.glyphBuf.Bounds.Max.X+0x3f) >> 6
ymax := int(fy-c.glyphBuf.B.YMin+0x3f) >> 6 ymax := int(fy-c.glyphBuf.Bounds.Min.Y+0x3f) >> 6
if xmin > xmax || ymin > ymax { if xmin > xmax || ymin > ymax {
return 0, nil, image.Point{}, errors.New("freetype: negative sized glyph") return 0, nil, image.Point{}, errors.New("freetype: negative sized glyph")
} }
...@@ -266,10 +266,10 @@ func (c *Context) recalc() { ...@@ -266,10 +266,10 @@ func (c *Context) recalc() {
} else { } else {
// Set the rasterizer's bounds to be big enough to handle the largest glyph. // Set the rasterizer's bounds to be big enough to handle the largest glyph.
b := c.f.Bounds(c.scale) b := c.f.Bounds(c.scale)
xmin := +int(b.XMin) >> 6 xmin := +int(b.Min.X) >> 6
ymin := -int(b.YMax) >> 6 ymin := -int(b.Max.Y) >> 6
xmax := +int(b.XMax+63) >> 6 xmax := +int(b.Max.X+63) >> 6
ymax := -int(b.YMin-63) >> 6 ymax := -int(b.Min.Y-63) >> 6
c.r.SetBounds(xmax-xmin, ymax-ymin) c.r.SetBounds(xmax-xmin, ymax-ymin)
} }
for i := range c.cache { for i := range c.cache {
......
...@@ -188,10 +188,10 @@ func NewFace(f *Font, opts *Options) font.Face { ...@@ -188,10 +188,10 @@ func NewFace(f *Font, opts *Options) font.Face {
// Set the rasterizer's bounds to be big enough to handle the largest glyph. // Set the rasterizer's bounds to be big enough to handle the largest glyph.
b := f.Bounds(a.scale) b := f.Bounds(a.scale)
xmin := +int(b.XMin) >> 6 xmin := +int(b.Min.X) >> 6
ymin := -int(b.YMax) >> 6 ymin := -int(b.Max.Y) >> 6
xmax := +int(b.XMax+63) >> 6 xmax := +int(b.Max.X+63) >> 6
ymax := -int(b.YMin-63) >> 6 ymax := -int(b.Min.Y-63) >> 6
a.maxw = xmax - xmin a.maxw = xmax - xmin
a.maxh = ymax - ymin a.maxh = ymax - ymin
a.masks = image.NewAlpha(image.Rect(0, 0, a.maxw, a.maxh*len(a.cache))) a.masks = image.NewAlpha(image.Rect(0, 0, a.maxw, a.maxh*len(a.cache)))
...@@ -291,10 +291,10 @@ func (a *face) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.In ...@@ -291,10 +291,10 @@ func (a *face) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.In
if err := a.glyphBuf.Load(a.f, a.scale, a.f.Index(r), a.hinting); err != nil { if err := a.glyphBuf.Load(a.f, a.scale, a.f.Index(r), a.hinting); err != nil {
return fixed.Rectangle26_6{}, 0, false return fixed.Rectangle26_6{}, 0, false
} }
xmin := +a.glyphBuf.B.XMin xmin := +a.glyphBuf.Bounds.Min.X
ymin := -a.glyphBuf.B.YMax ymin := -a.glyphBuf.Bounds.Max.Y
xmax := +a.glyphBuf.B.XMax xmax := +a.glyphBuf.Bounds.Max.X
ymax := -a.glyphBuf.B.YMin ymax := -a.glyphBuf.Bounds.Min.Y
if xmin > xmax || ymin > ymax { if xmin > xmax || ymin > ymax {
return fixed.Rectangle26_6{}, 0, false return fixed.Rectangle26_6{}, 0, false
} }
...@@ -326,10 +326,10 @@ func (a *face) rasterize(index Index, fx, fy fixed.Int26_6) (v cacheVal, ok bool ...@@ -326,10 +326,10 @@ func (a *face) rasterize(index Index, fx, fy fixed.Int26_6) (v cacheVal, ok bool
return cacheVal{}, false return cacheVal{}, false
} }
// Calculate the integer-pixel bounds for the glyph. // Calculate the integer-pixel bounds for the glyph.
xmin := int(fx+a.glyphBuf.B.XMin) >> 6 xmin := int(fx+a.glyphBuf.Bounds.Min.X) >> 6
ymin := int(fy-a.glyphBuf.B.YMax) >> 6 ymin := int(fy-a.glyphBuf.Bounds.Max.Y) >> 6
xmax := int(fx+a.glyphBuf.B.XMax+0x3f) >> 6 xmax := int(fx+a.glyphBuf.Bounds.Max.X+0x3f) >> 6
ymax := int(fy-a.glyphBuf.B.YMin+0x3f) >> 6 ymax := int(fy-a.glyphBuf.Bounds.Min.Y+0x3f) >> 6
if xmin > xmax || ymin > ymax { if xmin > xmax || ymin > ymax {
return cacheVal{}, false return cacheVal{}, false
} }
......
...@@ -26,8 +26,8 @@ type Point struct { ...@@ -26,8 +26,8 @@ type Point struct {
type GlyphBuf struct { type GlyphBuf struct {
// AdvanceWidth is the glyph's advance width. // AdvanceWidth is the glyph's advance width.
AdvanceWidth fixed.Int26_6 AdvanceWidth fixed.Int26_6
// B is the glyph's bounding box. // Bounds is the glyph's bounding box.
B Bounds Bounds fixed.Rectangle26_6
// Point contains all Points from all contours of the glyph. If // Point contains all Points from all contours of the glyph. If
// hinting was used to load a glyph then Unhinted contains those // 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 InFontUnits contains those
...@@ -131,40 +131,40 @@ func (g *GlyphBuf) Load(f *Font, scale fixed.Int26_6, i Index, h font.Hinting) e ...@@ -131,40 +131,40 @@ func (g *GlyphBuf) Load(f *Font, scale fixed.Int26_6, i Index, h font.Hinting) e
} }
g.AdvanceWidth = advanceWidth g.AdvanceWidth = advanceWidth
// Set g.B to the 'control box', which is the bounding box of the Bézier // Set g.Bounds to the 'control box', which is the bounding box of the
// curves' control points. This is easier to calculate, no smaller than // Bézier curves' control points. This is easier to calculate, no smaller
// and often equal to the tightest possible bounding box of the curves // 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 // 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 // the nominal bounding box in the glyf data as the hinting process and
// phantom point adjustment may move points outside of that box. // phantom point adjustment may move points outside of that box.
if len(g.Point) == 0 { if len(g.Point) == 0 {
g.B = Bounds{} g.Bounds = fixed.Rectangle26_6{}
} else { } else {
p := g.Point[0] p := g.Point[0]
g.B.XMin = p.X g.Bounds.Min.X = p.X
g.B.XMax = p.X g.Bounds.Max.X = p.X
g.B.YMin = p.Y g.Bounds.Min.Y = p.Y
g.B.YMax = p.Y g.Bounds.Max.Y = p.Y
for _, p := range g.Point[1:] { for _, p := range g.Point[1:] {
if g.B.XMin > p.X { if g.Bounds.Min.X > p.X {
g.B.XMin = p.X g.Bounds.Min.X = p.X
} else if g.B.XMax < p.X { } else if g.Bounds.Max.X < p.X {
g.B.XMax = p.X g.Bounds.Max.X = p.X
} }
if g.B.YMin > p.Y { if g.Bounds.Min.Y > p.Y {
g.B.YMin = p.Y g.Bounds.Min.Y = p.Y
} else if g.B.YMax < p.Y { } else if g.Bounds.Max.Y < p.Y {
g.B.YMax = p.Y g.Bounds.Max.Y = p.Y
} }
} }
// Snap the box to the grid, if hinting is on. // Snap the box to the grid, if hinting is on.
if h != font.HintingNone { if h != font.HintingNone {
g.B.XMin &^= 63 g.Bounds.Min.X &^= 63
g.B.YMin &^= 63 g.Bounds.Min.Y &^= 63
g.B.XMax += 63 g.Bounds.Max.X += 63
g.B.XMax &^= 63 g.Bounds.Max.X &^= 63
g.B.YMax += 63 g.Bounds.Max.Y += 63
g.B.YMax &^= 63 g.Bounds.Max.Y &^= 63
} }
} }
return nil return nil
......
...@@ -26,12 +26,6 @@ import ( ...@@ -26,12 +26,6 @@ import (
// An Index is a Font's index of a rune. // An Index is a Font's index of a rune.
type Index uint16 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 fixed.Int26_6
}
// An HMetric holds the horizontal metrics of a single glyph. // An HMetric holds the horizontal metrics of a single glyph.
type HMetric struct { type HMetric struct {
AdvanceWidth, LeftSideBearing fixed.Int26_6 AdvanceWidth, LeftSideBearing fixed.Int26_6
...@@ -108,7 +102,7 @@ type Font struct { ...@@ -108,7 +102,7 @@ type Font struct {
locaOffsetFormat int locaOffsetFormat int
nGlyph, nHMetric, nKern int nGlyph, nHMetric, nKern int
fUnitsPerEm int32 fUnitsPerEm int32
bounds Bounds bounds fixed.Rectangle26_6
// Values from the maxp section. // Values from the maxp section.
maxTwilightPoints, maxStorage, maxFunctionDefs, maxStackElements uint16 maxTwilightPoints, maxStorage, maxFunctionDefs, maxStackElements uint16
} }
...@@ -227,10 +221,10 @@ func (f *Font) parseHead() error { ...@@ -227,10 +221,10 @@ func (f *Font) parseHead() error {
return FormatError(fmt.Sprintf("bad head length: %d", len(f.head))) return FormatError(fmt.Sprintf("bad head length: %d", len(f.head)))
} }
f.fUnitsPerEm = int32(u16(f.head, 18)) f.fUnitsPerEm = int32(u16(f.head, 18))
f.bounds.XMin = fixed.Int26_6(int16(u16(f.head, 36))) f.bounds.Min.X = fixed.Int26_6(int16(u16(f.head, 36)))
f.bounds.YMin = fixed.Int26_6(int16(u16(f.head, 38))) f.bounds.Min.Y = fixed.Int26_6(int16(u16(f.head, 38)))
f.bounds.XMax = fixed.Int26_6(int16(u16(f.head, 40))) f.bounds.Max.X = fixed.Int26_6(int16(u16(f.head, 40)))
f.bounds.YMax = fixed.Int26_6(int16(u16(f.head, 42))) f.bounds.Max.Y = fixed.Int26_6(int16(u16(f.head, 42)))
switch i := u16(f.head, 50); i { switch i := u16(f.head, 50); i {
case 0: case 0:
f.locaOffsetFormat = locaOffsetFormatShort f.locaOffsetFormat = locaOffsetFormatShort
...@@ -317,12 +311,12 @@ func (f *Font) scale(x fixed.Int26_6) fixed.Int26_6 { ...@@ -317,12 +311,12 @@ func (f *Font) scale(x fixed.Int26_6) fixed.Int26_6 {
} }
// Bounds returns the union of a Font's glyphs' bounds. // Bounds returns the union of a Font's glyphs' bounds.
func (f *Font) Bounds(scale fixed.Int26_6) Bounds { func (f *Font) Bounds(scale fixed.Int26_6) fixed.Rectangle26_6 {
b := f.bounds b := f.bounds
b.XMin = f.scale(scale * b.XMin) b.Min.X = f.scale(scale * b.Min.X)
b.YMin = f.scale(scale * b.YMin) b.Min.Y = f.scale(scale * b.Min.Y)
b.XMax = f.scale(scale * b.XMax) b.Max.X = f.scale(scale * b.Max.X)
b.YMax = f.scale(scale * b.YMax) b.Max.Y = f.scale(scale * b.Max.Y)
return b return b
} }
......
...@@ -33,6 +33,19 @@ func parseTestdataFont(name string) (f *Font, testdataIsOptional bool, err error ...@@ -33,6 +33,19 @@ func parseTestdataFont(name string) (f *Font, testdataIsOptional bool, err error
return f, false, nil return f, false, nil
} }
func mkBounds(minX, minY, maxX, maxY fixed.Int26_6) fixed.Rectangle26_6 {
return fixed.Rectangle26_6{
Min: fixed.Point26_6{
X: minX,
Y: minY,
},
Max: fixed.Point26_6{
X: maxX,
Y: maxY,
},
}
}
// TestParse tests that the luxisr.ttf metrics and glyphs are parsed correctly. // TestParse tests that the luxisr.ttf metrics and glyphs are parsed correctly.
// The numerical values can be manually verified by examining luxisr.ttx. // The numerical values can be manually verified by examining luxisr.ttx.
func TestParse(t *testing.T) { func TestParse(t *testing.T) {
...@@ -44,7 +57,7 @@ func TestParse(t *testing.T) { ...@@ -44,7 +57,7 @@ func TestParse(t *testing.T) {
t.Errorf("FUnitsPerEm: got %v, want %v", got, want) t.Errorf("FUnitsPerEm: got %v, want %v", got, want)
} }
fupe := fixed.Int26_6(f.FUnitsPerEm()) fupe := fixed.Int26_6(f.FUnitsPerEm())
if got, want := f.Bounds(fupe), (Bounds{-441, -432, 2024, 2033}); got != want { if got, want := f.Bounds(fupe), mkBounds(-441, -432, 2024, 2033); got != want {
t.Errorf("Bounds: got %v, want %v", got, want) t.Errorf("Bounds: got %v, want %v", got, want)
} }
...@@ -69,12 +82,12 @@ func TestParse(t *testing.T) { ...@@ -69,12 +82,12 @@ func TestParse(t *testing.T) {
t.Fatalf("Load: %v", err) t.Fatalf("Load: %v", err)
} }
g0 := &GlyphBuf{ g0 := &GlyphBuf{
B: g.B, Bounds: g.Bounds,
Point: g.Point, Point: g.Point,
End: g.End, End: g.End,
} }
g1 := &GlyphBuf{ g1 := &GlyphBuf{
B: Bounds{19, 0, 1342, 1480}, Bounds: mkBounds(19, 0, 1342, 1480),
Point: []Point{ Point: []Point{
{19, 0, 51}, {19, 0, 51},
{581, 1480, 1}, {581, 1480, 1},
...@@ -200,7 +213,7 @@ func TestIndex(t *testing.T) { ...@@ -200,7 +213,7 @@ func TestIndex(t *testing.T) {
type scalingTestData struct { type scalingTestData struct {
advanceWidth fixed.Int26_6 advanceWidth fixed.Int26_6
bounds Bounds bounds fixed.Rectangle26_6
points []Point points []Point
} }
...@@ -221,10 +234,10 @@ func scalingTestParse(line string) (ret scalingTestData) { ...@@ -221,10 +234,10 @@ func scalingTestParse(line string) (ret scalingTestData) {
prefix, line := line[:i], line[i+1:] prefix, line := line[:i], line[i+1:]
prefix, ret.advanceWidth = next(prefix) prefix, ret.advanceWidth = next(prefix)
prefix, ret.bounds.XMin = next(prefix) prefix, ret.bounds.Min.X = next(prefix)
prefix, ret.bounds.YMin = next(prefix) prefix, ret.bounds.Min.Y = next(prefix)
prefix, ret.bounds.XMax = next(prefix) prefix, ret.bounds.Max.X = next(prefix)
prefix, ret.bounds.YMax = next(prefix) prefix, ret.bounds.Max.Y = next(prefix)
ret.points = make([]Point, 0, 1+strings.Count(line, ",")) ret.points = make([]Point, 0, 1+strings.Count(line, ","))
for len(line) > 0 { for len(line) > 0 {
...@@ -327,7 +340,7 @@ func testScaling(t *testing.T, h font.Hinting) { ...@@ -327,7 +340,7 @@ func testScaling(t *testing.T, h font.Hinting) {
} }
got := scalingTestData{ got := scalingTestData{
advanceWidth: glyphBuf.AdvanceWidth, advanceWidth: glyphBuf.AdvanceWidth,
bounds: glyphBuf.B, bounds: glyphBuf.Bounds,
points: glyphBuf.Point, points: glyphBuf.Point,
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册