font.go 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333
  1. // Copyright 2016 The G3N Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. package text
  5. import (
  6. "github.com/g3n/engine/math32"
  7. "github.com/golang/freetype/truetype"
  8. "golang.org/x/image/font"
  9. "golang.org/x/image/math/fixed"
  10. "image"
  11. "image/color"
  12. "image/draw"
  13. "io/ioutil"
  14. "math"
  15. "strings"
  16. )
  17. // Font represents a TrueType font
  18. type Font struct {
  19. ttf *truetype.Font
  20. face font.Face
  21. fgColor math32.Color4
  22. bgColor math32.Color4
  23. fontSize float64
  24. fontDPI float64
  25. lineSpacing float64
  26. fg *image.Uniform
  27. bg *image.Uniform
  28. hinting font.Hinting
  29. changed bool
  30. }
  31. // Font hinting types
  32. const (
  33. HintingNone = font.HintingNone
  34. HintingVertical = font.HintingVertical
  35. HintingFull = font.HintingFull
  36. )
  37. // NewFont creates and returns a new font object using the specified
  38. // truetype font file
  39. func NewFont(fontfile string) (*Font, error) {
  40. // Reads font bytes
  41. fontBytes, err := ioutil.ReadFile(fontfile)
  42. if err != nil {
  43. return nil, err
  44. }
  45. return NewFontFromData(fontBytes)
  46. }
  47. // NewFontFromData creates and returns a new font object from the
  48. // specified data
  49. func NewFontFromData(fontData []byte) (*Font, error) {
  50. // Parses the font data
  51. ff, err := truetype.Parse(fontData)
  52. if err != nil {
  53. return nil, err
  54. }
  55. f := new(Font)
  56. f.ttf = ff
  57. f.fontSize = 12
  58. f.fontDPI = 72
  59. f.lineSpacing = 1.0
  60. f.hinting = font.HintingNone
  61. f.SetFgColor4(&math32.Color4{0, 0, 0, 1})
  62. f.SetBgColor4(&math32.Color4{1, 1, 1, 0})
  63. f.changed = false
  64. // Creates font face
  65. f.face = truetype.NewFace(f.ttf, &truetype.Options{
  66. Size: f.fontSize,
  67. DPI: f.fontDPI,
  68. Hinting: f.hinting,
  69. })
  70. return f, nil
  71. }
  72. // SetDPI sets the current DPI for the font
  73. func (f *Font) SetDPI(dpi float64) {
  74. if dpi == f.fontDPI {
  75. return
  76. }
  77. f.fontDPI = dpi
  78. f.changed = true
  79. }
  80. // SetSize sets the size of the font
  81. func (f *Font) SetSize(size float64) {
  82. if size == f.fontSize {
  83. return
  84. }
  85. f.fontSize = size
  86. f.changed = true
  87. }
  88. // SetHinting sets the hinting type
  89. func (f *Font) SetHinting(hinting font.Hinting) {
  90. if hinting == f.hinting {
  91. return
  92. }
  93. f.hinting = hinting
  94. f.changed = true
  95. }
  96. // Size returns the current font size
  97. func (f *Font) Size() float64 {
  98. return f.fontSize
  99. }
  100. // DPI returns the current font DPI
  101. func (f *Font) DPI() float64 {
  102. return f.fontDPI
  103. }
  104. // SetFgColor sets the current foreground color of the font
  105. // The alpha value is set to 1 (opaque)
  106. func (f *Font) SetFgColor(color *math32.Color) {
  107. f.fgColor.R = color.R
  108. f.fgColor.G = color.G
  109. f.fgColor.B = color.B
  110. f.fgColor.A = 1.0
  111. f.fg = image.NewUniform(Color4NRGBA(&f.fgColor))
  112. }
  113. // SetFgColor4 sets the current foreground color of the font
  114. func (f *Font) SetFgColor4(color *math32.Color4) {
  115. f.fgColor = *color
  116. f.fg = image.NewUniform(Color4NRGBA(color))
  117. }
  118. // FgColor4 returns the current foreground color
  119. func (f *Font) FgColor4() math32.Color4 {
  120. return f.fgColor
  121. }
  122. // SetBgColor sets the current foreground color of the font
  123. // The alpha value is set to 1 (opaque)
  124. func (f *Font) SetBgColor(color *math32.Color) {
  125. f.bgColor.R = color.R
  126. f.bgColor.G = color.G
  127. f.bgColor.B = color.B
  128. f.bgColor.A = 1.0
  129. f.bg = image.NewUniform(Color4NRGBA(&f.fgColor))
  130. }
  131. // SetBgColor4 sets the current background color of the font
  132. func (f *Font) SetBgColor4(color *math32.Color4) {
  133. f.bgColor = *color
  134. f.bg = image.NewUniform(Color4NRGBA(color))
  135. }
  136. // BgColor4 returns the current background color
  137. func (f *Font) BgColor4() math32.Color4 {
  138. return f.bgColor
  139. }
  140. // SetLineSpacing sets the spacing between lines
  141. func (f *Font) SetLineSpacing(spacing float64) {
  142. f.lineSpacing = spacing
  143. }
  144. // MeasureText returns the maximum width and height in pixels
  145. // necessary for an image to contain the specified text.
  146. // The supplied text string can contain line break escape sequences (\n).
  147. func (f *Font) MeasureText(text string) (int, int) {
  148. // Creates drawer
  149. f.updateFace()
  150. d := &font.Drawer{Dst: nil, Src: f.fg, Face: f.face}
  151. // Draw text
  152. width := 0
  153. py := int(math.Ceil(f.fontSize * f.fontDPI / 72))
  154. dy := int(math.Ceil(f.fontSize * f.lineSpacing * f.fontDPI / 72))
  155. lines := strings.Split(text, "\n")
  156. for _, s := range lines {
  157. d.Dot = fixed.P(0, py)
  158. lfixed := d.MeasureString(s)
  159. lw := int(lfixed >> 6)
  160. if lw > width {
  161. width = lw
  162. }
  163. py += dy
  164. }
  165. height := py - dy/2
  166. return width, height
  167. }
  168. // Metrics returns the font metrics
  169. func (f *Font) Metrics() font.Metrics {
  170. f.updateFace()
  171. return f.face.Metrics()
  172. }
  173. // Canvas is an image to draw text
  174. type Canvas struct {
  175. RGBA *image.RGBA
  176. bgColor *image.Uniform
  177. }
  178. // Update font face if font parameters changed
  179. func (f *Font) updateFace() {
  180. if f.changed {
  181. f.face = truetype.NewFace(f.ttf, &truetype.Options{
  182. Size: f.fontSize,
  183. DPI: f.fontDPI,
  184. Hinting: f.hinting,
  185. })
  186. f.changed = false
  187. }
  188. }
  189. // NewCanvas creates and returns a pointer to a new canvas with the
  190. // specified width and height in pixels and background color
  191. func NewCanvas(width, height int, bgColor *math32.Color4) *Canvas {
  192. c := new(Canvas)
  193. c.RGBA = image.NewRGBA(image.Rect(0, 0, width, height))
  194. // Creates the image.Uniform for the background color
  195. c.bgColor = image.NewUniform(Color4NRGBA(bgColor))
  196. // Draw image
  197. draw.Draw(c.RGBA, c.RGBA.Bounds(), c.bgColor, image.ZP, draw.Src)
  198. return c
  199. }
  200. // DrawText draws text at the specified position (in pixels)
  201. // of this canvas, using the specified font.
  202. // The supplied text string can contain line break escape sequences (\n).
  203. func (c Canvas) DrawText(x, y int, text string, f *Font) {
  204. // Creates drawer
  205. f.updateFace()
  206. d := &font.Drawer{Dst: c.RGBA, Src: f.fg, Face: f.face}
  207. // Draw text
  208. py := y + int(math.Ceil(f.fontSize*f.fontDPI/72))
  209. dy := int(math.Ceil(f.fontSize * f.lineSpacing * f.fontDPI / 72))
  210. lines := strings.Split(text, "\n")
  211. for _, s := range lines {
  212. d.Dot = fixed.P(x, py)
  213. d.DrawString(s)
  214. py += dy
  215. }
  216. }
  217. // DrawTextCaret draws text at the specified position (in pixels)
  218. // of this canvas, using the specified font, and also a caret at
  219. // the specified line and column.
  220. // The supplied text string can contain line break escape sequences (\n).
  221. func (c Canvas) DrawTextCaret(x, y int, text string, f *Font, line, col int) error {
  222. // Creates drawer
  223. f.updateFace()
  224. d := &font.Drawer{Dst: c.RGBA, Src: f.fg, Face: f.face}
  225. py := y + int(math.Ceil(f.fontSize*f.fontDPI/72))
  226. dy := int(math.Ceil(f.fontSize * f.lineSpacing * f.fontDPI / 72))
  227. lines := strings.Split(text, "\n")
  228. for l, s := range lines {
  229. d.Dot = fixed.P(x, py)
  230. d.DrawString(s)
  231. // Checks for caret position
  232. if l == line && col <= StrCount(s) {
  233. width, _ := f.MeasureText(StrPrefix(s, col))
  234. // Draw caret vertical line
  235. caretH := int(f.fontSize) + 2
  236. //caretY := int(pt.Y>>6) - int(f.fontSize) + 2
  237. caretY := int(d.Dot.Y>>6) - int(f.fontSize) + 2
  238. color := Color4NRGBA(&math32.Color4{0, 0, 0, 1})
  239. for j := caretY; j < caretY+caretH; j++ {
  240. c.RGBA.Set(x+width, j, color)
  241. }
  242. }
  243. py += dy
  244. }
  245. // pt := freetype.Pt(font.marginX+x, font.marginY+y+int(font.ctx.PointToFixed(font.fontSize)>>6))
  246. // for l, s := range lines {
  247. // // Draw string
  248. // _, err := font.ctx.DrawString(s, pt)
  249. // if err != nil {
  250. // return err
  251. // }
  252. // // Checks for caret position
  253. // if l == line && col <= StrCount(s) {
  254. // width, _, err := font.MeasureText(StrPrefix(s, col))
  255. // if err != nil {
  256. // return err
  257. // }
  258. // // Draw caret vertical line
  259. // caretH := int(font.fontSize) + 2
  260. // caretY := int(pt.Y>>6) - int(font.fontSize) + 2
  261. // color := Color4NRGBA(&math32.Color4{0, 0, 0, 1})
  262. // for j := caretY; j < caretY+caretH; j++ {
  263. // c.RGBA.Set(x+width, j, color)
  264. // }
  265. // }
  266. // // Increment y coordinate
  267. // pt.Y += font.ctx.PointToFixed(font.fontSize * font.lineSpacing)
  268. // }
  269. return nil
  270. }
  271. // Color4NRGBA converts a math32.Color4 to Go's image/color.NRGBA
  272. // NON pre-multiplied alpha.
  273. func Color4NRGBA(c *math32.Color4) color.NRGBA {
  274. red := uint8(c.R * 0xFF)
  275. green := uint8(c.G * 0xFF)
  276. blue := uint8(c.B * 0xFF)
  277. al := uint8(c.A * 0xFF)
  278. return color.NRGBA{red, green, blue, al}
  279. }