caption/line.go
Yonah 8e6affff41
caption: overridable default
This increases performance in the typical use case.
2025-07-13 01:38:09 +09:00

80 lines
2.0 KiB
Go

package caption
import (
"image"
"image/color"
"github.com/golang/freetype/truetype"
"golang.org/x/image/draw"
"golang.org/x/image/font"
"golang.org/x/image/math/fixed"
)
type Line struct {
// collection of all text segments
Data []Segment `json:"data"`
// applies to all segments; avoid direct assignment and use SetFont instead
Font []byte `json:"font"`
// applies to all segments; avoid direct assignment and use SetFont instead
Options *truetype.Options `json:"options"`
// contains reference to parsed font, populated once per instance
face font.Face
}
// SetFont clears line font cache and sets the new font.
func (l *Line) SetFont(v []byte, opts *truetype.Options) { l.face = nil; l.Font = v; l.Options = opts }
type Segment struct {
Value string `json:"value"`
Color color.Color `json:"color"`
}
// Render draws the current contents of Line on [draw.Image].
func (l *Line) Render(ctx *Context, frame draw.Image, x, y int) error {
face := l.face
if face == nil {
if len(l.Font) != 0 {
// parse & cache Line font
if err := l.cacheFont(l.Font); err != nil {
return err
}
return l.Render(ctx, frame, x, y)
}
// fall back to default font
if l.Options != nil { // global default: Options is not zero
// cache global default without clobbering Line font
if err := l.cacheFont(defaultFont); err != nil {
return err // unreachable
}
return l.Render(ctx, frame, x, y)
}
// fast path: Font and Options are zero
if err := ctx.parseFont(); err != nil {
return err
}
face = ctx.defaultFont
}
drawer := &font.Drawer{Dst: frame, Face: face, Dot: fixed.Point26_6{X: fixed.I(x), Y: fixed.I(y)}}
for _, seg := range l.Data {
drawer.Src = image.NewUniform(seg.Color)
drawer.DrawString(seg.Value)
}
return nil
}
// cacheFont parses truetype font data v and stores the result in Line.
// The cache is invalidated by SetFont.
func (l *Line) cacheFont(v []byte) error {
f, err := truetype.Parse(v)
if err == nil {
l.face = truetype.NewFace(f, l.Options)
}
return err
}