gox.go 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. // Copyright 2023 The Ebitengine Authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package text
  15. import (
  16. "image"
  17. "unicode/utf8"
  18. "golang.org/x/image/font"
  19. "golang.org/x/image/math/fixed"
  20. "github.com/hajimehoshi/ebiten/v2"
  21. "github.com/hajimehoshi/ebiten/v2/vector"
  22. )
  23. var _ Face = (*GoXFace)(nil)
  24. type goXFaceGlyphImageCacheKey struct {
  25. rune rune
  26. xoffset fixed.Int26_6
  27. // yoffset is always the same if the rune is the same, so this doesn't have to be a key.
  28. }
  29. // GoXFace is a Face implementation for a semi-standard font.Face (golang.org/x/image/font).
  30. // GoXFace is useful to transit from existing codebase with text v1, or to use some bitmap fonts defined as font.Face.
  31. // GoXFace must not be copied by value.
  32. //
  33. // Unlike GoTextFace, one GoXFace instance has its own glyph image cache.
  34. // You should reuse the same GoXFace instance as much as possible.
  35. type GoXFace struct {
  36. f *faceWithCache
  37. glyphImageCache *cache[goXFaceGlyphImageCacheKey, *ebiten.Image]
  38. cachedMetrics Metrics
  39. originXCache *cache[string, []fixed.Int26_6]
  40. addr *GoXFace
  41. }
  42. // NewGoXFace creates a new GoXFace from a semi-standard font.Face.
  43. func NewGoXFace(face font.Face) *GoXFace {
  44. g := &GoXFace{
  45. f: &faceWithCache{
  46. f: face,
  47. },
  48. }
  49. // Set addr as early as possible. This is necessary for glyphVariationCount.
  50. g.addr = g
  51. g.glyphImageCache = newCache[goXFaceGlyphImageCacheKey, *ebiten.Image](128 * glyphVariationCount(g))
  52. g.originXCache = newCache[string, []fixed.Int26_6](512)
  53. return g
  54. }
  55. func (g *GoXFace) copyCheck() {
  56. if g.addr != g {
  57. panic("text: illegal use of non-zero GoXFace copied by value")
  58. }
  59. }
  60. // Metrics implements Face.
  61. func (g *GoXFace) Metrics() Metrics {
  62. g.copyCheck()
  63. if g.cachedMetrics != (Metrics{}) {
  64. return g.cachedMetrics
  65. }
  66. fm := g.f.Metrics()
  67. m := Metrics{
  68. HLineGap: fixed26_6ToFloat64(fm.Height - fm.Ascent - fm.Descent),
  69. HAscent: fixed26_6ToFloat64(fm.Ascent),
  70. HDescent: fixed26_6ToFloat64(fm.Descent),
  71. XHeight: fixed26_6ToFloat64(fm.XHeight),
  72. CapHeight: fixed26_6ToFloat64(fm.CapHeight),
  73. }
  74. // There is an issue that XHeight and CapHeight are negative for some old fonts (golang/go#69378).
  75. if fm.XHeight < 0 {
  76. m.XHeight *= -1
  77. }
  78. if fm.CapHeight < 0 {
  79. m.CapHeight *= -1
  80. }
  81. g.cachedMetrics = m
  82. return m
  83. }
  84. // UnsafeInternal returns its internal font.Face.
  85. //
  86. // UnsafeInternal is unsafe since this might make internal cache states out of sync.
  87. //
  88. // UnsafeInternal might have breaking changes even in the same major version.
  89. func (g *GoXFace) UnsafeInternal() font.Face {
  90. g.copyCheck()
  91. return g.f.f
  92. }
  93. // advance implements Face.
  94. func (g *GoXFace) advance(text string) float64 {
  95. xs := g.originXs(text)
  96. if len(xs) == 0 {
  97. return 0
  98. }
  99. return fixed26_6ToFloat64(xs[len(xs)-1])
  100. }
  101. func (g *GoXFace) originXs(text string) []fixed.Int26_6 {
  102. return g.originXCache.getOrCreate(text, func() ([]fixed.Int26_6, bool) {
  103. if len(text) == 0 {
  104. return nil, false
  105. }
  106. var originXs []fixed.Int26_6
  107. prevR := rune(-1)
  108. var originX fixed.Int26_6
  109. for _, r := range text {
  110. if prevR >= 0 {
  111. originX += g.f.Kern(prevR, r)
  112. originXs = append(originXs, originX)
  113. }
  114. a, _ := g.f.GlyphAdvance(r)
  115. originX += a
  116. prevR = r
  117. }
  118. originXs = append(originXs, originX)
  119. return originXs, true
  120. })
  121. }
  122. // hasGlyph implements Face.
  123. func (g *GoXFace) hasGlyph(r rune) bool {
  124. _, ok := g.f.GlyphAdvance(r)
  125. return ok
  126. }
  127. // appendGlyphsForLine implements Face.
  128. func (g *GoXFace) appendGlyphsForLine(glyphs []Glyph, line string, indexOffset int, originX, originY float64) []Glyph {
  129. g.copyCheck()
  130. origin := fixed.Point26_6{
  131. X: float64ToFixed26_6(originX),
  132. Y: float64ToFixed26_6(originY),
  133. }
  134. ox := origin.X
  135. originXs := g.originXs(line)
  136. var advanceIndex int
  137. for i, r := range line {
  138. if i > 0 {
  139. origin.X = ox + originXs[advanceIndex]
  140. advanceIndex++
  141. }
  142. // imgX and imgY are integers so that the nearest filter can be used.
  143. img, imgX, imgY := g.glyphImage(r, origin)
  144. // Adjust the position to the integers.
  145. // The current glyph images assume that they are rendered on integer positions so far.
  146. _, size := utf8.DecodeRuneInString(line[i:])
  147. // Append a glyph even if img is nil.
  148. // This is necessary to return index information for control characters.
  149. glyphs = append(glyphs, Glyph{
  150. StartIndexInBytes: indexOffset + i,
  151. EndIndexInBytes: indexOffset + i + size,
  152. Image: img,
  153. X: float64(imgX),
  154. Y: float64(imgY),
  155. OriginX: fixed26_6ToFloat64(origin.X),
  156. OriginY: fixed26_6ToFloat64(origin.Y),
  157. OriginOffsetX: 0,
  158. OriginOffsetY: 0,
  159. })
  160. }
  161. return glyphs
  162. }
  163. func (g *GoXFace) glyphImage(r rune, origin fixed.Point26_6) (*ebiten.Image, int, int) {
  164. // Assume that GoXFace's direction is always horizontal.
  165. origin.X = adjustGranularity(origin.X, g)
  166. origin.Y &^= ((1 << 6) - 1)
  167. b, _, _ := g.f.GlyphBounds(r)
  168. subpixelOffset := fixed.Point26_6{
  169. X: (origin.X + b.Min.X) & ((1 << 6) - 1),
  170. Y: (origin.Y + b.Min.Y) & ((1 << 6) - 1),
  171. }
  172. key := goXFaceGlyphImageCacheKey{
  173. rune: r,
  174. xoffset: subpixelOffset.X,
  175. }
  176. img := g.glyphImageCache.getOrCreate(key, func() (*ebiten.Image, bool) {
  177. img := g.glyphImageImpl(r, subpixelOffset, b)
  178. return img, img != nil
  179. })
  180. imgX := (origin.X + b.Min.X).Floor()
  181. imgY := (origin.Y + b.Min.Y).Floor()
  182. return img, imgX, imgY
  183. }
  184. func (g *GoXFace) glyphImageImpl(r rune, subpixelOffset fixed.Point26_6, glyphBounds fixed.Rectangle26_6) *ebiten.Image {
  185. w, h := (glyphBounds.Max.X - glyphBounds.Min.X).Ceil(), (glyphBounds.Max.Y - glyphBounds.Min.Y).Ceil()
  186. if w == 0 || h == 0 {
  187. return nil
  188. }
  189. // Add always 1 to the size.
  190. // In theory, it is possible to determine whether +1 is necessary or not, but the calculation is pretty complicated.
  191. w++
  192. h++
  193. rgba := image.NewRGBA(image.Rect(0, 0, w, h))
  194. d := font.Drawer{
  195. Dst: rgba,
  196. Src: image.White,
  197. Face: g.f,
  198. Dot: fixed.Point26_6{
  199. X: -glyphBounds.Min.X + subpixelOffset.X,
  200. Y: -glyphBounds.Min.Y + subpixelOffset.Y,
  201. },
  202. }
  203. d.DrawString(string(r))
  204. return ebiten.NewImageFromImage(rgba)
  205. }
  206. // direction implements Face.
  207. func (g *GoXFace) direction() Direction {
  208. return DirectionLeftToRight
  209. }
  210. // appendVectorPathForLine implements Face.
  211. func (g *GoXFace) appendVectorPathForLine(path *vector.Path, line string, originX, originY float64) {
  212. }
  213. // Metrics implements Face.
  214. func (g *GoXFace) private() {
  215. }