mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-25 03:08:54 +01:00
text: improve rendering quality with HintingVertical
When HintingVertical is used, the interval between two glyphs is not quantized (i.e. not a whole pixel). The text package didn't consider this situation. This change improves the quality by using more various glyph images with 1/4 pixels granularity in vertical direction. Closes #2469
This commit is contained in:
parent
56788cf8d9
commit
a042af98b1
@ -46,7 +46,7 @@ func init() {
|
||||
mplusSmallFont, err = opentype.NewFace(tt, &opentype.FaceOptions{
|
||||
Size: 24,
|
||||
DPI: dpi,
|
||||
Hinting: font.HintingFull,
|
||||
Hinting: font.HintingVertical,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
@ -54,7 +54,7 @@ func init() {
|
||||
mplusNormalFont, err = opentype.NewFace(tt, &opentype.FaceOptions{
|
||||
Size: 32,
|
||||
DPI: dpi,
|
||||
Hinting: font.HintingFull,
|
||||
Hinting: font.HintingVertical,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
@ -62,7 +62,7 @@ func init() {
|
||||
mplusBigFont, err = opentype.NewFace(tt, &opentype.FaceOptions{
|
||||
Size: 48,
|
||||
DPI: dpi,
|
||||
Hinting: font.HintingFull,
|
||||
Hinting: font.HintingVertical,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -96,7 +96,7 @@ func init() {
|
||||
mplusNormalFont, err = opentype.NewFace(tt, &opentype.FaceOptions{
|
||||
Size: 24,
|
||||
DPI: dpi,
|
||||
Hinting: font.HintingFull,
|
||||
Hinting: font.HintingVertical,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
@ -104,7 +104,7 @@ func init() {
|
||||
mplusBigFont, err = opentype.NewFace(tt, &opentype.FaceOptions{
|
||||
Size: 48,
|
||||
DPI: dpi,
|
||||
Hinting: font.HintingFull,
|
||||
Hinting: font.HintingFull, // Use quantization to save glyph cache images.
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -58,7 +58,7 @@ func initFont() {
|
||||
mplusFont, err = opentype.NewFace(tt, &opentype.FaceOptions{
|
||||
Size: 12 * ebiten.DeviceScaleFactor(),
|
||||
DPI: dpi,
|
||||
Hinting: font.HintingFull,
|
||||
Hinting: font.HintingVertical,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -53,7 +53,7 @@ func init() {
|
||||
mplusNormalFont, err = opentype.NewFace(tt, &opentype.FaceOptions{
|
||||
Size: 24,
|
||||
DPI: dpi,
|
||||
Hinting: font.HintingFull,
|
||||
Hinting: font.HintingVertical,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
@ -61,7 +61,7 @@ func init() {
|
||||
mplusBigFont, err = opentype.NewFace(tt, &opentype.FaceOptions{
|
||||
Size: 32,
|
||||
DPI: dpi,
|
||||
Hinting: font.HintingFull,
|
||||
Hinting: font.HintingVertical,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
@ -57,7 +57,7 @@ func init() {
|
||||
uiFont, err = opentype.NewFace(tt, &opentype.FaceOptions{
|
||||
Size: 12,
|
||||
DPI: 72,
|
||||
Hinting: font.HintingFull,
|
||||
Hinting: font.HintingVertical,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
|
41
text/text.go
41
text/text.go
@ -48,6 +48,10 @@ func fixed26_6ToFloat64(x fixed.Int26_6) float64 {
|
||||
return float64(x>>6) + float64(x&((1<<6)-1))/float64(1<<6)
|
||||
}
|
||||
|
||||
func adjustOffsetGranularity(x fixed.Int26_6) fixed.Int26_6 {
|
||||
return x / (1 << 4) * (1 << 4)
|
||||
}
|
||||
|
||||
func drawGlyph(dst *ebiten.Image, face font.Face, r rune, img *ebiten.Image, topleft fixed.Point26_6, op *ebiten.DrawImageOptions) {
|
||||
if img == nil {
|
||||
return
|
||||
@ -83,21 +87,30 @@ func getGlyphBounds(face font.Face, r rune) fixed.Rectangle26_6 {
|
||||
return b
|
||||
}
|
||||
|
||||
type glyphImageCacheKey struct {
|
||||
rune rune
|
||||
xoffset fixed.Int26_6
|
||||
}
|
||||
|
||||
type glyphImageCacheEntry struct {
|
||||
image *ebiten.Image
|
||||
atime int64
|
||||
}
|
||||
|
||||
var (
|
||||
glyphImageCache = map[font.Face]map[rune]*glyphImageCacheEntry{}
|
||||
glyphImageCache = map[font.Face]map[glyphImageCacheKey]*glyphImageCacheEntry{}
|
||||
)
|
||||
|
||||
func getGlyphImage(face font.Face, r rune, offset fixed.Point26_6) *ebiten.Image {
|
||||
if _, ok := glyphImageCache[face]; !ok {
|
||||
glyphImageCache[face] = map[rune]*glyphImageCacheEntry{}
|
||||
glyphImageCache[face] = map[glyphImageCacheKey]*glyphImageCacheEntry{}
|
||||
}
|
||||
|
||||
if e, ok := glyphImageCache[face][r]; ok {
|
||||
key := glyphImageCacheKey{
|
||||
rune: r,
|
||||
xoffset: offset.X,
|
||||
}
|
||||
if e, ok := glyphImageCache[face][key]; ok {
|
||||
e.atime = now()
|
||||
return e.image
|
||||
}
|
||||
@ -105,7 +118,7 @@ func getGlyphImage(face font.Face, r rune, offset fixed.Point26_6) *ebiten.Image
|
||||
b := getGlyphBounds(face, r)
|
||||
w, h := (b.Max.X - b.Min.X).Ceil(), (b.Max.Y - b.Min.Y).Ceil()
|
||||
if w == 0 || h == 0 {
|
||||
glyphImageCache[face][r] = &glyphImageCacheEntry{
|
||||
glyphImageCache[face][key] = &glyphImageCacheEntry{
|
||||
image: nil,
|
||||
atime: now(),
|
||||
}
|
||||
@ -133,7 +146,7 @@ func getGlyphImage(face font.Face, r rune, offset fixed.Point26_6) *ebiten.Image
|
||||
d.DrawString(string(r))
|
||||
|
||||
img := ebiten.NewImageFromImage(rgba)
|
||||
glyphImageCache[face][r] = &glyphImageCacheEntry{
|
||||
glyphImageCache[face][key] = &glyphImageCacheEntry{
|
||||
image: img,
|
||||
atime: now(),
|
||||
}
|
||||
@ -242,7 +255,7 @@ func DrawWithOptions(dst *ebiten.Image, text string, face font.Face, options *eb
|
||||
// The current glyph images assume that they are rendered on integer positions so far.
|
||||
b := getGlyphBounds(face, r)
|
||||
offset := fixed.Point26_6{
|
||||
X: b.Min.X & ((1 << 6) - 1),
|
||||
X: (adjustOffsetGranularity(dx) + b.Min.X) & ((1 << 6) - 1),
|
||||
Y: b.Min.Y & ((1 << 6) - 1),
|
||||
}
|
||||
img := getGlyphImage(face, r, offset)
|
||||
@ -353,13 +366,24 @@ func CacheGlyphs(face font.Face, text string) {
|
||||
textM.Lock()
|
||||
defer textM.Unlock()
|
||||
|
||||
var dx fixed.Int26_6
|
||||
prevR := rune(-1)
|
||||
for _, r := range text {
|
||||
if prevR >= 0 {
|
||||
dx += face.Kern(prevR, r)
|
||||
}
|
||||
if r == '\n' {
|
||||
dx = 0
|
||||
continue
|
||||
}
|
||||
b := getGlyphBounds(face, r)
|
||||
offset := fixed.Point26_6{
|
||||
X: b.Min.X & ((1 << 6) - 1),
|
||||
X: (adjustOffsetGranularity(dx) + b.Min.X) & ((1 << 6) - 1),
|
||||
Y: b.Min.Y & ((1 << 6) - 1),
|
||||
}
|
||||
getGlyphImage(face, r, offset)
|
||||
dx += glyphAdvance(face, r)
|
||||
prevR = r
|
||||
}
|
||||
}
|
||||
|
||||
@ -448,7 +472,7 @@ func AppendGlyphs(glyphs []Glyph, face font.Face, text string) []Glyph {
|
||||
|
||||
b := getGlyphBounds(face, r)
|
||||
offset := fixed.Point26_6{
|
||||
X: b.Min.X & ((1 << 6) - 1),
|
||||
X: (adjustOffsetGranularity(pos.X) + b.Min.X) & ((1 << 6) - 1),
|
||||
Y: b.Min.Y & ((1 << 6) - 1),
|
||||
}
|
||||
if img := getGlyphImage(face, r, offset); img != nil {
|
||||
@ -463,7 +487,6 @@ func AppendGlyphs(glyphs []Glyph, face font.Face, text string) []Glyph {
|
||||
})
|
||||
}
|
||||
pos.X += glyphAdvance(face, r)
|
||||
|
||||
prevR = r
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user