161 lines
3.4 KiB
Go
161 lines
3.4 KiB
Go
package canvas
|
|
|
|
import (
|
|
"image"
|
|
"image/color"
|
|
|
|
"golang.org/x/image/font"
|
|
"golang.org/x/image/math/fixed"
|
|
)
|
|
|
|
// SetFont 设置字体
|
|
func (c *Context) SetFont(face font.Face) {
|
|
c.state.fontFace = face
|
|
}
|
|
|
|
// SetTextAlign 设置文本对齐
|
|
func (c *Context) SetTextAlign(align string) {
|
|
c.state.textAlign = align
|
|
}
|
|
|
|
// SetTextBaseline 设置文本基线
|
|
func (c *Context) SetTextBaseline(baseline string) {
|
|
c.state.textBaseline = baseline
|
|
}
|
|
|
|
// FillText 绘制填充文本
|
|
func (c *Context) FillText(text string, x, y float64) {
|
|
if c.state.fontFace == nil {
|
|
return
|
|
}
|
|
|
|
// 应用阴影
|
|
if c.state.shadowBlur > 0 || c.state.shadowOffsetX != 0 || c.state.shadowOffsetY != 0 {
|
|
c.drawTextShadow(text, x, y, true)
|
|
}
|
|
|
|
c.drawText(text, x, y, true)
|
|
}
|
|
|
|
// StrokeText 绘制描边文本
|
|
func (c *Context) StrokeText(text string, x, y float64) {
|
|
if c.state.fontFace == nil {
|
|
return
|
|
}
|
|
|
|
// 应用阴影
|
|
if c.state.shadowBlur > 0 || c.state.shadowOffsetX != 0 || c.state.shadowOffsetY != 0 {
|
|
c.drawTextShadow(text, x, y, false)
|
|
}
|
|
|
|
c.drawText(text, x, y, false)
|
|
}
|
|
|
|
// MeasureText 测量文本宽度
|
|
func (c *Context) MeasureText(text string) float64 {
|
|
if c.state.fontFace == nil {
|
|
return 0
|
|
}
|
|
|
|
width := 0
|
|
for _, r := range text {
|
|
aw, _ := c.state.fontFace.GlyphAdvance(r)
|
|
width += aw.Round()
|
|
}
|
|
return float64(width)
|
|
}
|
|
|
|
// 绘制文本
|
|
func (c *Context) drawText(text string, x, y float64, fill bool) {
|
|
pt := c.transformPoint(x, y)
|
|
|
|
// 计算文本宽度用于对齐
|
|
textWidth := c.MeasureText(text)
|
|
|
|
// 应用对齐
|
|
switch c.state.textAlign {
|
|
case "center":
|
|
pt.X -= int(textWidth / 2)
|
|
case "end", "right":
|
|
pt.X -= int(textWidth)
|
|
}
|
|
|
|
// 应用基线
|
|
metrics := c.state.fontFace.Metrics()
|
|
switch c.state.textBaseline {
|
|
case "top":
|
|
pt.Y += metrics.Ascent.Round()
|
|
case "middle":
|
|
pt.Y += (metrics.Ascent + metrics.Descent).Round() / 2
|
|
case "bottom":
|
|
pt.Y += metrics.Descent.Round()
|
|
}
|
|
|
|
// 创建文本绘制器
|
|
drawer := font.Drawer{
|
|
Dst: c.img,
|
|
Face: c.state.fontFace,
|
|
Dot: fixed.P(pt.X, pt.Y),
|
|
}
|
|
|
|
// 设置颜色
|
|
if fill {
|
|
switch style := c.state.fillStyle.(type) {
|
|
case color.Color:
|
|
drawer.Src = image.NewUniform(c.applyAlpha(style))
|
|
case Gradient:
|
|
drawer.Src = &gradientImage{grad: style, alpha: c.state.globalAlpha}
|
|
}
|
|
} else {
|
|
// 描边文本 - 简单实现,实际应使用路径
|
|
switch style := c.state.strokeStyle.(type) {
|
|
case color.Color:
|
|
drawer.Src = image.NewUniform(c.applyAlpha(style))
|
|
case Gradient:
|
|
drawer.Src = &gradientImage{grad: style, alpha: c.state.globalAlpha}
|
|
}
|
|
}
|
|
|
|
// 绘制文本
|
|
if fill {
|
|
drawer.DrawString(text)
|
|
} else {
|
|
// 简单描边实现 - 实际应用中应使用更高级的路径方法
|
|
offset := 1
|
|
for dx := -offset; dx <= offset; dx++ {
|
|
for dy := -offset; dy <= offset; dy++ {
|
|
if dx != 0 || dy != 0 {
|
|
drawer.Dot = fixed.P(pt.X+dx, pt.Y+dy)
|
|
drawer.DrawString(text)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 绘制文本阴影
|
|
func (c *Context) drawTextShadow(text string, x, y float64, fill bool) {
|
|
// 保存当前状态
|
|
c.Save()
|
|
|
|
// 设置阴影样式
|
|
shadowStyle := c.state.shadowColor
|
|
c.SetFillColor(shadowStyle)
|
|
c.SetStrokeColor(shadowStyle)
|
|
c.SetGlobalAlpha(0.5 * c.state.globalAlpha) // 阴影透明度
|
|
|
|
// 应用阴影偏移
|
|
x += c.state.shadowOffsetX
|
|
y += c.state.shadowOffsetY
|
|
|
|
// 绘制阴影文本
|
|
if fill {
|
|
c.FillText(text, x, y)
|
|
} else {
|
|
c.StrokeText(text, x, y)
|
|
}
|
|
|
|
// 恢复状态
|
|
c.Restore()
|
|
}
|