2017-07-15 20:25:57 +02:00
// Copyright 2017 The Ebiten Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
2023-02-07 04:05:43 +01:00
// Package text offers functions to draw texts on an Ebitengine's image.
2017-07-16 23:39:06 +02:00
//
2017-10-07 20:14:33 +02:00
// For the example using a TTF font, see font package in the examples.
2023-11-11 11:04:13 +01:00
//
// Deprecated: as of v2.7. Use text/v2 instead.
2017-07-15 20:25:57 +02:00
package text
import (
"image"
"image/color"
2018-05-09 16:41:06 +02:00
"sync"
2017-07-15 20:25:57 +02:00
"golang.org/x/image/font"
"golang.org/x/image/math/fixed"
2020-10-03 19:35:13 +02:00
"github.com/hajimehoshi/ebiten/v2"
2023-10-06 06:49:42 +02:00
"github.com/hajimehoshi/ebiten/v2/internal/hook"
2017-07-15 20:25:57 +02:00
)
var (
monotonicClock int64
)
func now ( ) int64 {
return monotonicClock
}
2020-11-03 09:12:49 +01:00
func init ( ) {
2023-10-06 06:49:42 +02:00
hook . AppendHookOnBeforeUpdate ( func ( ) error {
2020-11-03 09:12:49 +01:00
monotonicClock ++
return nil
} )
}
2017-07-17 10:00:51 +02:00
func fixed26_6ToFloat64 ( x fixed . Int26_6 ) float64 {
2020-05-13 16:31:17 +02:00
return float64 ( x >> 6 ) + float64 ( x & ( ( 1 << 6 ) - 1 ) ) / float64 ( 1 << 6 )
2017-07-17 10:00:51 +02:00
}
2022-11-23 10:13:54 +01:00
func adjustOffsetGranularity ( x fixed . Int26_6 ) fixed . Int26_6 {
return x / ( 1 << 4 ) * ( 1 << 4 )
}
2023-01-12 19:28:41 +01:00
func drawGlyph ( dst * ebiten . Image , img * ebiten . Image , topleft fixed . Point26_6 , op * ebiten . DrawImageOptions ) {
2018-04-23 11:43:29 +02:00
if img == nil {
return
}
2021-07-27 10:01:36 +02:00
op2 := & ebiten . DrawImageOptions { }
if op != nil {
* op2 = * op
2021-11-11 16:17:51 +01:00
op2 . GeoM . Reset ( )
2021-07-27 10:01:36 +02:00
}
2022-11-23 14:16:41 +01:00
2022-11-25 07:03:32 +01:00
op2 . GeoM . Translate ( fixed26_6ToFloat64 ( topleft . X ) , fixed26_6ToFloat64 ( topleft . Y ) )
2021-07-27 20:20:35 +02:00
if op != nil {
op2 . GeoM . Concat ( op . GeoM )
}
2022-11-23 14:16:41 +01:00
2021-07-27 10:01:36 +02:00
dst . DrawImage ( img , op2 )
2017-07-15 20:25:57 +02:00
}
2022-11-23 10:13:54 +01:00
type glyphImageCacheKey struct {
rune rune
xoffset fixed . Int26_6
}
2018-03-03 13:53:09 +01:00
type glyphImageCacheEntry struct {
2020-10-03 13:00:12 +02:00
image * ebiten . Image
2018-03-03 13:53:09 +01:00
atime int64
}
2017-07-15 20:25:57 +02:00
2018-03-03 13:53:09 +01:00
var (
2023-05-25 18:44:13 +02:00
glyphImageCache = map [ * faceWithCache ] map [ glyphImageCacheKey ] * glyphImageCacheEntry { }
2018-03-03 13:53:09 +01:00
)
2017-07-15 20:25:57 +02:00
2023-05-25 18:44:13 +02:00
func getGlyphImage ( face * faceWithCache , r rune , offset fixed . Point26_6 ) * ebiten . Image {
2018-03-03 13:53:09 +01:00
if _ , ok := glyphImageCache [ face ] ; ! ok {
2022-11-23 10:13:54 +01:00
glyphImageCache [ face ] = map [ glyphImageCacheKey ] * glyphImageCacheEntry { }
2018-03-03 13:53:09 +01:00
}
2017-07-15 20:25:57 +02:00
2022-11-23 10:13:54 +01:00
key := glyphImageCacheKey {
rune : r ,
xoffset : offset . X ,
}
if e , ok := glyphImageCache [ face ] [ key ] ; ok {
2021-01-05 04:21:41 +01:00
e . atime = now ( )
return e . image
}
2017-07-15 20:25:57 +02:00
2023-05-25 18:44:13 +02:00
b , _ , _ := face . GlyphBounds ( r )
2021-01-05 04:21:41 +01:00
w , h := ( b . Max . X - b . Min . X ) . Ceil ( ) , ( b . Max . Y - b . Min . Y ) . Ceil ( )
if w == 0 || h == 0 {
2022-11-23 10:13:54 +01:00
glyphImageCache [ face ] [ key ] = & glyphImageCacheEntry {
2021-01-05 05:12:46 +01:00
image : nil ,
atime : now ( ) ,
}
2021-01-05 04:21:41 +01:00
return nil
2017-07-15 20:25:57 +02:00
}
2021-01-05 04:21:41 +01:00
if b . Min . X & ( ( 1 << 6 ) - 1 ) != 0 {
w ++
}
if b . Min . Y & ( ( 1 << 6 ) - 1 ) != 0 {
h ++
}
rgba := image . NewRGBA ( image . Rect ( 0 , 0 , w , h ) )
2018-04-23 11:43:29 +02:00
2021-01-05 04:21:41 +01:00
d := font . Drawer {
Dst : rgba ,
Src : image . White ,
Face : face ,
}
2022-11-23 14:16:41 +01:00
2021-01-05 04:21:41 +01:00
x , y := - b . Min . X , - b . Min . Y
2022-11-25 07:03:32 +01:00
x += offset . X
y += offset . Y
2021-01-05 04:21:41 +01:00
d . Dot = fixed . Point26_6 { X : x , Y : y }
d . DrawString ( string ( r ) )
img := ebiten . NewImageFromImage ( rgba )
2022-11-23 10:13:54 +01:00
glyphImageCache [ face ] [ key ] = & glyphImageCacheEntry {
2022-11-23 14:16:41 +01:00
image : img ,
atime : now ( ) ,
2018-02-12 14:02:05 +01:00
}
2021-01-05 04:21:41 +01:00
return img
2018-02-12 14:02:05 +01:00
}
2017-07-15 20:25:57 +02:00
var textM sync . Mutex
2017-07-17 09:17:01 +02:00
// Draw draws a given text on a given destination image dst.
2017-07-15 20:25:57 +02:00
//
// face is the font for text rendering.
2023-06-01 17:46:07 +02:00
// (x, y) represents the origin position in this figure:
2023-05-30 10:50:08 +02:00
// https://developer.apple.com/library/archive/documentation/TextFonts/Conceptual/CocoaTextArchitecture/Art/glyphterms_2x.png.
2022-08-21 13:02:09 +02:00
// Be careful that this doesn't represent upper-left corner position.
2020-07-29 17:02:32 +02:00
//
2017-07-15 20:25:57 +02:00
// clr is the color for text rendering.
//
2020-02-12 15:15:57 +01:00
// The '\n' newline character puts the following text on the next line.
// Line height is based on Metrics().Height of the font.
//
2017-07-16 19:15:00 +02:00
// Glyphs used for rendering are cached in least-recently-used way.
2021-04-26 19:28:52 +02:00
// Then old glyphs might be evicted from the cache.
2021-04-26 19:36:33 +02:00
// As the cache capacity has limit, it is not guaranteed that all the glyphs for runes given at Draw are cached.
2021-04-26 18:58:28 +02:00
// The cache is shared with CacheGlyphs.
//
2021-04-26 19:28:52 +02:00
// It is OK to call Draw with a same text and a same face at every frame in terms of performance.
//
2021-07-27 10:01:36 +02:00
// Draw/DrawWithOptions and CacheGlyphs are implemented like this:
2021-04-26 18:58:28 +02:00
//
2022-08-07 20:50:25 +02:00
// Draw = Create glyphs by `(*ebiten.Image).WritePixels` and put them into the cache if necessary
2022-08-03 13:48:02 +02:00
// + Draw them onto the destination by `(*ebiten.Image).DrawImage`
2022-08-07 20:50:25 +02:00
// CacheGlyphs = Create glyphs by `(*ebiten.Image).WritePixels` and put them into the cache if necessary
2017-07-16 19:13:30 +02:00
//
2018-03-15 15:00:23 +01:00
// Be careful that the passed font face is held by this package and is never released.
// This is a known issue (#498).
//
2018-07-14 18:04:46 +02:00
// Draw is concurrent-safe.
2017-07-19 19:20:15 +02:00
func Draw ( dst * ebiten . Image , text string , face font . Face , x , y int , clr color . Color ) {
2021-07-27 10:01:36 +02:00
op := & ebiten . DrawImageOptions { }
op . GeoM . Translate ( float64 ( x ) , float64 ( y ) )
2022-11-01 18:43:42 +01:00
op . ColorScale . ScaleWithColor ( clr )
2021-07-27 10:01:36 +02:00
DrawWithOptions ( dst , text , face , op )
}
// DrawWithOptions draws a given text on a given destination image dst.
//
// face is the font for text rendering.
2023-11-11 16:20:47 +01:00
// options is the options to draw glyph images.
2023-06-01 17:46:07 +02:00
// The origin point is the origin position in this figure:
2023-05-30 10:50:08 +02:00
// https://developer.apple.com/library/archive/documentation/TextFonts/Conceptual/CocoaTextArchitecture/Art/glyphterms_2x.png.
2022-08-21 13:02:09 +02:00
// Be careful that the origin point is not upper-left corner position of dst.
2023-11-11 16:20:47 +01:00
// The default glyph color is white. options' ColorScale adjusts the color.
2021-07-27 10:01:36 +02:00
//
// The '\n' newline character puts the following text on the next line.
// Line height is based on Metrics().Height of the font.
//
// Glyphs used for rendering are cached in least-recently-used way.
// Then old glyphs might be evicted from the cache.
// As the cache capacity has limit, it is not guaranteed that all the glyphs for runes given at DrawWithOptions are cached.
// The cache is shared with CacheGlyphs.
//
2021-07-27 10:38:34 +02:00
// It is OK to call DrawWithOptions with a same text and a same face at every frame in terms of performance.
2021-07-27 10:01:36 +02:00
//
// Draw/DrawWithOptions and CacheGlyphs are implemented like this:
//
2022-08-07 20:50:25 +02:00
// Draw = Create glyphs by `(*ebiten.Image).WritePixels` and put them into the cache if necessary
2022-08-03 13:48:02 +02:00
// + Draw them onto the destination by `(*ebiten.Image).DrawImage`
2022-08-07 20:50:25 +02:00
// CacheGlyphs = Create glyphs by `(*ebiten.Image).WritePixels` and put them into the cache if necessary
2021-07-27 10:01:36 +02:00
//
// Be careful that the passed font face is held by this package and is never released.
// This is a known issue (#498).
//
// DrawWithOptions is concurrent-safe.
func DrawWithOptions ( dst * ebiten . Image , text string , face font . Face , options * ebiten . DrawImageOptions ) {
textM . Lock ( )
defer textM . Unlock ( )
2021-05-30 08:17:11 +02:00
2023-05-25 18:44:13 +02:00
fc := faceWithCacheFromFace ( face )
2021-07-27 10:01:36 +02:00
var dx , dy fixed . Int26_6
2018-03-03 13:53:09 +01:00
prevR := rune ( - 1 )
2017-07-15 20:25:57 +02:00
2023-05-25 18:44:13 +02:00
faceHeight := fc . Metrics ( ) . Height
2020-05-13 11:03:39 +02:00
2021-01-05 04:21:41 +01:00
for _ , r := range text {
2018-03-03 13:53:09 +01:00
if prevR >= 0 {
2023-05-25 18:44:13 +02:00
dx += fc . Kern ( prevR , r )
2017-07-15 20:25:57 +02:00
}
2020-02-12 15:15:57 +01:00
if r == '\n' {
2021-07-27 10:01:36 +02:00
dx = 0
dy += faceHeight
2020-02-12 15:15:57 +01:00
prevR = rune ( - 1 )
continue
}
2022-11-25 07:03:32 +01:00
// Adjust the position to the integers.
// The current glyph images assume that they are rendered on integer positions so far.
2023-05-25 18:44:13 +02:00
b , a , _ := fc . GlyphBounds ( r )
2022-11-25 07:03:32 +01:00
offset := fixed . Point26_6 {
2022-11-23 10:13:54 +01:00
X : ( adjustOffsetGranularity ( dx ) + b . Min . X ) & ( ( 1 << 6 ) - 1 ) ,
2022-11-25 07:03:32 +01:00
Y : b . Min . Y & ( ( 1 << 6 ) - 1 ) ,
}
2023-05-25 18:44:13 +02:00
img := getGlyphImage ( fc , r , offset )
2023-01-12 19:28:41 +01:00
drawGlyph ( dst , img , fixed . Point26_6 {
2022-11-25 07:03:32 +01:00
X : dx + b . Min . X - offset . X ,
Y : dy + b . Min . Y - offset . Y ,
} , options )
2023-05-25 18:44:13 +02:00
dx += a
2018-03-03 13:53:09 +01:00
prevR = r
2017-07-15 20:25:57 +02:00
}
2020-11-03 07:17:04 +01:00
2020-11-03 09:12:49 +01:00
// cacheSoftLimit indicates the soft limit of the number of glyphs in the cache.
// If the number of glyphs exceeds this soft limits, old glyphs are removed.
2023-01-27 16:14:37 +01:00
// Even after cleaning up the cache, the number of glyphs might still exceed the soft limit, but
2020-11-03 09:12:49 +01:00
// this is fine.
const cacheSoftLimit = 512
2020-11-03 08:36:10 +01:00
// Clean up the cache.
2023-05-25 18:44:13 +02:00
if len ( glyphImageCache [ fc ] ) > cacheSoftLimit {
for r , e := range glyphImageCache [ fc ] {
2020-11-03 09:12:49 +01:00
// 60 is an arbitrary number.
if e . atime < now ( ) - 60 {
2023-05-25 18:44:13 +02:00
delete ( glyphImageCache [ fc ] , r )
2020-11-03 07:17:04 +01:00
}
}
}
2020-05-13 11:03:39 +02:00
}
2020-07-29 17:19:08 +02:00
// BoundString returns the measured size of a given string using a given font.
2020-05-13 11:03:39 +02:00
// This method will return the exact size in pixels that a string drawn by Draw will be.
2023-06-01 17:46:07 +02:00
// The bound's origin point indicates the origin position in this figure:
2023-05-30 10:50:08 +02:00
// https://developer.apple.com/library/archive/documentation/TextFonts/Conceptual/CocoaTextArchitecture/Art/glyphterms_2x.png.
2020-07-29 17:02:32 +02:00
//
2022-04-03 19:55:09 +02:00
// BoundString behaves almost exactly like golang.org/x/image/font's BoundString,
// but newline characters '\n' in the input string move the text position to the following line.
2020-05-13 11:03:39 +02:00
//
// face is the font for text rendering.
2020-07-29 17:54:57 +02:00
// text is the string that's being measured.
2020-05-13 11:03:39 +02:00
//
// Be careful that the passed font face is held by this package and is never released.
// This is a known issue (#498).
//
2020-07-29 17:19:08 +02:00
// BoundString is concurrent-safe.
2023-06-10 12:21:01 +02:00
//
// Deprecated: as of v2.6. Use golang.org/x/image/font.BoundString instead, or use a face's Metrics (Ascent and Descent) and font.MeasureString instead.
2020-07-29 17:19:08 +02:00
func BoundString ( face font . Face , text string ) image . Rectangle {
2020-05-13 11:03:39 +02:00
textM . Lock ( )
defer textM . Unlock ( )
2023-05-25 18:44:13 +02:00
fc := faceWithCacheFromFace ( face )
m := fc . Metrics ( )
2020-05-13 11:03:39 +02:00
faceHeight := m . Height
fx , fy := fixed . I ( 0 ) , fixed . I ( 0 )
prevR := rune ( - 1 )
2020-07-29 17:19:08 +02:00
var bounds fixed . Rectangle26_6
2021-01-05 04:21:41 +01:00
for _ , r := range text {
2020-05-13 11:03:39 +02:00
if prevR >= 0 {
2023-05-25 18:44:13 +02:00
fx += fc . Kern ( prevR , r )
2020-05-13 11:03:39 +02:00
}
if r == '\n' {
fx = fixed . I ( 0 )
fy += faceHeight
prevR = rune ( - 1 )
continue
}
2023-05-25 18:44:13 +02:00
b , a , _ := fc . GlyphBounds ( r )
2020-07-29 17:19:08 +02:00
b . Min . X += fx
b . Max . X += fx
b . Min . Y += fy
b . Max . Y += fy
bounds = bounds . Union ( b )
2020-05-13 11:03:39 +02:00
2023-05-25 18:44:13 +02:00
fx += a
2020-05-13 11:03:39 +02:00
prevR = r
}
2020-07-29 17:19:08 +02:00
return image . Rect (
2022-08-12 17:28:30 +02:00
bounds . Min . X . Floor ( ) ,
bounds . Min . Y . Floor ( ) ,
bounds . Max . X . Ceil ( ) ,
bounds . Max . Y . Ceil ( ) ,
2020-07-29 17:19:08 +02:00
)
2017-07-15 20:25:57 +02:00
}
2020-11-03 09:12:49 +01:00
// CacheGlyphs precaches the glyphs for the given text and the given font face into the cache.
2021-04-26 19:28:52 +02:00
//
// Glyphs used for rendering are cached in least-recently-used way.
// Then old glyphs might be evicted from the cache.
2021-04-26 19:36:33 +02:00
// As the cache capacity has limit, it is not guaranteed that all the glyphs for runes given at CacheGlyphs are cached.
2021-04-26 18:58:28 +02:00
// The cache is shared with Draw.
2020-11-03 09:12:49 +01:00
//
2021-07-27 10:01:36 +02:00
// Draw/DrawWithOptions and CacheGlyphs are implemented like this:
2021-04-26 19:28:52 +02:00
//
2022-08-07 20:50:25 +02:00
// Draw = Create glyphs by `(*ebiten.Image).WritePixels` and put them into the cache if necessary
2022-08-03 13:48:02 +02:00
// + Draw them onto the destination by `(*ebiten.Image).DrawImage`
2022-08-07 20:50:25 +02:00
// CacheGlyphs = Create glyphs by `(*ebiten.Image).WritePixels` and put them into the cache if necessary
2021-04-26 19:28:52 +02:00
//
2020-11-03 09:12:49 +01:00
// Draw automatically creates and caches necessary glyphs, so usually you don't have to call CacheGlyphs
// explicitly. However, for example, when you call Draw for each rune of one big text, Draw tries to create the glyph
2020-11-03 11:54:51 +01:00
// cache and render it for each rune. This is very inefficient because creating a glyph image and rendering it are
2022-08-07 20:50:25 +02:00
// different operations (`(*ebiten.Image).WritePixels` and `(*ebiten.Image).DrawImage`) and can never be merged as
2021-04-26 19:20:10 +02:00
// one draw call. CacheGlyphs creates necessary glyphs without rendering them so that these operations are likely
// merged into one draw call regardless of the size of the text.
2021-04-26 18:58:28 +02:00
//
2020-11-03 12:35:55 +01:00
// If a rune's glyph is already cached, CacheGlyphs does nothing for the rune.
2023-01-07 15:22:55 +01:00
//
2023-01-27 16:14:37 +01:00
// One rune can have multiple variations of glyphs due to sub-pixels in X direction.
2023-01-07 15:22:55 +01:00
// CacheGlyphs creates all such variations for one rune, while Draw creates only necessary glyphs.
2020-11-03 09:12:49 +01:00
func CacheGlyphs ( face font . Face , text string ) {
textM . Lock ( )
defer textM . Unlock ( )
2023-05-25 18:44:13 +02:00
fc := faceWithCacheFromFace ( face )
2022-11-23 10:13:54 +01:00
var dx fixed . Int26_6
prevR := rune ( - 1 )
2021-01-05 04:21:41 +01:00
for _ , r := range text {
2022-11-23 10:13:54 +01:00
if prevR >= 0 {
2023-05-25 18:44:13 +02:00
dx += fc . Kern ( prevR , r )
2022-11-23 10:13:54 +01:00
}
if r == '\n' {
dx = 0
continue
}
2023-05-25 18:44:13 +02:00
b , a , _ : = fc . GlyphBounds ( r )
2023-01-07 15:22:55 +01:00
// Cache all 4 variations for one rune (#2528).
for i := 0 ; i < 4 ; i ++ {
offset := fixed . Point26_6 {
X : ( fixed . Int26_6 ( i * ( 1 << 4 ) ) + b . Min . X ) & ( ( 1 << 6 ) - 1 ) ,
Y : b . Min . Y & ( ( 1 << 6 ) - 1 ) ,
}
2023-05-25 18:44:13 +02:00
getGlyphImage ( fc , r , offset )
2022-11-25 07:03:32 +01:00
}
2023-01-07 15:22:55 +01:00
2023-05-25 18:44:13 +02:00
dx += a
2022-11-23 10:13:54 +01:00
prevR = r
2021-01-05 04:21:41 +01:00
}
2020-11-03 09:12:49 +01:00
}
2021-09-12 15:53:41 +02:00
// FaceWithLineHeight returns a font.Face with the given lineHeight in pixels.
// The returned face will otherwise have the same glyphs and metrics as face.
func FaceWithLineHeight ( face font . Face , lineHeight float64 ) font . Face {
return faceWithLineHeight {
face : face ,
lineHeight : fixed . Int26_6 ( lineHeight * ( 1 << 6 ) ) ,
}
}
type faceWithLineHeight struct {
face font . Face
lineHeight fixed . Int26_6
}
func ( f faceWithLineHeight ) Close ( ) error {
return f . face . Close ( )
}
2023-11-11 12:22:30 +01:00
func ( f faceWithLineHeight ) Glyph ( origin fixed . Point26_6 , r rune ) ( dr image . Rectangle , mask image . Image , maskp image . Point , advance fixed . Int26_6 , ok bool ) {
return f . face . Glyph ( origin , r )
2021-09-12 15:53:41 +02:00
}
func ( f faceWithLineHeight ) GlyphBounds ( r rune ) ( bounds fixed . Rectangle26_6 , advance fixed . Int26_6 , ok bool ) {
return f . face . GlyphBounds ( r )
}
func ( f faceWithLineHeight ) GlyphAdvance ( r rune ) ( advance fixed . Int26_6 , ok bool ) {
return f . face . GlyphAdvance ( r )
}
func ( f faceWithLineHeight ) Kern ( r0 , r1 rune ) fixed . Int26_6 {
return f . face . Kern ( r0 , r1 )
}
func ( f faceWithLineHeight ) Metrics ( ) font . Metrics {
m := f . face . Metrics ( )
m . Height = f . lineHeight
return m
}
2021-11-11 16:54:15 +01:00
2023-01-27 16:14:37 +01:00
// Glyph is information to render one glyph.
2021-11-11 16:54:15 +01:00
type Glyph struct {
// Rune is a character for this glyph.
Rune rune
// Image is an image for this glyph.
// Image is a grayscale image i.e. RGBA values are the same.
2022-04-02 12:00:11 +02:00
// Image should be used as a render source and should not be modified.
2021-11-11 16:54:15 +01:00
Image * ebiten . Image
// X is the X position to render this glyph.
// The position is determined in a sequence of characters given at AppendGlyphs.
2023-11-11 12:22:30 +01:00
// The position's origin is the first character's origin position.
2021-11-11 16:54:15 +01:00
X float64
// Y is the Y position to render this glyph.
// The position is determined in a sequence of characters given at AppendGlyphs.
2023-11-11 12:22:30 +01:00
// The position's origin is the first character's origin position.
2021-11-11 16:54:15 +01:00
Y float64
}
// AppendGlyphs appends the glyph information to glyphs.
// You can render each glyphs as you like. See examples/text for an example of AppendGlyphs.
func AppendGlyphs ( glyphs [ ] Glyph , face font . Face , text string ) [ ] Glyph {
textM . Lock ( )
defer textM . Unlock ( )
2023-05-25 18:44:13 +02:00
fc := faceWithCacheFromFace ( face )
2021-11-11 16:54:15 +01:00
var pos fixed . Point26_6
prevR := rune ( - 1 )
2023-05-25 18:44:13 +02:00
faceHeight := fc . Metrics ( ) . Height
2021-11-11 16:54:15 +01:00
for _ , r := range text {
if prevR >= 0 {
2023-05-25 18:44:13 +02:00
pos . X += fc . Kern ( prevR , r )
2021-11-11 16:54:15 +01:00
}
if r == '\n' {
pos . X = 0
pos . Y += faceHeight
prevR = rune ( - 1 )
continue
}
2023-05-25 18:44:13 +02:00
b , a , _ := fc . GlyphBounds ( r )
2022-11-25 07:03:32 +01:00
offset := fixed . Point26_6 {
2022-11-23 10:13:54 +01:00
X : ( adjustOffsetGranularity ( pos . X ) + b . Min . X ) & ( ( 1 << 6 ) - 1 ) ,
2022-11-25 07:03:32 +01:00
Y : b . Min . Y & ( ( 1 << 6 ) - 1 ) ,
}
2023-05-25 18:44:13 +02:00
if img := getGlyphImage ( fc , r , offset ) ; img != nil {
2022-11-23 14:16:41 +01:00
// Adjust the position to the integers.
// The current glyph images assume that they are rendered on integer positions so far.
2021-11-11 16:54:15 +01:00
glyphs = append ( glyphs , Glyph {
Rune : r ,
Image : img ,
2022-11-25 07:03:32 +01:00
X : fixed26_6ToFloat64 ( pos . X + b . Min . X - offset . X ) ,
Y : fixed26_6ToFloat64 ( pos . Y + b . Min . Y - offset . Y ) ,
2021-11-11 16:54:15 +01:00
} )
}
2023-05-25 18:44:13 +02:00
pos . X += a
2021-11-11 16:54:15 +01:00
prevR = r
}
return glyphs
}