| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330 |
- // Copyright 2016 The G3N Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package text
- import (
- "github.com/g3n/engine/math32"
- "github.com/golang/freetype/truetype"
- "golang.org/x/image/font"
- "golang.org/x/image/math/fixed"
- "image"
- "image/color"
- "image/draw"
- "io/ioutil"
- "math"
- "strings"
- )
- type Font struct {
- ttf *truetype.Font
- face font.Face
- fgColor math32.Color4
- bgColor math32.Color4
- fontSize float64
- fontDPI float64
- lineSpacing float64
- fg *image.Uniform
- bg *image.Uniform
- hinting font.Hinting
- changed bool
- }
- const (
- HintingNone = font.HintingNone
- HintingVertical = font.HintingVertical
- HintingFull = font.HintingFull
- )
- // NewFont creates and returns a new font object using the specified
- // truetype font file
- func NewFont(fontfile string) (*Font, error) {
- // Reads font bytes
- fontBytes, err := ioutil.ReadFile(fontfile)
- if err != nil {
- return nil, err
- }
- return NewFontFromData(fontBytes)
- }
- // NewFontFromData creates and returns a new font object from the
- // specified data
- func NewFontFromData(fontData []byte) (*Font, error) {
- // Parses the font data
- ff, err := truetype.Parse(fontData)
- if err != nil {
- return nil, err
- }
- f := new(Font)
- f.ttf = ff
- f.fontSize = 12
- f.fontDPI = 72
- f.lineSpacing = 1.0
- f.hinting = font.HintingNone
- f.SetFgColor4(&math32.Color4{0, 0, 0, 1})
- f.SetBgColor4(&math32.Color4{1, 1, 1, 0})
- f.changed = false
- // Creates font face
- f.face = truetype.NewFace(f.ttf, &truetype.Options{
- Size: f.fontSize,
- DPI: f.fontDPI,
- Hinting: f.hinting,
- })
- return f, nil
- }
- // SetDPI sets the current DPI for the font
- func (f *Font) SetDPI(dpi float64) {
- if dpi == f.fontDPI {
- return
- }
- f.fontDPI = dpi
- f.changed = true
- }
- // SetSize sets the size of the font
- func (f *Font) SetSize(size float64) {
- if size == f.fontSize {
- return
- }
- f.fontSize = size
- f.changed = true
- }
- // SetHinting sets the hinting type
- func (f *Font) SetHinting(hinting font.Hinting) {
- if hinting == f.hinting {
- return
- }
- f.hinting = hinting
- f.changed = true
- }
- // Size returns the current font size
- func (f *Font) Size() float64 {
- return f.fontSize
- }
- // DPI returns the current font DPI
- func (f *Font) DPI() float64 {
- return f.fontDPI
- }
- // SetFgColor sets the current foreground color of the font
- // The alpha value is set to 1 (opaque)
- func (f *Font) SetFgColor(color *math32.Color) {
- f.fgColor.R = color.R
- f.fgColor.G = color.G
- f.fgColor.B = color.B
- f.fgColor.A = 1.0
- f.fg = image.NewUniform(Color4NRGBA(&f.fgColor))
- }
- // SetFgColor4 sets the current foreground color of the font
- func (f *Font) SetFgColor4(color *math32.Color4) {
- f.fgColor = *color
- f.fg = image.NewUniform(Color4NRGBA(color))
- }
- // FgColor returns the current foreground color
- func (f *Font) FgColor4() math32.Color4 {
- return f.fgColor
- }
- // SetBgColor sets the current foreground color of the font
- // The alpha value is set to 1 (opaque)
- func (f *Font) SetBgColor(color *math32.Color) {
- f.bgColor.R = color.R
- f.bgColor.G = color.G
- f.bgColor.B = color.B
- f.bgColor.A = 1.0
- f.bg = image.NewUniform(Color4NRGBA(&f.fgColor))
- }
- // SetBgColor sets the current background color of the font
- func (f *Font) SetBgColor4(color *math32.Color4) {
- f.bgColor = *color
- f.bg = image.NewUniform(Color4NRGBA(color))
- }
- // BgColor returns the current background color
- func (f *Font) BgColor4() math32.Color4 {
- return f.bgColor
- }
- // SetLineSpacing sets the spacing between lines
- func (f *Font) SetLineSpacing(spacing float64) {
- f.lineSpacing = spacing
- }
- // MeasureText returns the maximum width and height in pixels
- // necessary for an image to contain the specified text.
- // The supplied text string can contain line break escape sequences (\n).
- func (f *Font) MeasureText(text string) (int, int) {
- // Creates drawer
- f.updateFace()
- d := &font.Drawer{Dst: nil, Src: f.fg, Face: f.face}
- // Draw text
- width := 0
- py := int(math.Ceil(f.fontSize * f.fontDPI / 72))
- dy := int(math.Ceil(f.fontSize * f.lineSpacing * f.fontDPI / 72))
- lines := strings.Split(text, "\n")
- for _, s := range lines {
- d.Dot = fixed.P(0, py)
- lfixed := d.MeasureString(s)
- lw := int(lfixed >> 6)
- if lw > width {
- width = lw
- }
- py += dy
- }
- height := py - dy/2
- return width, height
- }
- func (f *Font) Metrics() font.Metrics {
- f.updateFace()
- return f.face.Metrics()
- }
- // Canvas is an image to draw text
- type Canvas struct {
- RGBA *image.RGBA
- bgColor *image.Uniform
- }
- // Update font face if font parameters changed
- func (f *Font) updateFace() {
- if f.changed {
- f.face = truetype.NewFace(f.ttf, &truetype.Options{
- Size: f.fontSize,
- DPI: f.fontDPI,
- Hinting: f.hinting,
- })
- f.changed = false
- }
- }
- // NewCanvas creates and returns a pointer to a new canvas with the
- // specified width and height in pixels and background color
- func NewCanvas(width, height int, bgColor *math32.Color4) *Canvas {
- c := new(Canvas)
- c.RGBA = image.NewRGBA(image.Rect(0, 0, width, height))
- // Creates the image.Uniform for the background color
- c.bgColor = image.NewUniform(Color4NRGBA(bgColor))
- // Draw image
- draw.Draw(c.RGBA, c.RGBA.Bounds(), c.bgColor, image.ZP, draw.Src)
- return c
- }
- // DrawText draws text at the specified position (in pixels)
- // of this canvas, using the specified font.
- // The supplied text string can contain line break escape sequences (\n).
- func (c Canvas) DrawText(x, y int, text string, f *Font) {
- // Creates drawer
- f.updateFace()
- d := &font.Drawer{Dst: c.RGBA, Src: f.fg, Face: f.face}
- // Draw text
- py := y + int(math.Ceil(f.fontSize*f.fontDPI/72))
- dy := int(math.Ceil(f.fontSize * f.lineSpacing * f.fontDPI / 72))
- lines := strings.Split(text, "\n")
- for _, s := range lines {
- d.Dot = fixed.P(x, py)
- d.DrawString(s)
- py += dy
- }
- }
- // DrawTextCaret draws text at the specified position (in pixels)
- // of this canvas, using the specified font, and also a caret at
- // the specified line and column.
- // The supplied text string can contain line break escape sequences (\n).
- func (c Canvas) DrawTextCaret(x, y int, text string, f *Font, line, col int) error {
- // Creates drawer
- f.updateFace()
- d := &font.Drawer{Dst: c.RGBA, Src: f.fg, Face: f.face}
- py := y + int(math.Ceil(f.fontSize*f.fontDPI/72))
- dy := int(math.Ceil(f.fontSize * f.lineSpacing * f.fontDPI / 72))
- lines := strings.Split(text, "\n")
- for l, s := range lines {
- d.Dot = fixed.P(x, py)
- d.DrawString(s)
- // Checks for caret position
- if l == line && col <= StrCount(s) {
- width, _ := f.MeasureText(StrPrefix(s, col))
- // Draw caret vertical line
- caretH := int(f.fontSize) + 2
- //caretY := int(pt.Y>>6) - int(f.fontSize) + 2
- caretY := int(d.Dot.Y>>6) - int(f.fontSize) + 2
- color := Color4NRGBA(&math32.Color4{0, 0, 0, 1})
- for j := caretY; j < caretY+caretH; j++ {
- c.RGBA.Set(x+width, j, color)
- }
- }
- py += dy
- }
- // pt := freetype.Pt(font.marginX+x, font.marginY+y+int(font.ctx.PointToFixed(font.fontSize)>>6))
- // for l, s := range lines {
- // // Draw string
- // _, err := font.ctx.DrawString(s, pt)
- // if err != nil {
- // return err
- // }
- // // Checks for caret position
- // if l == line && col <= StrCount(s) {
- // width, _, err := font.MeasureText(StrPrefix(s, col))
- // if err != nil {
- // return err
- // }
- // // Draw caret vertical line
- // caretH := int(font.fontSize) + 2
- // caretY := int(pt.Y>>6) - int(font.fontSize) + 2
- // color := Color4NRGBA(&math32.Color4{0, 0, 0, 1})
- // for j := caretY; j < caretY+caretH; j++ {
- // c.RGBA.Set(x+width, j, color)
- // }
- // }
- // // Increment y coordinate
- // pt.Y += font.ctx.PointToFixed(font.fontSize * font.lineSpacing)
- // }
- return nil
- }
- // Color4NRGBA converts a math32.Color4 to Go's image/color.NRGBA
- // NON pre-multiplied alpha.
- func Color4NRGBA(c *math32.Color4) color.NRGBA {
- red := uint8(c.R * 0xFF)
- green := uint8(c.G * 0xFF)
- blue := uint8(c.B * 0xFF)
- al := uint8(c.A * 0xFF)
- return color.NRGBA{red, green, blue, al}
- }
|