text: Replace MeasureString with BoundString

Fixes #1272
This commit is contained in:
Hajime Hoshi 2020-07-30 00:19:08 +09:00
parent bb3a4cda22
commit 481c160c2a
3 changed files with 132 additions and 28 deletions

View File

@ -37,12 +37,13 @@ const (
screenHeight = 480 screenHeight = 480
) )
const sampleText = `The quick brown fox jumps over the lazy dog.`
var ( var (
sampleText = `The quick brown fox jumps over the lazy dog.`
mplusNormalFont font.Face mplusNormalFont font.Face
mplusBigFont font.Face mplusBigFont font.Face
jaKanjis = []rune{}
) )
var jaKanjis = []rune{}
func init() { func init() {
// table is the list of Japanese Kanji characters in a part of JIS X 0208. // table is the list of Japanese Kanji characters in a part of JIS X 0208.

107
examples/text/main.go Normal file
View File

@ -0,0 +1,107 @@
// Copyright 2020 The Ebiten Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// +build example jsgo
package main
import (
"image/color"
"log"
"math/rand"
"time"
"github.com/golang/freetype/truetype"
"golang.org/x/image/font"
"github.com/hajimehoshi/ebiten"
"github.com/hajimehoshi/ebiten/ebitenutil"
"github.com/hajimehoshi/ebiten/examples/resources/fonts"
"github.com/hajimehoshi/ebiten/text"
)
const (
screenWidth = 640
screenHeight = 480
)
const sampleText = `The quick brown fox jumps
over the lazy dog.`
var (
mplusNormalFont font.Face
mplusBigFont font.Face
)
func init() {
tt, err := truetype.Parse(fonts.MPlus1pRegular_ttf)
if err != nil {
log.Fatal(err)
}
const dpi = 72
mplusNormalFont = truetype.NewFace(tt, &truetype.Options{
Size: 24,
DPI: dpi,
Hinting: font.HintingFull,
})
mplusBigFont = truetype.NewFace(tt, &truetype.Options{
Size: 32,
DPI: dpi,
Hinting: font.HintingFull,
})
}
func init() {
rand.Seed(time.Now().UnixNano())
}
type Game struct {
counter int
kanjiText []rune
kanjiTextColor color.RGBA
}
func (g *Game) Update(screen *ebiten.Image) error {
return nil
}
func (g *Game) Draw(screen *ebiten.Image) {
gray := color.RGBA{0x80, 0x80, 0x80, 0xff}
{
const x, y = 20, 40
b := text.BoundString(mplusNormalFont, sampleText)
ebitenutil.DrawRect(screen, float64(b.Min.X+x), float64(b.Min.Y+y), float64(b.Dx()), float64(b.Dy()), gray)
text.Draw(screen, sampleText, mplusNormalFont, x, y, color.White)
}
{
const x, y = 20, 140
b := text.BoundString(mplusBigFont, sampleText)
ebitenutil.DrawRect(screen, float64(b.Min.X+x), float64(b.Min.Y+y), float64(b.Dx()), float64(b.Dy()), gray)
text.Draw(screen, sampleText, mplusBigFont, x, y, color.White)
}
}
func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
return screenWidth, screenHeight
}
func main() {
ebiten.SetWindowSize(screenWidth, screenHeight)
ebiten.SetWindowTitle("Font (Ebiten Demo)")
if err := ebiten.RunGame(&Game{}); err != nil {
log.Fatal(err)
}
}

View File

@ -208,9 +208,8 @@ var textM sync.Mutex
// //
// If you want to adjust the position of the text, these functions are useful: // If you want to adjust the position of the text, these functions are useful:
// //
// * text.BoundString: the rendered bounds of the given text.
// * golang.org/x/image/font.Face.Metrics: the metrics of the face. // * golang.org/x/image/font.Face.Metrics: the metrics of the face.
// * text.MeasureString: the size of the given text.
// * golang.org/x/image/font.BoundString: the bound rectangle of the given text.
// //
// The '\n' newline character puts the following text on the next line. // The '\n' newline character puts the following text on the next line.
// Line height is based on Metrics().Height of the font. // Line height is based on Metrics().Height of the font.
@ -253,10 +252,11 @@ func Draw(dst *ebiten.Image, text string, face font.Face, x, y int, clr color.Co
} }
} }
// MeasureString returns the measured size of a given string using a given font. // BoundString returns the measured size of a given string using a given font.
// This method will return the exact size in pixels that a string drawn by Draw will be. // This method will return the exact size in pixels that a string drawn by Draw will be.
// //
// This is very similar to golang.org/x/image/font's MeasureString, but this MeasureString considers multiple lines. // This is very similar to golang.org/x/image/font's BoundString,
// but this BoundString calculates the actual rendered area considering multiple lines and space characters.
// //
// text is the string that's being measured. // text is the string that's being measured.
// face is the font for text rendering. // face is the font for text rendering.
@ -264,23 +264,19 @@ func Draw(dst *ebiten.Image, text string, face font.Face, x, y int, clr color.Co
// Be careful that the passed font face is held by this package and is never released. // Be careful that the passed font face is held by this package and is never released.
// This is a known issue (#498). // This is a known issue (#498).
// //
// MeasureString is concurrent-safe. // BoundString is concurrent-safe.
func MeasureString(text string, face font.Face) image.Point { func BoundString(face font.Face, text string) image.Rectangle {
textM.Lock() textM.Lock()
defer textM.Unlock() defer textM.Unlock()
var w, h fixed.Int26_6
m := face.Metrics() m := face.Metrics()
faceHeight := m.Height faceHeight := m.Height
faceDescent := m.Descent
fx, fy := fixed.I(0), fixed.I(0) fx, fy := fixed.I(0), fixed.I(0)
prevR := rune(-1) prevR := rune(-1)
runes := []rune(text) var bounds fixed.Rectangle26_6
for _, r := range []rune(text) {
for _, r := range runes {
if prevR >= 0 { if prevR >= 0 {
fx += face.Kern(prevR, r) fx += face.Kern(prevR, r)
} }
@ -291,22 +287,22 @@ func MeasureString(text string, face font.Face) image.Point {
continue continue
} }
bp := getGlyphBounds(face, r)
b := *bp
b.Min.X += fx
b.Max.X += fx
b.Min.Y += fy
b.Max.Y += fy
bounds = bounds.Union(b)
fx += glyphAdvance(face, r) fx += glyphAdvance(face, r)
if fx > w {
w = fx
}
if (fy + faceHeight) > h {
h = fy + faceHeight
}
prevR = r prevR = r
} }
bounds := image.Point{ return image.Rect(
X: int(math.Ceil(fixed26_6ToFloat64(w))), int(math.Floor(fixed26_6ToFloat64(bounds.Min.X))),
Y: int(math.Ceil(fixed26_6ToFloat64(h + faceDescent))), int(math.Floor(fixed26_6ToFloat64(bounds.Min.Y))),
} int(math.Ceil(fixed26_6ToFloat64(bounds.Max.X))),
int(math.Ceil(fixed26_6ToFloat64(bounds.Max.Y))),
return bounds )
} }