Make face.Glyph not allocate a new mask each time.

benchmark                 old ns/op     new ns/op     delta
BenchmarkDrawString-4     96914389      88683659      -8.49%

benchmark                 old allocs     new allocs     delta
BenchmarkDrawString-4     32287          0              -100.00%

benchmark                 old bytes     new bytes     delta
BenchmarkDrawString-4     1929619       156           -99.99%
This commit is contained in:
Nigel Tao 2015-08-23 21:11:02 +10:00
parent 2d84b72ff6
commit 9c46b87503

View file

@ -68,7 +68,11 @@ func NewFace(f *Font, opts Options) font.Face {
ymin := -int(b.YMax) >> 6
xmax := +int(b.XMax+63) >> 6
ymax := -int(b.YMin-63) >> 6
a.r.SetBounds(xmax-xmin, ymax-ymin)
a.maxw = xmax - xmin
a.maxh = ymax - ymin
a.mask = image.NewAlpha(image.Rect(0, 0, a.maxw, a.maxh))
a.r.SetBounds(a.maxw, a.maxh)
a.p = raster.NewAlphaSrcPainter(a.mask)
return a
}
@ -77,7 +81,11 @@ type face struct {
f *Font
hinting font.Hinting
scale fixed.Int26_6
mask *image.Alpha
r raster.Rasterizer
p raster.Painter
maxw int
maxh int
glyphBuf GlyphBuf
// TODO: clip rectangle?
@ -105,7 +113,7 @@ func (a *face) Glyph(dot fixed.Point26_6, r rune) (
ix, fx := int(dot.X>>6), dot.X&0x3f
iy, fy := int(dot.Y>>6), dot.Y&0x3f
advanceWidth, mask, offset, ok := a.rasterize(a.f.Index(r), fx, fy)
advanceWidth, offset, gw, gh, ok := a.rasterize(a.f.Index(r), fx, fy)
if !ok {
return fixed.Point26_6{}, image.Rectangle{}, nil, image.Point{}, false
}
@ -113,26 +121,26 @@ func (a *face) Glyph(dot fixed.Point26_6, r rune) (
X: dot.X + advanceWidth,
Y: dot.Y,
}
mb := mask.Bounds()
dr.Min = image.Point{
X: ix + offset.X,
Y: iy + offset.Y,
}
dr.Max = image.Point{
X: dr.Min.X + mb.Dx(),
Y: dr.Min.Y + mb.Dy(),
X: dr.Min.X + gw,
Y: dr.Min.Y + gh,
}
return newDot, dr, mask, image.Point{}, true
return newDot, dr, a.mask, image.Point{}, true
}
// rasterize returns the advance width, glyph mask and integer-pixel offset
// to render the given glyph at the given sub-pixel offsets.
// rasterize returns the advance width, integer-pixel offset to render at, and
// the width and height of the given glyph at the given sub-pixel offsets.
//
// The 26.6 fixed point arguments fx and fy must be in the range [0, 1).
func (a *face) rasterize(index Index, fx, fy fixed.Int26_6) (
fixed.Int26_6, *image.Alpha, image.Point, bool) {
advanceWidth fixed.Int26_6, offset image.Point, gw int, gh int, ok bool) {
if err := a.glyphBuf.Load(a.f, a.scale, index, a.hinting); err != nil {
return 0, nil, image.Point{}, false
return 0, image.Point{}, 0, 0, false
}
// Calculate the integer-pixel bounds for the glyph.
xmin := int(fx+fixed.Int26_6(a.glyphBuf.B.XMin)) >> 6
@ -140,7 +148,7 @@ func (a *face) rasterize(index Index, fx, fy fixed.Int26_6) (
xmax := int(fx+fixed.Int26_6(a.glyphBuf.B.XMax)+0x3f) >> 6
ymax := int(fy-fixed.Int26_6(a.glyphBuf.B.YMin)+0x3f) >> 6
if xmin > xmax || ymin > ymax {
return 0, nil, image.Point{}, false
return 0, image.Point{}, 0, 0, false
}
// A TrueType's glyph's nodes can have negative co-ordinates, but the
// rasterizer clips anything left of x=0 or above y=0. xmin and ymin are
@ -151,15 +159,20 @@ func (a *face) rasterize(index Index, fx, fy fixed.Int26_6) (
fy += fixed.Int26_6(-ymin << 6)
// Rasterize the glyph's vectors.
a.r.Clear()
clear(a.mask.Pix)
e0 := 0
for _, e1 := range a.glyphBuf.End {
a.drawContour(a.glyphBuf.Point[e0:e1], fx, fy)
e0 = e1
}
// TODO: don't allocate a new mask each time.
mask := image.NewAlpha(image.Rect(0, 0, xmax-xmin, ymax-ymin))
a.r.Rasterize(raster.NewAlphaSrcPainter(mask))
return fixed.Int26_6(a.glyphBuf.AdvanceWidth), mask, image.Point{xmin, ymin}, true
a.r.Rasterize(a.p)
return fixed.Int26_6(a.glyphBuf.AdvanceWidth), image.Point{xmin, ymin}, xmax-xmin, ymax-ymin, true
}
func clear(pix []byte) {
for i := range pix {
pix[i] = 0
}
}
// drawContour draws the given closed contour with the given offset.