text/v2: rename StdFace to GoXFace

Closes #2925
This commit is contained in:
Hajime Hoshi 2024-03-20 02:38:22 +09:00
parent d15b12b4e5
commit cd90f083bc
8 changed files with 83 additions and 83 deletions

View File

@ -35,7 +35,7 @@ const (
) )
var ( var (
inconsolataFace = text.NewStdFace(inconsolata.Bold8x16) inconsolataFace = text.NewGoXFace(inconsolata.Bold8x16)
) )
// mode is a blend mode with description. // mode is a blend mode with description.

View File

@ -35,7 +35,7 @@ const (
screenHeight = 240 screenHeight = 240
) )
var fontFace = text.NewStdFace(bitmapfont.Face) var fontFace = text.NewGoXFace(bitmapfont.Face)
var keyboardImage *ebiten.Image var keyboardImage *ebiten.Image

View File

@ -32,7 +32,7 @@ import (
"github.com/hajimehoshi/ebiten/v2/vector" "github.com/hajimehoshi/ebiten/v2/vector"
) )
var fontFace = text.NewStdFace(bitmapfont.FaceEA) var fontFace = text.NewGoXFace(bitmapfont.FaceEA)
const ( const (
screenWidth = 640 screenWidth = 640

View File

@ -25,29 +25,29 @@ import (
"github.com/hajimehoshi/ebiten/v2/vector" "github.com/hajimehoshi/ebiten/v2/vector"
) )
var _ Face = (*StdFace)(nil) var _ Face = (*GoXFace)(nil)
type stdFaceGlyphImageCacheKey struct { type goXFaceGlyphImageCacheKey struct {
rune rune rune rune
xoffset fixed.Int26_6 xoffset fixed.Int26_6
// yoffset is always the same if the rune is the same, so this doesn't have to be a key. // yoffset is always the same if the rune is the same, so this doesn't have to be a key.
} }
// StdFace is a Face implementation for a semi-standard font.Face (golang.org/x/image/font). // GoXFace is a Face implementation for a semi-standard font.Face (golang.org/x/image/font).
// StdFace is useful to transit from existing codebase with text v1, or to use some bitmap fonts defined as font.Face. // GoXFace is useful to transit from existing codebase with text v1, or to use some bitmap fonts defined as font.Face.
// StdFace must not be copied by value. // GoXFace must not be copied by value.
type StdFace struct { type GoXFace struct {
f *faceWithCache f *faceWithCache
glyphImageCache glyphImageCache[stdFaceGlyphImageCacheKey] glyphImageCache glyphImageCache[goXFaceGlyphImageCacheKey]
addr *StdFace addr *GoXFace
} }
// NewStdFace creates a new StdFace from a semi-standard font.Face. // NewGoXFace creates a new GoXFace from a semi-standard font.Face.
func NewStdFace(face font.Face) *StdFace { func NewGoXFace(face font.Face) *GoXFace {
s := &StdFace{ s := &GoXFace{
f: &faceWithCache{ f: &faceWithCache{
f: face, f: face,
}, },
@ -56,14 +56,14 @@ func NewStdFace(face font.Face) *StdFace {
return s return s
} }
func (s *StdFace) copyCheck() { func (s *GoXFace) copyCheck() {
if s.addr != s { if s.addr != s {
panic("text: illegal use of non-zero StdFace copied by value") panic("text: illegal use of non-zero GoXFace copied by value")
} }
} }
// Metrics implements Face. // Metrics implements Face.
func (s *StdFace) Metrics() Metrics { func (s *GoXFace) Metrics() Metrics {
s.copyCheck() s.copyCheck()
m := s.f.Metrics() m := s.f.Metrics()
@ -77,24 +77,24 @@ func (s *StdFace) Metrics() Metrics {
// UnsafeInternal returns its internal font.Face. // UnsafeInternal returns its internal font.Face.
// //
// This is unsafe since this might make internal cache states out of sync. // This is unsafe since this might make internal cache states out of sync.
func (s *StdFace) UnsafeInternal() font.Face { func (s *GoXFace) UnsafeInternal() font.Face {
s.copyCheck() s.copyCheck()
return s.f.f return s.f.f
} }
// advance implements Face. // advance implements Face.
func (s *StdFace) advance(text string) float64 { func (s *GoXFace) advance(text string) float64 {
return fixed26_6ToFloat64(font.MeasureString(s.f, text)) return fixed26_6ToFloat64(font.MeasureString(s.f, text))
} }
// hasGlyph implements Face. // hasGlyph implements Face.
func (s *StdFace) hasGlyph(r rune) bool { func (s *GoXFace) hasGlyph(r rune) bool {
_, ok := s.f.GlyphAdvance(r) _, ok := s.f.GlyphAdvance(r)
return ok return ok
} }
// appendGlyphsForLine implements Face. // appendGlyphsForLine implements Face.
func (s *StdFace) appendGlyphsForLine(glyphs []Glyph, line string, indexOffset int, originX, originY float64) []Glyph { func (s *GoXFace) appendGlyphsForLine(glyphs []Glyph, line string, indexOffset int, originX, originY float64) []Glyph {
s.copyCheck() s.copyCheck()
origin := fixed.Point26_6{ origin := fixed.Point26_6{
@ -129,8 +129,8 @@ func (s *StdFace) appendGlyphsForLine(glyphs []Glyph, line string, indexOffset i
return glyphs return glyphs
} }
func (s *StdFace) glyphImage(r rune, origin fixed.Point26_6) (*ebiten.Image, int, int, fixed.Int26_6) { func (s *GoXFace) glyphImage(r rune, origin fixed.Point26_6) (*ebiten.Image, int, int, fixed.Int26_6) {
// Assume that StdFace's direction is always horizontal. // Assume that GoXFace's direction is always horizontal.
origin.X = adjustGranularity(origin.X, s) origin.X = adjustGranularity(origin.X, s)
origin.Y &^= ((1 << 6) - 1) origin.Y &^= ((1 << 6) - 1)
@ -139,7 +139,7 @@ func (s *StdFace) glyphImage(r rune, origin fixed.Point26_6) (*ebiten.Image, int
X: (origin.X + b.Min.X) & ((1 << 6) - 1), X: (origin.X + b.Min.X) & ((1 << 6) - 1),
Y: (origin.Y + b.Min.Y) & ((1 << 6) - 1), Y: (origin.Y + b.Min.Y) & ((1 << 6) - 1),
} }
key := stdFaceGlyphImageCacheKey{ key := goXFaceGlyphImageCacheKey{
rune: r, rune: r,
xoffset: subpixelOffset.X, xoffset: subpixelOffset.X,
} }
@ -151,7 +151,7 @@ func (s *StdFace) glyphImage(r rune, origin fixed.Point26_6) (*ebiten.Image, int
return img, imgX, imgY, a return img, imgX, imgY, a
} }
func (s *StdFace) glyphImageImpl(r rune, subpixelOffset fixed.Point26_6, glyphBounds fixed.Rectangle26_6) *ebiten.Image { func (s *GoXFace) glyphImageImpl(r rune, subpixelOffset fixed.Point26_6, glyphBounds fixed.Rectangle26_6) *ebiten.Image {
w, h := (glyphBounds.Max.X - glyphBounds.Min.X).Ceil(), (glyphBounds.Max.Y - glyphBounds.Min.Y).Ceil() w, h := (glyphBounds.Max.X - glyphBounds.Min.X).Ceil(), (glyphBounds.Max.Y - glyphBounds.Min.Y).Ceil()
if w == 0 || h == 0 { if w == 0 || h == 0 {
return nil return nil
@ -179,14 +179,14 @@ func (s *StdFace) glyphImageImpl(r rune, subpixelOffset fixed.Point26_6, glyphBo
} }
// direction implements Face. // direction implements Face.
func (s *StdFace) direction() Direction { func (s *GoXFace) direction() Direction {
return DirectionLeftToRight return DirectionLeftToRight
} }
// appendVectorPathForLine implements Face. // appendVectorPathForLine implements Face.
func (s *StdFace) appendVectorPathForLine(path *vector.Path, line string, originX, originY float64) { func (s *GoXFace) appendVectorPathForLine(path *vector.Path, line string, originX, originY float64) {
} }
// Metrics implements Face. // Metrics implements Face.
func (s *StdFace) private() { func (s *GoXFace) private() {
} }

View File

@ -89,7 +89,7 @@ type LayoutOptions struct {
// //
// For horizontal directions, the start and end depends on the face. // For horizontal directions, the start and end depends on the face.
// If the face is GoTextFace, the start and the end depend on the Direction property. // If the face is GoTextFace, the start and the end depend on the Direction property.
// If the face is StdFace, the start and the end are always left and right respectively. // If the face is GoXFace, the start and the end are always left and right respectively.
// //
// For vertical directions, the start and end are top and bottom respectively. // For vertical directions, the start and end are top and bottom respectively.
// //

View File

@ -26,7 +26,7 @@ import (
) )
func TestMultiFace(t *testing.T) { func TestMultiFace(t *testing.T) {
faces := []text.Face{text.NewStdFace(bitmapfont.Face)} faces := []text.Face{text.NewGoXFace(bitmapfont.Face)}
f, err := text.NewMultiFace(faces...) f, err := text.NewMultiFace(faces...)
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)

View File

@ -27,7 +27,7 @@ import (
) )
// Face is an interface representing a font face. // Face is an interface representing a font face.
// The implementations are only faces defined in this package, like GoTextFace and StdFace. // The implementations are only faces defined in this package, like GoTextFace and GoXFace.
type Face interface { type Face interface {
// Metrics returns the metrics for this Face. // Metrics returns the metrics for this Face.
Metrics() Metrics Metrics() Metrics
@ -59,15 +59,15 @@ type Metrics struct {
HDescent float64 HDescent float64
// VLineGap is the recommended amount of horizontal space between two lines of text in pixels. // VLineGap is the recommended amount of horizontal space between two lines of text in pixels.
// If the face is StdFace or the font doesn't support a vertical direction, VLineGap is 0. // If the face is GoXFace or the font doesn't support a vertical direction, VLineGap is 0.
VLineGap float64 VLineGap float64
// VAscent is the distance in pixels from the top of a line to its baseline for vertical lines. // VAscent is the distance in pixels from the top of a line to its baseline for vertical lines.
// If the face is StdFace or the font doesn't support a vertical direction, VAscent is 0. // If the face is GoXFace or the font doesn't support a vertical direction, VAscent is 0.
VAscent float64 VAscent float64
// VDescent is the distance in pixels from the top of a line to its baseline for vertical lines. // VDescent is the distance in pixels from the top of a line to its baseline for vertical lines.
// If the face is StdFace or the font doesn't support a vertical direction, VDescent is 0. // If the face is GoXFace or the font doesn't support a vertical direction, VDescent is 0.
VDescent float64 VDescent float64
} }

View File

@ -38,7 +38,7 @@ func TestGlyphIndex(t *testing.T) {
const sampleText = `The quick brown fox jumps const sampleText = `The quick brown fox jumps
over the lazy dog.` over the lazy dog.`
f := text.NewStdFace(bitmapfont.Face) f := text.NewGoXFace(bitmapfont.Face)
got := sampleText got := sampleText
for _, g := range text.AppendGlyphs(nil, sampleText, f, nil) { for _, g := range text.AppendGlyphs(nil, sampleText, f, nil) {
got = got[:g.StartIndexInBytes] + strings.Repeat(" ", g.EndIndexInBytes-g.StartIndexInBytes) + got[g.EndIndexInBytes:] got = got[:g.StartIndexInBytes] + strings.Repeat(" ", g.EndIndexInBytes-g.StartIndexInBytes) + got[g.EndIndexInBytes:]
@ -55,7 +55,7 @@ func TestTextColor(t *testing.T) {
op := &text.DrawOptions{} op := &text.DrawOptions{}
op.GeoM.Translate(0, 0) op.GeoM.Translate(0, 0)
op.ColorScale.ScaleWithColor(clr) op.ColorScale.ScaleWithColor(clr)
text.Draw(img, "Hello", text.NewStdFace(bitmapfont.Face), op) text.Draw(img, "Hello", text.NewGoXFace(bitmapfont.Face), op)
w, h := img.Bounds().Dx(), img.Bounds().Dy() w, h := img.Bounds().Dx(), img.Bounds().Dy()
allTransparent := true allTransparent := true
@ -77,12 +77,12 @@ func TestTextColor(t *testing.T) {
} }
} }
const testStdFaceSize = 6 const testGoXFaceSize = 6
type testStdFace struct{} type testGoXFace struct{}
func (f *testStdFace) Glyph(dot fixed.Point26_6, r rune) (dr image.Rectangle, mask image.Image, maskp image.Point, advance fixed.Int26_6, ok bool) { func (f *testGoXFace) Glyph(dot fixed.Point26_6, r rune) (dr image.Rectangle, mask image.Image, maskp image.Point, advance fixed.Int26_6, ok bool) {
dr = image.Rect(0, 0, testStdFaceSize, testStdFaceSize) dr = image.Rect(0, 0, testGoXFaceSize, testGoXFaceSize)
a := image.NewAlpha(dr) a := image.NewAlpha(dr)
switch r { switch r {
case 'a': case 'a':
@ -99,56 +99,56 @@ func (f *testStdFace) Glyph(dot fixed.Point26_6, r rune) (dr image.Rectangle, ma
} }
} }
mask = a mask = a
advance = fixed.I(testStdFaceSize) advance = fixed.I(testGoXFaceSize)
ok = true ok = true
return return
} }
func (f *testStdFace) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) { func (f *testGoXFace) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) {
bounds = fixed.R(0, 0, testStdFaceSize, testStdFaceSize) bounds = fixed.R(0, 0, testGoXFaceSize, testGoXFaceSize)
advance = fixed.I(testStdFaceSize) advance = fixed.I(testGoXFaceSize)
ok = true ok = true
return return
} }
func (f *testStdFace) GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) { func (f *testGoXFace) GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) {
return fixed.I(testStdFaceSize), true return fixed.I(testGoXFaceSize), true
} }
func (f *testStdFace) Kern(r0, r1 rune) fixed.Int26_6 { func (f *testGoXFace) Kern(r0, r1 rune) fixed.Int26_6 {
if r1 == 'b' { if r1 == 'b' {
return fixed.I(-testStdFaceSize) return fixed.I(-testGoXFaceSize)
} }
return 0 return 0
} }
func (f *testStdFace) Close() error { func (f *testGoXFace) Close() error {
return nil return nil
} }
func (f *testStdFace) Metrics() font.Metrics { func (f *testGoXFace) Metrics() font.Metrics {
return font.Metrics{ return font.Metrics{
Height: fixed.I(testStdFaceSize), Height: fixed.I(testGoXFaceSize),
Ascent: 0, Ascent: 0,
Descent: fixed.I(testStdFaceSize), Descent: fixed.I(testGoXFaceSize),
XHeight: 0, XHeight: 0,
CapHeight: fixed.I(testStdFaceSize), CapHeight: fixed.I(testGoXFaceSize),
CaretSlope: image.Pt(0, 1), CaretSlope: image.Pt(0, 1),
} }
} }
// Issue #1378 // Issue #1378
func TestNegativeKern(t *testing.T) { func TestNegativeKern(t *testing.T) {
f := text.NewStdFace(&testStdFace{}) f := text.NewGoXFace(&testGoXFace{})
dst := ebiten.NewImage(testStdFaceSize*2, testStdFaceSize) dst := ebiten.NewImage(testGoXFaceSize*2, testGoXFaceSize)
// With testStdFace, 'b' is rendered at the previous position as 0xff. // With testGoXFace, 'b' is rendered at the previous position as 0xff.
// 'a' is rendered at the current position as 0x80. // 'a' is rendered at the current position as 0x80.
op := &text.DrawOptions{} op := &text.DrawOptions{}
op.GeoM.Translate(0, 0) op.GeoM.Translate(0, 0)
text.Draw(dst, "ab", f, op) text.Draw(dst, "ab", f, op)
for j := 0; j < testStdFaceSize; j++ { for j := 0; j < testGoXFaceSize; j++ {
for i := 0; i < testStdFaceSize; i++ { for i := 0; i < testGoXFaceSize; i++ {
got := dst.At(i, j) got := dst.At(i, j)
want := color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff} want := color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}
if got != want { if got != want {
@ -159,10 +159,10 @@ func TestNegativeKern(t *testing.T) {
// The glyph 'a' should be treated correctly. // The glyph 'a' should be treated correctly.
op = &text.DrawOptions{} op = &text.DrawOptions{}
op.GeoM.Translate(testStdFaceSize, 0) op.GeoM.Translate(testGoXFaceSize, 0)
text.Draw(dst, "a", f, op) text.Draw(dst, "a", f, op)
for j := 0; j < testStdFaceSize; j++ { for j := 0; j < testGoXFaceSize; j++ {
for i := testStdFaceSize; i < testStdFaceSize*2; i++ { for i := testGoXFaceSize; i < testGoXFaceSize*2; i++ {
got := dst.At(i, j) got := dst.At(i, j)
want := color.RGBA{R: 0x80, G: 0x80, B: 0x80, A: 0x80} want := color.RGBA{R: 0x80, G: 0x80, B: 0x80, A: 0x80}
if got != want { if got != want {
@ -172,12 +172,12 @@ func TestNegativeKern(t *testing.T) {
} }
} }
type unhashableStdFace func() type unhashableGoXFace func()
const unhashableStdFaceSize = 10 const unhashableGoXFaceSize = 10
func (u *unhashableStdFace) Glyph(dot fixed.Point26_6, r rune) (dr image.Rectangle, mask image.Image, maskp image.Point, advance fixed.Int26_6, ok bool) { func (u *unhashableGoXFace) Glyph(dot fixed.Point26_6, r rune) (dr image.Rectangle, mask image.Image, maskp image.Point, advance fixed.Int26_6, ok bool) {
dr = image.Rect(0, 0, unhashableStdFaceSize, unhashableStdFaceSize) dr = image.Rect(0, 0, unhashableGoXFaceSize, unhashableGoXFaceSize)
a := image.NewAlpha(dr) a := image.NewAlpha(dr)
for j := dr.Min.Y; j < dr.Max.Y; j++ { for j := dr.Min.Y; j < dr.Max.Y; j++ {
for i := dr.Min.X; i < dr.Max.X; i++ { for i := dr.Min.X; i < dr.Max.X; i++ {
@ -185,53 +185,53 @@ func (u *unhashableStdFace) Glyph(dot fixed.Point26_6, r rune) (dr image.Rectang
} }
} }
mask = a mask = a
advance = fixed.I(unhashableStdFaceSize) advance = fixed.I(unhashableGoXFaceSize)
ok = true ok = true
return return
} }
func (u *unhashableStdFace) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) { func (u *unhashableGoXFace) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) {
bounds = fixed.R(0, 0, unhashableStdFaceSize, unhashableStdFaceSize) bounds = fixed.R(0, 0, unhashableGoXFaceSize, unhashableGoXFaceSize)
advance = fixed.I(unhashableStdFaceSize) advance = fixed.I(unhashableGoXFaceSize)
ok = true ok = true
return return
} }
func (u *unhashableStdFace) GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) { func (u *unhashableGoXFace) GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) {
return fixed.I(unhashableStdFaceSize), true return fixed.I(unhashableGoXFaceSize), true
} }
func (u *unhashableStdFace) Kern(r0, r1 rune) fixed.Int26_6 { func (u *unhashableGoXFace) Kern(r0, r1 rune) fixed.Int26_6 {
return 0 return 0
} }
func (u *unhashableStdFace) Close() error { func (u *unhashableGoXFace) Close() error {
return nil return nil
} }
func (u *unhashableStdFace) Metrics() font.Metrics { func (u *unhashableGoXFace) Metrics() font.Metrics {
return font.Metrics{ return font.Metrics{
Height: fixed.I(unhashableStdFaceSize), Height: fixed.I(unhashableGoXFaceSize),
Ascent: 0, Ascent: 0,
Descent: fixed.I(unhashableStdFaceSize), Descent: fixed.I(unhashableGoXFaceSize),
XHeight: 0, XHeight: 0,
CapHeight: fixed.I(unhashableStdFaceSize), CapHeight: fixed.I(unhashableGoXFaceSize),
CaretSlope: image.Pt(0, 1), CaretSlope: image.Pt(0, 1),
} }
} }
// Issue #2669 // Issue #2669
func TestUnhashableFace(t *testing.T) { func TestUnhashableFace(t *testing.T) {
var face unhashableStdFace var face unhashableGoXFace
f := text.NewStdFace(&face) f := text.NewGoXFace(&face)
dst := ebiten.NewImage(unhashableStdFaceSize*2, unhashableStdFaceSize*2) dst := ebiten.NewImage(unhashableGoXFaceSize*2, unhashableGoXFaceSize*2)
text.Draw(dst, "a", f, nil) text.Draw(dst, "a", f, nil)
for j := 0; j < unhashableStdFaceSize*2; j++ { for j := 0; j < unhashableGoXFaceSize*2; j++ {
for i := 0; i < unhashableStdFaceSize*2; i++ { for i := 0; i < unhashableGoXFaceSize*2; i++ {
got := dst.At(i, j) got := dst.At(i, j)
var want color.RGBA var want color.RGBA
if i < unhashableStdFaceSize && j < unhashableStdFaceSize { if i < unhashableGoXFaceSize && j < unhashableGoXFaceSize {
want = color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff} want = color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff}
} }
if got != want { if got != want {