text/v2: replace Rune and IndexInBytes with Start/EndIndexInBytes in Glyph

The relationships between runes and glyphs are n:m in general,
then Rune is not enough. Let Glyph have a range of a string.

Updates #2454
This commit is contained in:
Hajime Hoshi 2023-11-14 01:16:26 +09:00
parent ea1d9dde4e
commit 2b46a77e39
4 changed files with 59 additions and 15 deletions

View File

@ -204,6 +204,7 @@ func appendGlyphs(glyphs []Glyph, text string, face Face, x, y float64, options
} }
} }
var indexOffset int
var originX, originY float64 var originX, originY float64
var i int var i int
for t := text; ; { for t := text; ; {
@ -231,12 +232,13 @@ func appendGlyphs(glyphs []Glyph, text string, face Face, x, y float64, options
} }
} }
glyphs = face.appendGlyphs(glyphs, line, originX+offsetX+x, originY+offsetY+y) glyphs = face.appendGlyphs(glyphs, line, indexOffset, originX+offsetX+x, originY+offsetY+y)
if !found { if !found {
break break
} }
t = rest t = rest
indexOffset += len(line) + 1
i++ i++
// Advance the origin position in the secondary direction. // Advance the origin position in the secondary direction.

View File

@ -18,6 +18,7 @@ import (
"image" "image"
"runtime" "runtime"
"sync/atomic" "sync/atomic"
"unicode/utf8"
"golang.org/x/image/font" "golang.org/x/image/font"
"golang.org/x/image/math/fixed" "golang.org/x/image/math/fixed"
@ -92,7 +93,7 @@ func (s *StdFace) advance(text string) float64 {
} }
// appendGlyphs implements Face. // appendGlyphs implements Face.
func (s *StdFace) appendGlyphs(glyphs []Glyph, text string, originX, originY float64) []Glyph { func (s *StdFace) appendGlyphs(glyphs []Glyph, text string, indexOffset int, originX, originY float64) []Glyph {
s.copyCheck() s.copyCheck()
origin := fixed.Point26_6{ origin := fixed.Point26_6{
@ -109,9 +110,10 @@ func (s *StdFace) appendGlyphs(glyphs []Glyph, text string, originX, originY flo
if img != nil { if img != nil {
// Adjust the position to the integers. // Adjust the position to the integers.
// The current glyph images assume that they are rendered on integer positions so far. // The current glyph images assume that they are rendered on integer positions so far.
_, size := utf8.DecodeRuneInString(text[i:])
glyphs = append(glyphs, Glyph{ glyphs = append(glyphs, Glyph{
Rune: r, StartIndexInBytes: indexOffset + i,
IndexInBytes: i, EndIndexInBytes: indexOffset + i + size,
Image: img, Image: img,
X: float64(imgX), X: float64(imgX),
Y: float64(imgY), Y: float64(imgY),

View File

@ -42,7 +42,7 @@ type Face interface {
advance(text string) float64 advance(text string) float64
appendGlyphs(glyphs []Glyph, text string, originX, originY float64) []Glyph appendGlyphs(glyphs []Glyph, text string, indexOffset int, originX, originY float64) []Glyph
direction() Direction direction() Direction
@ -90,11 +90,14 @@ func adjustGranularity(x fixed.Int26_6) fixed.Int26_6 {
// Glyph represents one glyph to render. // Glyph represents one glyph to render.
type Glyph struct { type Glyph struct {
// Rune is a character for this glyph. // StartIndexInBytes is the start index in bytes for the given string at AppendGlyphs.
Rune rune StartIndexInBytes int
// IndexInBytes is an index in bytes for the given string at AppendGlyphs. // EndIndexInBytes is the end index in bytes for the given string at AppendGlyphs.
IndexInBytes int EndIndexInBytes int
// GID is an ID for a glyph of TrueType or OpenType font. GID is valid when the font is GoTextFont.
GID uint32
// Image is a rasterized glyph image. // Image is a rasterized glyph image.
// Image is a grayscale image i.e. RGBA values are the same. // Image is a grayscale image i.e. RGBA values are the same.
@ -110,9 +113,6 @@ type Glyph struct {
// The position is determined in a sequence of characters given at AppendGlyphs. // The position is determined in a sequence of characters given at AppendGlyphs.
// The position's origin is the first character's origin position. // The position's origin is the first character's origin position.
Y float64 Y float64
// GID is an ID for a glyph of TrueType or OpenType font. GID is valid when the font is GoTextFont.
GID uint32
} }
// Advance returns the advanced distance from the origin position when rendering the given text with the given face. // Advance returns the advanced distance from the origin position when rendering the given text with the given face.

40
text/v2/text_test.go Normal file
View File

@ -0,0 +1,40 @@
// Copyright 2023 The Ebitengine 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.
package text_test
import (
"regexp"
"strings"
"testing"
"github.com/hajimehoshi/bitmapfont/v3"
"github.com/hajimehoshi/ebiten/v2/text/v2"
)
func TestGlyphIndex(t *testing.T) {
const sampleText = `The quick brown fox jumps
over the lazy dog.`
f := text.NewStdFace(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:]
}
want := regexp.MustCompile(`\S`).ReplaceAllString(sampleText, " ")
if got != want {
t.Errorf("got: %q, want: %q", got, want)
}
}