Explorar el Código

Fix #280, blur font on darwin (#298)

* fix #280
Font's on retina will be blur cause Rentina have 2x DPI
Now we times the DPI by 2 when on darwin, and scale the Label panel by 0.5 to keep the original size
Since we scaled the panel size back, so it should not affect much on non-Rentina darwin
However, if you are using non-darwin with rentina monitor, issue #280 will still happen, need a way to detect the monitor

* remove a debug log

* we should devide on int but not floor, or the text will be out of shape

* Revert "we should devide on int but not floor, or the text will be out of shape"
I was completely wrong, we should divide on the floor or the text won't keep it's original size

This reverts commit 73f1b6a320f90cfec9de8878ec06fa351966beaf.

* use `window.Get().GetScale()` instead of hardcoded size and os

* use explicit rune casting to pass `go test`

* fix format for font.go

* fix for Edit
Kevin Z hace 2 años
padre
commit
4e30d5c3f7
Se han modificado 4 ficheros con 73 adiciones y 29 borrados
  1. 1 1
      gui/builder.go
  2. 6 4
      gui/edit.go
  3. 12 2
      gui/label.go
  4. 54 22
      text/font.go

+ 1 - 1
gui/builder.go

@@ -879,7 +879,7 @@ func AttribCheckIcons(b *Builder, am map[string]interface{}, fname string) error
 		if err != nil {
 			return b.err(am, fname, fmt.Sprintf("Invalid icon codepoint value/name:%v", parts[i]))
 		}
-		text += string(val)
+		text += string(rune(val))
 	}
 	am[fname] = text
 	return nil

+ 6 - 4
gui/edit.go

@@ -394,7 +394,7 @@ func (ed *Edit) CursorInput(s string) {
 
 	// Checks if new text exceeds edit width
 	width, _ := ed.Label.font.MeasureText(newText)
-	if float32(width)+editMarginX+float32(1) >= ed.Label.ContentWidth() {
+	if float32(width) / float32(ed.Label.font.ScaleX()) + editMarginX + float32(1) >= ed.Label.ContentWidth() {
 		return
 	}
 
@@ -412,7 +412,8 @@ func (ed *Edit) CursorInput(s string) {
 func (ed *Edit) redraw(caret bool) {
 
 	line := 0
-	ed.Label.setTextCaret(ed.text, editMarginX, ed.width, caret, line, ed.col, ed.selStart, ed.selEnd)
+	scaleX, _ := window.Get().GetScale()
+	ed.Label.setTextCaret(ed.text, editMarginX, int(float64(ed.width) * scaleX), caret, line, ed.col, ed.selStart, ed.selEnd)
 }
 
 // onKey receives subscribed key events
@@ -499,7 +500,7 @@ func (ed *Edit) handleMouse(mouseX float32, dragged bool) {
 	for nchars = 1; nchars <= text.StrCount(ed.text); nchars++ {
 		width, _ := ed.Label.font.MeasureText(text.StrPrefix(ed.text, nchars))
 		posx := mouseX - ed.pospix.X
-		if posx < editMarginX+float32(width) {
+		if posx < editMarginX + float32(float64(width) / ed.Label.font.ScaleX()) {
 			break
 		}
 	}
@@ -597,8 +598,9 @@ func (ed *Edit) applyStyle(s *EditStyle) {
 	//ed.Label.SetBgAlpha(s.BgAlpha)
 
 	if !ed.focus && len(ed.text) == 0 && len(ed.placeHolder) > 0 {
+		scaleX, _ := window.Get().GetScale()
 		ed.Label.SetColor4(&s.HolderColor)
-		ed.Label.setTextCaret(ed.placeHolder, editMarginX, ed.width, false, -1, ed.col, ed.selStart, ed.selEnd)
+		ed.Label.setTextCaret(ed.placeHolder, editMarginX, int(float64(ed.width) * scaleX), false, -1, ed.col, ed.selStart, ed.selEnd)
 	} else {
 		ed.Label.SetColor4(&s.FgColor)
 		ed.redraw(ed.focus)

+ 12 - 2
gui/label.go

@@ -9,6 +9,7 @@ import (
 	"github.com/g3n/engine/math32"
 	"github.com/g3n/engine/text"
 	"github.com/g3n/engine/texture"
+	"github.com/g3n/engine/window"
 )
 
 // Label is a panel which contains a texture with text.
@@ -83,6 +84,9 @@ func (l *Label) SetText(text string) {
 	l.font.SetAttributes(&l.style.FontAttributes)
 	l.font.SetColor(&l.style.FgColor)
 
+	scaleX, scaleY := window.Get().GetScale()
+	l.font.SetScaleXY(scaleX, scaleY)
+
 	// Create an image with the text
 	textImage := l.font.DrawText(text)
 
@@ -98,7 +102,10 @@ func (l *Label) SetText(text string) {
 	}
 
 	// Update label panel dimensions
-	l.Panel.SetContentSize(float32(textImage.Rect.Dx()), float32(textImage.Rect.Dy()))
+	width, height := float32(textImage.Rect.Dx()), float32(textImage.Rect.Dy())
+	// since we enlarged the font texture for higher quality, we have to scale it back to it's original point size
+	width, height = width / float32(scaleX), height / float32(scaleY)
+	l.Panel.SetContentSize(width, height)
 }
 
 // Text returns the label text.
@@ -219,6 +226,9 @@ func (l *Label) setTextCaret(msg string, mx, width int, drawCaret bool, line, co
 	l.font.SetAttributes(&l.style.FontAttributes)
 	l.font.SetColor(&l.style.FgColor)
 
+	scaleX, scaleY := window.Get().GetScale()
+	l.font.SetScaleXY(scaleX, scaleY)
+
 	// Create canvas and draw text
 	_, height := l.font.MeasureText(msg)
 	canvas := text.NewCanvas(width, height, &l.style.BgColor)
@@ -237,6 +247,6 @@ func (l *Label) setTextCaret(msg string, mx, width int, drawCaret bool, line, co
 	l.tex.SetMinFilter(gls.NEAREST)
 
 	// Updates label panel dimensions
-	l.Panel.SetContentSize(float32(width), float32(height))
+	l.Panel.SetContentSize(float32(width) / float32(scaleX), float32(height) / float32(scaleY))
 	l.text = msg
 }

+ 54 - 22
text/font.go

@@ -9,6 +9,7 @@ import (
 	"image/color"
 	"image/draw"
 	"io/ioutil"
+	"math"
 	"strings"
 
 	"github.com/g3n/engine/math32"
@@ -20,12 +21,13 @@ import (
 // Font represents a TrueType font face.
 // Attributes must be set prior to drawing.
 type Font struct {
-	ttf     *truetype.Font // The TrueType font
-	face    font.Face      // The font face
-	attrib  FontAttributes // Internal attribute cache
-	fg      *image.Uniform // Text color cache
-	bg      *image.Uniform // Background color cache
-	changed bool           // Whether attributes have changed and the font face needs to be recreated
+	ttf            *truetype.Font // The TrueType font
+	face           font.Face      // The font face
+	attrib         FontAttributes // Internal attribute cache
+	fg             *image.Uniform // Text color cache
+	bg             *image.Uniform // Background color cache
+	scaleX, scaleY float64        // Scales of actual pixel/GL point, used for fix Retina Monitor
+	changed        bool           // Whether attributes have changed and the font face needs to be recreated
 }
 
 // FontAttributes contains tunable attributes of a font.
@@ -36,6 +38,18 @@ type FontAttributes struct {
 	Hinting     font.Hinting // Font hinting
 }
 
+func (a *FontAttributes) newTTOptions(scaleX, scaleY float64) *truetype.Options {
+	dpi := a.DPI
+	if scaleX != 0 && scaleY != 0 {
+		dpi *= math.Sqrt(scaleX * scaleY)
+	}
+	return &truetype.Options{
+		Size:    a.PointSize,
+		DPI:     dpi,
+		Hinting: a.Hinting,
+	}
+}
+
 // Font Hinting types.
 const (
 	HintingNone     = font.HintingNone
@@ -75,11 +89,7 @@ func NewFontFromData(fontData []byte) (*Font, error) {
 	f.SetColor(&math32.Color4{0, 0, 0, 1})
 
 	// Create font face
-	f.face = truetype.NewFace(f.ttf, &truetype.Options{
-		Size:    f.attrib.PointSize,
-		DPI:     f.attrib.DPI,
-		Hinting: f.attrib.Hinting,
-	})
+	f.face = truetype.NewFace(f.ttf, f.attrib.newTTOptions(f.scaleX, f.scaleY))
 
 	return f, nil
 }
@@ -124,6 +134,29 @@ func (f *Font) SetHinting(hinting font.Hinting) {
 	f.changed = true
 }
 
+func (f *Font) ScaleXY() (x, y float64) {
+	return f.scaleX, f.scaleY
+}
+
+func (f *Font) ScaleX() float64 {
+	return f.scaleX
+}
+
+func (f *Font) ScaleY() float64 {
+	return f.scaleY
+}
+
+// SetScale sets the ratio of actual pixel/GL point.
+func (f *Font) SetScaleXY(x, y float64) {
+
+	if x == f.scaleX && y == f.scaleY {
+		return
+	}
+	f.scaleX = x
+	f.scaleY = y
+	f.changed = true
+}
+
 // SetFgColor sets the text color.
 func (f *Font) SetFgColor(color *math32.Color4) {
 
@@ -158,11 +191,7 @@ func (f *Font) SetAttributes(fa *FontAttributes) {
 func (f *Font) updateFace() {
 
 	if f.changed {
-		f.face = truetype.NewFace(f.ttf, &truetype.Options{
-			Size:    f.attrib.PointSize,
-			DPI:     f.attrib.DPI,
-			Hinting: f.attrib.Hinting,
-		})
+		f.face = truetype.NewFace(f.ttf, f.attrib.newTTOptions(f.scaleX, f.scaleY))
 		f.changed = false
 	}
 }
@@ -277,6 +306,7 @@ func (c Canvas) DrawTextCaret(x, y int, text string, f *Font, drawCaret bool, li
 	d := &font.Drawer{Dst: c.RGBA, Src: f.fg, Face: f.face}
 
 	// Draw text
+	actualPointSize := int(f.attrib.PointSize * f.scaleY)
 	metrics := f.face.Metrics()
 	py := y + metrics.Ascent.Round()
 	lineHeight := (metrics.Ascent + metrics.Descent).Ceil()
@@ -291,8 +321,8 @@ func (c Canvas) DrawTextCaret(x, y int, text string, f *Font, drawCaret bool, li
 			// TODO This will not work when the selection spans multiple lines
 			// Currently there is no multiline edit text
 			// Once there is, this needs to change
-			caretH := int(f.attrib.PointSize) + 2
-			caretY := int(d.Dot.Y>>6) - int(f.attrib.PointSize) + 2
+			caretH := actualPointSize + 2
+			caretY := int(d.Dot.Y>>6) - actualPointSize + 2
 			color := Color4RGBA(&math32.Color4{0, 0, 1, 0.5}) // Hardcoded to blue, alpha 50%
 			for w := width; w < widthEnd; w++ {
 				for j := caretY; j < caretY+caretH; j++ {
@@ -305,11 +335,13 @@ func (c Canvas) DrawTextCaret(x, y int, text string, f *Font, drawCaret bool, li
 		if drawCaret && l == line && col <= StrCount(s) {
 			width, _ := f.MeasureText(StrPrefix(s, col))
 			// Draw caret vertical line
-			caretH := int(f.attrib.PointSize) + 2
-			caretY := int(d.Dot.Y>>6) - int(f.attrib.PointSize) + 2
+			caretH := actualPointSize + 2
+			caretY := int(d.Dot.Y>>6) - actualPointSize + 2
 			color := Color4RGBA(&math32.Color4{0, 0, 0, 1}) // Hardcoded to black
-			for j := caretY; j < caretY+caretH; j++ {
-				c.RGBA.Set(x+width, j, color)
+			for i := 0; i < int(f.scaleX); i++ {
+				for j := caretY; j < caretY+caretH; j++ {
+					c.RGBA.Set(x+width+i, j, color)
+				}
 			}
 		}
 		py += lineHeight