114 lines
2.7 KiB
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)
|
|
}
|
|
}
|