git.lucas.co / go_mono
git clone https://git.lucas.co/go_mono.git

commit22f1b5f81b4df8e1f0eae03d7c94f37e681a26db
parent7a320489ae
authorNigel Tao <nigeltao@golang.org>
date2016-04-08 13:51
font/plan9font: optimize the conversion to image.Alpha.

Also remove the dependency on the image/draw package. That package will
give the right answer for arbitrary source images, including those of
type plan9Image, but doing the conversion directly avoids bouncing uint8
or color.Alpha values through the general-purpose draw.Image,
image.Image and color.Color interfaces.

It is possible to optimize this even further, but this will do for now.

benchmark                   old ns/op     new ns/op     delta
BenchmarkParseSubfont-8     2298492       492443        -78.58%

Change-Id: Iea9436bffa097a1ba0052dbabf21516bce8b61e0
Reviewed-on: https://go-review.googlesource.com/21693
Reviewed-by: Nigel Tao <nigeltao@golang.org>

 font/plan9font/plan9font.go      | 58 +++++++++++++++++++++++++---------------
 font/plan9font/plan9font_test.go | 24 +++++++++++++++++
 2 files changed, 60 insertions(+), 22 deletions(-)

diff --git a/font/plan9font/plan9font.go b/font/plan9font/plan9font.go
index cd688b1..af57c55 100644
--- a/font/plan9font/plan9font.go
+++ b/font/plan9font/plan9font.go
@@ -13,7 +13,6 @@ import (
 	"fmt"
 	"image"
 	"image/color"
-	"image/draw"
 	"log"
 	"strconv"
 	"strings"
@@ -297,8 +296,18 @@ func ParseSubfont(data []byte, firstRune rune) (font.Face, error) {
 	if len(data) != 6*(n+1) {
 		return nil, errors.New("plan9font: invalid subfont: data length mismatch")
 	}
+
+	// Convert from plan9Image to image.Alpha, as the standard library's
+	// image/draw package works best when glyph masks are of that type.
 	img := image.NewAlpha(m.Bounds())
-	draw.Draw(img, img.Bounds(), m, image.ZP, draw.Over)
+	for y := img.Rect.Min.Y; y < img.Rect.Max.Y; y++ {
+		i := img.PixOffset(img.Rect.Min.X, y)
+		for x := img.Rect.Min.X; x < img.Rect.Max.X; x++ {
+			img.Pix[i] = m.at(x, y)
+			i++
+		}
+	}
+
 	return &subface{
 		firstRune: firstRune,
 		n:         n,
@@ -339,30 +348,35 @@ func (m *plan9Image) ColorModel() color.Model { return color.AlphaModel }
 
 func (m *plan9Image) At(x, y int) color.Color {
 	if (image.Point{x, y}).In(m.rect) {
-		b := m.pix[m.byteoffset(x, y)]
-		switch m.depth {
-		case 1:
-			// CGrey, 1.
-			mask := uint8(1 << uint8(7-x&7))
-			if (b & mask) != 0 {
-				return color.Alpha{0xff}
-			}
-			return color.Alpha{0x00}
-		case 2:
-			// CGrey, 2.
-			shift := uint(x&3) << 1
-			// Place pixel at top of word.
-			y := b << shift
-			y &= 0xc0
-			// Replicate throughout.
-			y |= y >> 2
-			y |= y >> 4
-			return color.Alpha{y}
-		}
+		return color.Alpha{m.at(x, y)}
 	}
 	return color.Alpha{0x00}
 }
 
+func (m *plan9Image) at(x, y int) uint8 {
+	b := m.pix[m.byteoffset(x, y)]
+	switch m.depth {
+	case 1:
+		// CGrey, 1.
+		mask := uint8(1 << uint8(7-x&7))
+		if (b & mask) != 0 {
+			return 0xff
+		}
+		return 0
+	case 2:
+		// CGrey, 2.
+		shift := uint(x&3) << 1
+		// Place pixel at top of word.
+		y := b << shift
+		y &= 0xc0
+		// Replicate throughout.
+		y |= y >> 2
+		y |= y >> 4
+		return y
+	}
+	return 0
+}
+
 var compressed = []byte("compressed\n")
 
 func parseImage(data []byte) (remainingData []byte, m *plan9Image, retErr error) {
diff --git a/font/plan9font/plan9font_test.go b/font/plan9font/plan9font_test.go
new file mode 100644
index 0000000..23393a1
--- /dev/null
+++ b/font/plan9font/plan9font_test.go
@@ -0,0 +1,24 @@
+// Copyright 2016 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package plan9font
+
+import (
+	"io/ioutil"
+	"path/filepath"
+	"testing"
+)
+
+func BenchmarkParseSubfont(b *testing.B) {
+	subfontData, err := ioutil.ReadFile(filepath.FromSlash("../testdata/fixed/7x13.0000"))
+	if err != nil {
+		b.Fatal(err)
+	}
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		if _, err := ParseSubfont(subfontData, 0); err != nil {
+			b.Fatal(err)
+		}
+	}
+}