80 lines
1.9 KiB
Go
80 lines
1.9 KiB
Go
// Package textr 文本光栅化:把字符串用位图字体渲染成 RGBA 图像(CPU 侧,UI还原.md §4)。
|
||
//
|
||
// 字形来自素材包 8×8 位图字体(internal/font);本包输出可直接上传为 GL 纹理的图像,
|
||
// 因此与渲染线程解耦(GL 只负责贴图)。
|
||
package textr
|
||
|
||
import (
|
||
"image"
|
||
"image/color"
|
||
"image/draw"
|
||
|
||
"mc/internal/font"
|
||
)
|
||
|
||
// Drawer 文本绘制器。
|
||
type Drawer struct {
|
||
atlas *font.Atlas
|
||
}
|
||
|
||
// New 创建绘制器。
|
||
func New(atlas *font.Atlas) *Drawer { return &Drawer{atlas: atlas} }
|
||
|
||
// Width 计算文本像素宽度(8px/字符)。
|
||
func (d *Drawer) Width(s string) int {
|
||
w := 0
|
||
for _, r := range s {
|
||
if g, ok := d.atlas.Glyphs[r]; ok {
|
||
w += g.Width
|
||
} else {
|
||
w += 8 // 缺失字形占位
|
||
}
|
||
}
|
||
return w
|
||
}
|
||
|
||
// Draw 渲染一行文本(白字透明底;shadow 为 true 时附加 1px 黑色阴影,UI还原.md §4)。
|
||
// 返回图像与阴影偏移(阴影在图像内左上角,调用方绘制时需偏移 1px)。
|
||
func (d *Drawer) Draw(s string, c color.RGBA, shadow bool) image.Image {
|
||
w := d.Width(s)
|
||
h := 8
|
||
if shadow {
|
||
w++
|
||
h++
|
||
}
|
||
img := image.NewRGBA(image.Rect(0, 0, w, h))
|
||
x := 0
|
||
for _, r := range s {
|
||
g, ok := d.atlas.Glyphs[r]
|
||
if !ok {
|
||
g = d.atlas.Missing()
|
||
}
|
||
d.blit(img, g, x, 0, c)
|
||
if shadow {
|
||
d.blit(img, g, x+1, 1, color.RGBA{0, 0, 0, 255})
|
||
}
|
||
x += g.Width
|
||
}
|
||
return img
|
||
}
|
||
|
||
// blit 把字形像素以指定颜色写入目标(字形位图按 alpha 通道着色)。
|
||
func (d *Drawer) blit(dst *image.RGBA, g font.Glyph, dx, dy int, c color.RGBA) {
|
||
if d.atlas.Image == nil {
|
||
return
|
||
}
|
||
src := d.atlas.Image
|
||
for y := 0; y < g.H; y++ {
|
||
for x := 0; x < g.W; x++ {
|
||
r, _, _, a := src.At(g.X+x, g.Y+y).RGBA()
|
||
if a == 0 {
|
||
continue
|
||
}
|
||
_ = r // 字形颜色以纹理亮度为准,目标色统一用 c
|
||
dst.SetRGBA(dx+x, dy+y, c)
|
||
}
|
||
}
|
||
}
|
||
|
||
var _ = draw.Draw // 保持 image/draw 引用(后续 9-patch 使用)
|