diff options
author | Nigel Tao <nigeltao@golang.org> | 2010-05-10 10:32:08 +1000 |
---|---|---|
committer | Nigel Tao <nigeltao@golang.org> | 2010-05-10 10:32:08 +1000 |
commit | 03339a24bc08840777d0c72888c9779661fa83bb (patch) | |
tree | 867807836b77420de13380fba9ce16870d44d4f4 /src/pkg/exp | |
parent | 8675f36b64fd8dc030b7397b0fb6f80196fbe7d9 (diff) | |
download | golang-03339a24bc08840777d0c72888c9779661fa83bb.tar.gz |
exp/draw fast path for glyph images.
To draw.Draw a 32x32 image.Alpha 10000 times,
Before: 633ms with 10000 mallocs
After: 49ms with 0 mallocs
These times are just blitting an image.Alpha, and do not include
rasterizing a glyph's vector contours to an image.Alpha.
The "generic" test case in draw_test.go tests this fast path.
R=rsc
CC=golang-dev
http://codereview.appspot.com/1122043
Diffstat (limited to 'src/pkg/exp')
-rw-r--r-- | src/pkg/exp/draw/draw.go | 44 |
1 files changed, 43 insertions, 1 deletions
diff --git a/src/pkg/exp/draw/draw.go b/src/pkg/exp/draw/draw.go index bf5a08479..0589dde5e 100644 --- a/src/pkg/exp/draw/draw.go +++ b/src/pkg/exp/draw/draw.go @@ -62,7 +62,12 @@ func DrawMask(dst Image, r Rectangle, src image.Image, sp Point, mask image.Imag // Fast paths for special cases. If none of them apply, then we fall back to a general but slow implementation. if dst0, ok := dst.(*image.RGBA); ok { if op == Over { - // TODO(nigeltao): Implement a fast path for font glyphs (i.e. when mask is an image.Alpha). + if mask0, ok := mask.(*image.Alpha); ok { + if src0, ok := src.(image.ColorImage); ok { + drawGlyphOver(dst0, r, src0, mask0, mp) + return + } + } } else { if mask == nil { if src0, ok := src.(image.ColorImage); ok { @@ -147,6 +152,43 @@ func DrawMask(dst Image, r Rectangle, src image.Image, sp Point, mask image.Imag } } +func drawGlyphOver(dst *image.RGBA, r Rectangle, src image.ColorImage, mask *image.Alpha, mp Point) { + x0, x1 := r.Min.X, r.Max.X + y0, y1 := r.Min.Y, r.Max.Y + cr, cg, cb, ca := src.RGBA() + cr >>= 16 + cg >>= 16 + cb >>= 16 + ca >>= 16 + for y, my := y0, mp.Y; y != y1; y, my = y+1, my+1 { + for x, mx := x0, mp.X; x != x1; x, mx = x+1, mx+1 { + ma := uint32(mask.Pixel[my][mx].A) + if ma == 0 { + continue + } + ma |= ma << 8 + dr := uint32(dst.Pixel[y][x].R) + dg := uint32(dst.Pixel[y][x].G) + db := uint32(dst.Pixel[y][x].B) + da := uint32(dst.Pixel[y][x].A) + // dr, dg, db and da are all 8-bit color at the moment, ranging in [0,255]. + // We work in 16-bit color, and so would normally do: + // dr |= dr << 8 + // and similarly for dg, db and da, but instead we multiply a + // (which is a 16-bit color, ranging in [0,65535]) by 0x101. + // This yields the same result, but is fewer arithmetic operations. + const M = 1<<16 - 1 + a := M - (ca * ma / M) + a *= 0x101 + dr = (dr*a + cr*ma) / M + dg = (dg*a + cg*ma) / M + db = (db*a + cb*ma) / M + da = (da*a + ca*ma) / M + dst.Pixel[y][x] = image.RGBAColor{uint8(dr >> 8), uint8(dg >> 8), uint8(db >> 8), uint8(da >> 8)} + } + } +} + func drawFill(dst *image.RGBA, r Rectangle, src image.ColorImage) { if r.Dy() < 1 { return |