gotextseg.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  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. "image/draw"
  18. "math"
  19. "github.com/go-text/typesetting/font/opentype"
  20. "golang.org/x/image/math/fixed"
  21. gvector "golang.org/x/image/vector"
  22. "github.com/hajimehoshi/ebiten/v2"
  23. "github.com/hajimehoshi/ebiten/v2/vector"
  24. )
  25. func segmentsToBounds(segs []opentype.Segment) fixed.Rectangle26_6 {
  26. if len(segs) == 0 {
  27. return fixed.Rectangle26_6{}
  28. }
  29. minX := float32(math.Inf(1))
  30. minY := float32(math.Inf(1))
  31. maxX := float32(math.Inf(-1))
  32. maxY := float32(math.Inf(-1))
  33. for _, seg := range segs {
  34. n := 1
  35. switch seg.Op {
  36. case opentype.SegmentOpQuadTo:
  37. n = 2
  38. case opentype.SegmentOpCubeTo:
  39. n = 3
  40. }
  41. for i := 0; i < n; i++ {
  42. x := seg.Args[i].X
  43. y := seg.Args[i].Y
  44. if minX > x {
  45. minX = x
  46. }
  47. if minY > y {
  48. minY = y
  49. }
  50. if maxX < x {
  51. maxX = x
  52. }
  53. if maxY < y {
  54. maxY = y
  55. }
  56. }
  57. }
  58. return fixed.Rectangle26_6{
  59. Min: fixed.Point26_6{
  60. X: float32ToFixed26_6(minX),
  61. Y: float32ToFixed26_6(minY),
  62. },
  63. Max: fixed.Point26_6{
  64. X: float32ToFixed26_6(maxX),
  65. Y: float32ToFixed26_6(maxY),
  66. },
  67. }
  68. }
  69. func segmentsToImage(segs []opentype.Segment, subpixelOffset fixed.Point26_6, glyphBounds fixed.Rectangle26_6) *ebiten.Image {
  70. if len(segs) == 0 {
  71. return nil
  72. }
  73. w, h := (glyphBounds.Max.X - glyphBounds.Min.X).Ceil(), (glyphBounds.Max.Y - glyphBounds.Min.Y).Ceil()
  74. if w == 0 || h == 0 {
  75. return nil
  76. }
  77. // Add always 1 to the size.
  78. // In theory, it is possible to determine whether +1 is necessary or not, but the calculation is pretty complicated.
  79. w++
  80. h++
  81. biasX := fixed26_6ToFloat32(-glyphBounds.Min.X + subpixelOffset.X)
  82. biasY := fixed26_6ToFloat32(-glyphBounds.Min.Y + subpixelOffset.Y)
  83. rast := gvector.NewRasterizer(w, h)
  84. rast.DrawOp = draw.Src
  85. for _, seg := range segs {
  86. switch seg.Op {
  87. case opentype.SegmentOpMoveTo:
  88. rast.MoveTo(seg.Args[0].X+biasX, seg.Args[0].Y+biasY)
  89. case opentype.SegmentOpLineTo:
  90. rast.LineTo(seg.Args[0].X+biasX, seg.Args[0].Y+biasY)
  91. case opentype.SegmentOpQuadTo:
  92. rast.QuadTo(
  93. seg.Args[0].X+biasX, seg.Args[0].Y+biasY,
  94. seg.Args[1].X+biasX, seg.Args[1].Y+biasY,
  95. )
  96. case opentype.SegmentOpCubeTo:
  97. rast.CubeTo(
  98. seg.Args[0].X+biasX, seg.Args[0].Y+biasY,
  99. seg.Args[1].X+biasX, seg.Args[1].Y+biasY,
  100. seg.Args[2].X+biasX, seg.Args[2].Y+biasY,
  101. )
  102. }
  103. }
  104. // Explicit closing is necessary especially for some OpenType fonts like
  105. // NotoSansJP-VF.otf in https://github.com/notofonts/noto-cjk/releases/tag/Sans2.004.
  106. // See also https://github.com/go-text/typesetting/issues/122.
  107. rast.ClosePath()
  108. dst := image.NewRGBA(image.Rect(0, 0, w, h))
  109. rast.Draw(dst, dst.Bounds(), image.Opaque, image.Point{})
  110. return ebiten.NewImageFromImage(dst)
  111. }
  112. func appendVectorPathFromSegments(path *vector.Path, segs []opentype.Segment, x, y float32) {
  113. for _, seg := range segs {
  114. switch seg.Op {
  115. case opentype.SegmentOpMoveTo:
  116. path.MoveTo(seg.Args[0].X+x, seg.Args[0].Y+y)
  117. case opentype.SegmentOpLineTo:
  118. path.LineTo(seg.Args[0].X+x, seg.Args[0].Y+y)
  119. case opentype.SegmentOpQuadTo:
  120. path.QuadTo(
  121. seg.Args[0].X+x, seg.Args[0].Y+y,
  122. seg.Args[1].X+x, seg.Args[1].Y+y,
  123. )
  124. case opentype.SegmentOpCubeTo:
  125. path.CubicTo(
  126. seg.Args[0].X+x, seg.Args[0].Y+y,
  127. seg.Args[1].X+x, seg.Args[1].Y+y,
  128. seg.Args[2].X+x, seg.Args[2].Y+y,
  129. )
  130. }
  131. }
  132. path.Close()
  133. }