internal/task: switch to opentype for TTF font rendering

When working on CL 358897, I forgot that there's already an SFNT font
parser and glyph rasterizer available in the x/image module. Using it
produces an equivalent final image, and allows x/build to depend on 1
less module.

Also add a regression test for drawTerminal.

Updates golang/go#47403.

Change-Id: I77166a2180a7db9c81a30f77b12bb20dcd21bd8b
Reviewed-on: https://go-review.googlesource.com/c/build/+/388834
Trust: Dmitri Shuralyov <dmitshur@golang.org>
Run-TryBot: Dmitri Shuralyov <dmitshur@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Heschi Kreinick <heschi@google.com>
This commit is contained in:
Dmitri Shuralyov 2021-12-31 15:54:50 -05:00
Родитель 1c23949106
Коммит 00ab59d78d
5 изменённых файлов: 85 добавлений и 6 удалений

1
go.mod
Просмотреть файл

@ -19,7 +19,6 @@ require (
github.com/esimov/stackblur-go v1.0.1
github.com/gliderlabs/ssh v0.3.3
github.com/golang-migrate/migrate/v4 v4.15.0-beta.3
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
github.com/golang/protobuf v1.5.2
github.com/google/go-cmp v0.5.6
github.com/google/go-github v17.0.0+incompatible

2
go.sum
Просмотреть файл

@ -272,8 +272,6 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69
github.com/golang-migrate/migrate/v4 v4.15.0-beta.3 h1:tqLMcxs6gB6+b2Jhwao4s2sNMSKku+0rjtBtKucKWkg=
github.com/golang-migrate/migrate/v4 v4.15.0-beta.3/go.mod h1:g9qbiDvB47WyrRnNu2t2gMZFNHKnatsYRxsGZbCi4EM=
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g=
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=

Двоичные данные
internal/task/testdata/terminal.png поставляемый Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 107 KiB

Просмотреть файл

@ -25,11 +25,11 @@ import (
"github.com/dghubble/oauth1"
"github.com/esimov/stackblur-go"
"github.com/golang/freetype/truetype"
"golang.org/x/build/internal/secret"
"golang.org/x/build/internal/workflow"
"golang.org/x/image/font"
"golang.org/x/image/font/gofont/gomono"
"golang.org/x/image/font/opentype"
"golang.org/x/image/math/fixed"
)
@ -496,7 +496,7 @@ func (f golangorgDLFile) GOARCH() string {
// with the given text displayed.
func drawTerminal(text string) (image.Image, error) {
// Load font from TTF data.
f, err := truetype.Parse(gomono.TTF)
f, err := opentype.Parse(gomono.TTF)
if err != nil {
return nil, err
}
@ -521,7 +521,11 @@ func drawTerminal(text string) (image.Image, error) {
roundedRect(image.Rect(50, 80, width-50, height-80), 10), image.Point{}, draw.Over)
// Text.
d := font.Drawer{Dst: m, Src: image.White, Face: truetype.NewFace(f, &truetype.Options{Size: 24})}
face, err := opentype.NewFace(f, &opentype.FaceOptions{Size: 24, DPI: 72})
if err != nil {
return nil, err
}
d := font.Drawer{Dst: m, Src: image.White, Face: face}
const lineHeight = 32
for n, line := range strings.Split(text, "\n") {
d.Dot = fixed.P(80, 135+n*lineHeight)

Просмотреть файл

@ -7,10 +7,15 @@ package task
import (
"bytes"
"context"
"flag"
"fmt"
"image"
"image/png"
"io"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"testing"
"golang.org/x/build/internal/workflow"
@ -175,6 +180,79 @@ func (f fmtWriter) Printf(format string, v ...interface{}) {
fmt.Fprintf(f.w, format, v...)
}
var updateFlag = flag.Bool("update", false, "Update golden files.")
func TestDrawTerminal(t *testing.T) {
got, err := drawTerminal(`$ go install golang.org/dl/go1.18beta1@latest
$ go1.18beta1 download
Downloaded 0.0% ( 0 / 111109966 bytes) ...
Downloaded 50.0% ( 55554983 / 111109966 bytes) ...
Downloaded 100.0% (111109966 / 111109966 bytes)
Unpacking go1.18beta1.linux-s390x.tar.gz ...
Success. You may now run 'go1.18beta1'
$ go1.18beta1 version
go version go1.18beta1 linux/s390x`)
if err != nil {
t.Fatalf("drawTerminal: got error=%v, want nil", err)
}
if *updateFlag {
encodePNG(t, filepath.Join("testdata", "terminal.png"), got)
return
}
want := decodePNG(t, filepath.Join("testdata", "terminal.png"))
if !got.Bounds().Eq(want.Bounds()) {
t.Fatalf("drawTerminal: got image bounds=%v, want %v", got.Bounds(), want.Bounds())
}
diff := func(a, b uint32) uint64 {
if a < b {
return uint64(b - a)
}
return uint64(a - b)
}
var total uint64
for y := 0; y < want.Bounds().Dy(); y++ {
for x := 0; x < want.Bounds().Dx(); x++ {
r0, g0, b0, a0 := got.At(x, y).RGBA()
r1, g1, b1, a1 := want.At(x, y).RGBA()
const D = 0xffff * 20 / 100 // Diff threshold of 20% for RGB color components.
if diff(r0, r1) > D || diff(g0, g1) > D || diff(b0, b1) > D || a0 != a1 {
t.Errorf("at (%d, %d):\n got RGBA %v\nwant RGBA %v", x, y, got.At(x, y), want.At(x, y))
}
total += diff(r0, r1) + diff(g0, g1) + diff(b0, b1)
}
}
if testing.Verbose() {
t.Logf("average pixel color diff: %v%%", 100*float64(total)/float64(0xffff*want.Bounds().Dx()*want.Bounds().Dy()))
}
}
func encodePNG(t *testing.T, name string, m image.Image) {
t.Helper()
var buf bytes.Buffer
err := (&png.Encoder{CompressionLevel: png.BestCompression}).Encode(&buf, m)
if err != nil {
t.Fatal(err)
}
err = os.WriteFile(name, buf.Bytes(), 0644)
if err != nil {
t.Fatal(err)
}
}
func decodePNG(t *testing.T, name string) image.Image {
t.Helper()
f, err := os.Open(name)
if err != nil {
t.Fatal(err)
}
defer f.Close()
m, err := png.Decode(f)
if err != nil {
t.Fatal(err)
}
return m
}
func TestPostTweet(t *testing.T) {
mux := http.NewServeMux()
mux.HandleFunc("upload.twitter.com/1.1/media/upload.json", func(w http.ResponseWriter, req *http.Request) {