diff --git a/examples/blend/main.go b/examples/blend/main.go index 40b80e326..d68806aa0 100644 --- a/examples/blend/main.go +++ b/examples/blend/main.go @@ -35,7 +35,7 @@ const ( ) var ( - inconsolataFace = text.NewStdFace(inconsolata.Bold8x16) + inconsolataFace = text.NewGoXFace(inconsolata.Bold8x16) ) // mode is a blend mode with description. diff --git a/examples/keyboard/main.go b/examples/keyboard/main.go index dd0b5d558..3f5b00206 100644 --- a/examples/keyboard/main.go +++ b/examples/keyboard/main.go @@ -35,7 +35,7 @@ const ( screenHeight = 240 ) -var fontFace = text.NewStdFace(bitmapfont.Face) +var fontFace = text.NewGoXFace(bitmapfont.Face) var keyboardImage *ebiten.Image diff --git a/examples/textinput/main.go b/examples/textinput/main.go index 3704eaa0d..81081658e 100644 --- a/examples/textinput/main.go +++ b/examples/textinput/main.go @@ -32,7 +32,7 @@ import ( "github.com/hajimehoshi/ebiten/v2/vector" ) -var fontFace = text.NewStdFace(bitmapfont.FaceEA) +var fontFace = text.NewGoXFace(bitmapfont.FaceEA) const ( screenWidth = 640 diff --git a/text/v2/std.go b/text/v2/gox.go similarity index 77% rename from text/v2/std.go rename to text/v2/gox.go index 0ffd1710e..98cec7d9d 100644 --- a/text/v2/std.go +++ b/text/v2/gox.go @@ -25,29 +25,29 @@ import ( "github.com/hajimehoshi/ebiten/v2/vector" ) -var _ Face = (*StdFace)(nil) +var _ Face = (*GoXFace)(nil) -type stdFaceGlyphImageCacheKey struct { +type goXFaceGlyphImageCacheKey struct { rune rune xoffset fixed.Int26_6 // 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). -// StdFace 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. -type StdFace struct { +// GoXFace is a Face implementation for a semi-standard font.Face (golang.org/x/image/font). +// GoXFace is useful to transit from existing codebase with text v1, or to use some bitmap fonts defined as font.Face. +// GoXFace must not be copied by value. +type GoXFace struct { f *faceWithCache - glyphImageCache glyphImageCache[stdFaceGlyphImageCacheKey] + glyphImageCache glyphImageCache[goXFaceGlyphImageCacheKey] - addr *StdFace + addr *GoXFace } -// NewStdFace creates a new StdFace from a semi-standard font.Face. -func NewStdFace(face font.Face) *StdFace { - s := &StdFace{ +// NewGoXFace creates a new GoXFace from a semi-standard font.Face. +func NewGoXFace(face font.Face) *GoXFace { + s := &GoXFace{ f: &faceWithCache{ f: face, }, @@ -56,14 +56,14 @@ func NewStdFace(face font.Face) *StdFace { return s } -func (s *StdFace) copyCheck() { +func (s *GoXFace) copyCheck() { 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. -func (s *StdFace) Metrics() Metrics { +func (s *GoXFace) Metrics() Metrics { s.copyCheck() m := s.f.Metrics() @@ -77,24 +77,24 @@ func (s *StdFace) Metrics() Metrics { // UnsafeInternal returns its internal font.Face. // // 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() return s.f.f } // 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)) } // hasGlyph implements Face. -func (s *StdFace) hasGlyph(r rune) bool { +func (s *GoXFace) hasGlyph(r rune) bool { _, ok := s.f.GlyphAdvance(r) return ok } // 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() origin := fixed.Point26_6{ @@ -129,8 +129,8 @@ func (s *StdFace) appendGlyphsForLine(glyphs []Glyph, line string, indexOffset i return glyphs } -func (s *StdFace) glyphImage(r rune, origin fixed.Point26_6) (*ebiten.Image, int, int, fixed.Int26_6) { - // Assume that StdFace's direction is always horizontal. +func (s *GoXFace) glyphImage(r rune, origin fixed.Point26_6) (*ebiten.Image, int, int, fixed.Int26_6) { + // Assume that GoXFace's direction is always horizontal. origin.X = adjustGranularity(origin.X, s) 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), Y: (origin.Y + b.Min.Y) & ((1 << 6) - 1), } - key := stdFaceGlyphImageCacheKey{ + key := goXFaceGlyphImageCacheKey{ rune: r, 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 } -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() if w == 0 || h == 0 { return nil @@ -179,14 +179,14 @@ func (s *StdFace) glyphImageImpl(r rune, subpixelOffset fixed.Point26_6, glyphBo } // direction implements Face. -func (s *StdFace) direction() Direction { +func (s *GoXFace) direction() Direction { return DirectionLeftToRight } // 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. -func (s *StdFace) private() { +func (s *GoXFace) private() { } diff --git a/text/v2/layout.go b/text/v2/layout.go index ce022f393..f99757f65 100644 --- a/text/v2/layout.go +++ b/text/v2/layout.go @@ -89,7 +89,7 @@ type LayoutOptions struct { // // 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 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. // diff --git a/text/v2/multi_test.go b/text/v2/multi_test.go index 894447d8b..cde61847e 100644 --- a/text/v2/multi_test.go +++ b/text/v2/multi_test.go @@ -26,7 +26,7 @@ import ( ) func TestMultiFace(t *testing.T) { - faces := []text.Face{text.NewStdFace(bitmapfont.Face)} + faces := []text.Face{text.NewGoXFace(bitmapfont.Face)} f, err := text.NewMultiFace(faces...) if err != nil { t.Fatal(err) diff --git a/text/v2/text.go b/text/v2/text.go index ee0bad26a..32d750b71 100644 --- a/text/v2/text.go +++ b/text/v2/text.go @@ -27,7 +27,7 @@ import ( ) // 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 { // Metrics returns the metrics for this Face. Metrics() Metrics @@ -59,15 +59,15 @@ type Metrics struct { HDescent float64 // 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 // 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 // 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 } diff --git a/text/v2/text_test.go b/text/v2/text_test.go index fb7aa6089..707f1b1dd 100644 --- a/text/v2/text_test.go +++ b/text/v2/text_test.go @@ -38,7 +38,7 @@ func TestGlyphIndex(t *testing.T) { const sampleText = `The quick brown fox jumps over the lazy dog.` - f := text.NewStdFace(bitmapfont.Face) + f := text.NewGoXFace(bitmapfont.Face) got := sampleText for _, g := range text.AppendGlyphs(nil, sampleText, f, nil) { 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.GeoM.Translate(0, 0) 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() 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) { - dr = image.Rect(0, 0, testStdFaceSize, testStdFaceSize) +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, testGoXFaceSize, testGoXFaceSize) a := image.NewAlpha(dr) switch r { case 'a': @@ -99,56 +99,56 @@ func (f *testStdFace) Glyph(dot fixed.Point26_6, r rune) (dr image.Rectangle, ma } } mask = a - advance = fixed.I(testStdFaceSize) + advance = fixed.I(testGoXFaceSize) ok = true return } -func (f *testStdFace) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) { - bounds = fixed.R(0, 0, testStdFaceSize, testStdFaceSize) - advance = fixed.I(testStdFaceSize) +func (f *testGoXFace) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) { + bounds = fixed.R(0, 0, testGoXFaceSize, testGoXFaceSize) + advance = fixed.I(testGoXFaceSize) ok = true return } -func (f *testStdFace) GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) { - return fixed.I(testStdFaceSize), true +func (f *testGoXFace) GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) { + 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' { - return fixed.I(-testStdFaceSize) + return fixed.I(-testGoXFaceSize) } return 0 } -func (f *testStdFace) Close() error { +func (f *testGoXFace) Close() error { return nil } -func (f *testStdFace) Metrics() font.Metrics { +func (f *testGoXFace) Metrics() font.Metrics { return font.Metrics{ - Height: fixed.I(testStdFaceSize), + Height: fixed.I(testGoXFaceSize), Ascent: 0, - Descent: fixed.I(testStdFaceSize), + Descent: fixed.I(testGoXFaceSize), XHeight: 0, - CapHeight: fixed.I(testStdFaceSize), + CapHeight: fixed.I(testGoXFaceSize), CaretSlope: image.Pt(0, 1), } } // Issue #1378 func TestNegativeKern(t *testing.T) { - f := text.NewStdFace(&testStdFace{}) - dst := ebiten.NewImage(testStdFaceSize*2, testStdFaceSize) + f := text.NewGoXFace(&testGoXFace{}) + 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. op := &text.DrawOptions{} op.GeoM.Translate(0, 0) text.Draw(dst, "ab", f, op) - for j := 0; j < testStdFaceSize; j++ { - for i := 0; i < testStdFaceSize; i++ { + for j := 0; j < testGoXFaceSize; j++ { + for i := 0; i < testGoXFaceSize; i++ { got := dst.At(i, j) want := color.RGBA{R: 0xff, G: 0xff, B: 0xff, A: 0xff} if got != want { @@ -159,10 +159,10 @@ func TestNegativeKern(t *testing.T) { // The glyph 'a' should be treated correctly. op = &text.DrawOptions{} - op.GeoM.Translate(testStdFaceSize, 0) + op.GeoM.Translate(testGoXFaceSize, 0) text.Draw(dst, "a", f, op) - for j := 0; j < testStdFaceSize; j++ { - for i := testStdFaceSize; i < testStdFaceSize*2; i++ { + for j := 0; j < testGoXFaceSize; j++ { + for i := testGoXFaceSize; i < testGoXFaceSize*2; i++ { got := dst.At(i, j) want := color.RGBA{R: 0x80, G: 0x80, B: 0x80, A: 0x80} 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) { - dr = image.Rect(0, 0, unhashableStdFaceSize, unhashableStdFaceSize) +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, unhashableGoXFaceSize, unhashableGoXFaceSize) a := image.NewAlpha(dr) for j := dr.Min.Y; j < dr.Max.Y; j++ { 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 - advance = fixed.I(unhashableStdFaceSize) + advance = fixed.I(unhashableGoXFaceSize) ok = true return } -func (u *unhashableStdFace) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) { - bounds = fixed.R(0, 0, unhashableStdFaceSize, unhashableStdFaceSize) - advance = fixed.I(unhashableStdFaceSize) +func (u *unhashableGoXFace) GlyphBounds(r rune) (bounds fixed.Rectangle26_6, advance fixed.Int26_6, ok bool) { + bounds = fixed.R(0, 0, unhashableGoXFaceSize, unhashableGoXFaceSize) + advance = fixed.I(unhashableGoXFaceSize) ok = true return } -func (u *unhashableStdFace) GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) { - return fixed.I(unhashableStdFaceSize), true +func (u *unhashableGoXFace) GlyphAdvance(r rune) (advance fixed.Int26_6, ok bool) { + 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 } -func (u *unhashableStdFace) Close() error { +func (u *unhashableGoXFace) Close() error { return nil } -func (u *unhashableStdFace) Metrics() font.Metrics { +func (u *unhashableGoXFace) Metrics() font.Metrics { return font.Metrics{ - Height: fixed.I(unhashableStdFaceSize), + Height: fixed.I(unhashableGoXFaceSize), Ascent: 0, - Descent: fixed.I(unhashableStdFaceSize), + Descent: fixed.I(unhashableGoXFaceSize), XHeight: 0, - CapHeight: fixed.I(unhashableStdFaceSize), + CapHeight: fixed.I(unhashableGoXFaceSize), CaretSlope: image.Pt(0, 1), } } // Issue #2669 func TestUnhashableFace(t *testing.T) { - var face unhashableStdFace - f := text.NewStdFace(&face) - dst := ebiten.NewImage(unhashableStdFaceSize*2, unhashableStdFaceSize*2) + var face unhashableGoXFace + f := text.NewGoXFace(&face) + dst := ebiten.NewImage(unhashableGoXFaceSize*2, unhashableGoXFaceSize*2) text.Draw(dst, "a", f, nil) - for j := 0; j < unhashableStdFaceSize*2; j++ { - for i := 0; i < unhashableStdFaceSize*2; i++ { + for j := 0; j < unhashableGoXFaceSize*2; j++ { + for i := 0; i < unhashableGoXFaceSize*2; i++ { got := dst.At(i, j) 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} } if got != want {