vision/animation.go

114 lines
2.7 KiB
Go

package vision
import (
"image"
"image/color/palette"
"image/draw"
"image/gif"
"apigo.cc/go/file"
"github.com/fogleman/gg"
)
// Animation 代表一个动画序列 (如 GIF)
type Animation struct {
Frames []*Canvas
Delays []int // 每帧延迟时间 (单位: 1/100 秒)
}
// NewAnimation 创建一个空动画
func NewAnimation() *Animation {
return &Animation{
Frames: make([]*Canvas, 0),
Delays: make([]int, 0),
}
}
// AddFrame 添加一帧到动画中
// delay: 延迟时间 (100 = 1秒)
func (a *Animation) AddFrame(c *Canvas, delay int) {
a.Frames = append(a.Frames, c.Clone())
a.Delays = append(a.Delays, delay)
}
// SaveGIF 将动画保存为 GIF 文件
func (a *Animation) SaveGIF(path string, loopCount int) error {
out := &gif.GIF{
LoopCount: loopCount,
}
for i, c := range a.Frames {
img := c.dc.Image()
bounds := img.Bounds()
// 1. 使用 Plan9 标准调色板
paletted := image.NewPaletted(bounds, palette.Plan9)
// 2. 将图像绘制到调色板图像中
draw.FloydSteinberg.Draw(paletted, bounds, img, image.Point{})
out.Image = append(out.Image, paletted)
out.Delay = append(out.Delay, a.Delays[i])
}
f, err := file.Create(path)
if err != nil {
return err
}
defer f.Close()
return gif.EncodeAll(f, out)
}
// LoadGIF 从文件加载 GIF 动画
func LoadGIF(path string) (*Animation, error) {
f, err := file.Open(path)
if err != nil {
return nil, err
}
defer f.Close()
g, err := gif.DecodeAll(f)
if err != nil {
return nil, err
}
anim := NewAnimation()
for i, img := range g.Image {
canvas := &Canvas{
dc: gg.NewContextForImage(img),
}
// 注意: GIF 帧可能是增量更新的,这里简化处理
anim.AddFrame(canvas, g.Delay[i])
}
return anim, nil
}
// Watermark 给动画所有帧添加图片水印
func (a *Animation) Watermark(mark *Canvas, pos Position, opacity float64, padding int, angle ...float64) {
for _, f := range a.Frames {
f.Watermark(mark, pos, opacity, padding, angle...)
}
}
// TileWatermark 给动画所有帧平铺图片水印
func (a *Animation) TileWatermark(mark *Canvas, opacity float64, spacing int, angle float64) {
for _, f := range a.Frames {
f.TileWatermark(mark, opacity, spacing, angle)
}
}
// TextWatermark 给动画所有帧添加文字水印
func (a *Animation) TextWatermark(text string, pos Position, style *DrawStyle, opacity float64, padding int, angle ...float64) {
for _, f := range a.Frames {
f.TextWatermark(text, pos, style, opacity, padding, angle...)
}
}
// TileTextWatermark 给动画所有帧平铺文字水印
func (a *Animation) TileTextWatermark(text string, style *DrawStyle, opacity float64, spacing int, angle float64) {
for _, f := range a.Frames {
f.TileTextWatermark(text, style, opacity, spacing, angle)
}
}