git clone https://git.lucas.co/go_mono.git
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)
+ }
+ }
+}