From d7a6d22852168c94986ec1a4ca81efdfd8752970 Mon Sep 17 00:00:00 2001 From: Nigel Tao Date: Mon, 10 May 2010 10:32:08 +1000 Subject: [PATCH] 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 https://golang.org/cl/1122043 --- src/pkg/exp/draw/draw.go | 44 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/src/pkg/exp/draw/draw.go b/src/pkg/exp/draw/draw.go index bf5a08479e1..0589dde5ef0 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