From 22f1b5f81b4df8e1f0eae03d7c94f37e681a26db Mon Sep 17 00:00:00 2001 From: Nigel Tao Date: Fri, 8 Apr 2016 13:51:50 +1000 Subject: [PATCH] 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 --- font/plan9font/plan9font.go | 58 ++++++++++++++++++++------------ font/plan9font/plan9font_test.go | 24 +++++++++++++ 2 files changed, 60 insertions(+), 22 deletions(-) create mode 100644 font/plan9font/plan9font_test.go 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) + } + } +}