]> Cypherpunks repositories - gostls13.git/commitdiff
exp/draw fast path for glyph images.
authorNigel Tao <nigeltao@golang.org>
Mon, 10 May 2010 00:32:08 +0000 (10:32 +1000)
committerNigel Tao <nigeltao@golang.org>
Mon, 10 May 2010 00:32:08 +0000 (10:32 +1000)
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

index bf5a08479e12f8987d1bafdf73eb7e7ee6e61fde..0589dde5ef08c5eda4aa9c6ffd3c9d26b7834585 100644 (file)
@@ -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