U: TGIFAnimate To handle simple GIF animations

This commit is contained in:
杨红岩 2024-04-22 13:19:28 +08:00
parent 66ce6e5889
commit 3cfef1130b
2 changed files with 176 additions and 50 deletions

View File

@ -14,15 +14,25 @@ package gifanim
import (
"bytes"
"github.com/energye/energy/v2/pkgs/ext"
"github.com/energye/golcl/lcl"
"github.com/energye/golcl/lcl/types"
"image/gif"
"image/png"
"os"
)
const (
DisposalNone = iota + 1
DisposalBackground
DisposalPrevious
)
type TGIFAnimate struct {
*lcl.TImage
*ext.TImage
buffImg *lcl.TBitmap
task *lcl.TTimer
delay uint32
filePath string
cache bool
gif *gif.GIF
@ -37,53 +47,132 @@ type TGIFAnimate struct {
func NewGIFAnimate(owner lcl.IComponent) *TGIFAnimate {
m := new(TGIFAnimate)
m.TImage = lcl.NewImage(owner)
m.delay = 100
m.buffImg = lcl.NewBitmap()
m.TImage = ext.NewImage(owner)
//m.SetOnPaint(m.onPaint)
m.task = lcl.NewTimer(owner)
m.task.SetEnabled(false)
m.task.SetInterval(100)
m.task.SetInterval(m.delay)
m.task.SetOnTimer(m.onTimer)
return m
}
func (m *TGIFAnimate) Free() {
m.Stop()
m.reset()
m.TImage.Free()
m.buffImg.Free()
m.task.Free()
}
func (m *TGIFAnimate) onTimer(sender lcl.IObject) {
if m.count == 0 {
return
}
frame := m.frames[m.index]
m.doFrameChanged(frame)
m.scan()
if m.index >= m.count-1 {
m.index = 0
} else {
m.index++
}
nextDelay := m.nextDelay()
if nextDelay != frame.delay {
m.task.SetInterval(nextDelay)
if nextDelay != m.delay {
m.delay = nextDelay
m.task.SetEnabled(false)
m.task.SetInterval(m.delay)
m.task.SetEnabled(true)
}
m.scan(frame)
//m.Repaint() 使用OnPaint有些问题, 这里直接绘制
}
func (m *TGIFAnimate) nextDelay() uint32 {
return m.frames[m.index].delay
func (m *TGIFAnimate) onPaint(sender lcl.IObject) {
m.scan()
}
func (m *TGIFAnimate) first() {
func (m *TGIFAnimate) initialed() {
m.w = int32(m.gif.Config.Width)
m.h = int32(m.gif.Config.Height)
m.SetWidth(m.w)
m.SetHeight(m.h)
m.Canvas().FillRect(types.Rect(0, 0, m.w, m.h))
m.scan(m.frames[0])
m.Picture().LoadFromBytes(m.frames[0].data)
m.buffImg.SetSize(m.w, m.h)
m.buffImg.SetPixelFormat(types.Pf32bit)
m.buffImg.SetHandleType(types.BmDIB)
// 填充整个画布
//m.Canvas().FillRect(types.Rect(0, 0, m.w, m.h))
//m.Repaint()
}
func (m *TGIFAnimate) scan(frame *Frame) {
func (m *TGIFAnimate) scan() {
frame := m.currentFrame()
m.doFrameChanged(frame)
frame.scan()
m.Canvas().Draw(frame.x, frame.y, frame.image)
canvas := m.Canvas()
m.buffImg.Canvas().Draw(frame.x, frame.y, frame.image)
//canvas.Draw(frame.x, frame.y, frame.image)
switch frame.method {
case DisposalNone: // 不处理,图形留在原处
case DisposalBackground: // 删除当前帧,恢复为上一个帧的内容. 显示图形的区域必须要恢复成背景颜色
// TOdO 先不处理当前只处理简单的GIF
//pFrame := m.priorFrame(frame.index)
//pFrame.scan()
////canvas.Draw(pFrame.x, pFrame.y, pFrame.image)
//m.buffImg.Canvas().Draw(pFrame.x, pFrame.y, pFrame.image)
//m.task.SetEnabled(false) // debug
case DisposalPrevious: // 删除当前帧,恢复为 GIF 开始时的状态
canvas.FillRect(Rect(0, 0, m.w, m.h))
}
canvas.Draw(0, 0, m.buffImg)
//canvas.Draw(frame.x, frame.y, frame.image)
if !m.cache {
frame.reset()
}
}
func (m *TGIFAnimate) load() {
m.reset()
m.count = len(m.gif.Image)
m.frames = make([]*Frame, m.count)
for i, frame := range m.gif.Image {
bounds := frame.Bounds()
var pngBuf bytes.Buffer
err := png.Encode(&pngBuf, frame)
if err != nil {
panic(err)
}
m.frames[i] = &Frame{
index: i,
method: m.gif.Disposal[i],
x: int32(bounds.Min.X),
y: int32(bounds.Min.Y),
w: int32(bounds.Dx()),
h: int32(bounds.Dy()),
delay: uint32(m.gif.Delay[i] * 10),
data: pngBuf.Bytes(),
}
pngBuf.Reset()
}
m.initialed()
m.gif = nil
}
//
//func background(width, height int, data []byte) lcl.IBitmap {
// bmp := lcl.NewBitmap()
// bmp.SetSize(int32(width), int32(height))
// bmp.SetPixelFormat(types.Pf32bit)
// bmp.SetHandleType(types.BmDIB)
// //bmp.SetTransparent(true)
//
// img := lcl.NewPngImage()
// //img.SetTransparent(true)
// img.LoadFromBytes(data)
// bmp.Assign(img)
//
// return bmp
//}
func (m *TGIFAnimate) reset() {
for _, frame := range m.frames {
frame.free()
@ -94,30 +183,37 @@ func (m *TGIFAnimate) reset() {
m.count = 0
}
func (m *TGIFAnimate) load() {
m.reset()
m.count = len(m.gif.Image)
m.frames = make([]*Frame, m.count)
for i, img := range m.gif.Image {
bounds := img.Bounds()
var buf = new(bytes.Buffer)
err := gif.Encode(buf, img, nil)
if err != nil {
panic(err)
}
m.frames[i] = &Frame{
index: i,
x: int32(bounds.Min.X),
y: int32(bounds.Min.Y),
w: int32(bounds.Dx()),
h: int32(bounds.Dy()),
delay: uint32(m.gif.Delay[i] * 10),
data: buf.Bytes(),
}
buf.Reset()
func (m *TGIFAnimate) nextDelay() uint32 {
return m.frames[m.index].delay
}
func (m *TGIFAnimate) currentFrame() *Frame {
return m.frames[m.index]
}
func (m *TGIFAnimate) PrevFrame() {
m.index--
if m.index < 0 {
m.index = m.count - 1
}
m.first()
m.gif = nil
m.scan()
}
func (m *TGIFAnimate) NextFrame() {
if m.index >= m.count-1 {
m.index = 0
} else {
m.index++
}
m.scan()
}
func (m *TGIFAnimate) priorFrame(index int) *Frame {
index--
if index < 0 {
index = m.count - 1
}
return m.frames[index]
}
func (m *TGIFAnimate) SetAnimate(v bool) {
@ -129,6 +225,10 @@ func (m *TGIFAnimate) SetAnimate(v bool) {
}
}
func (m *TGIFAnimate) Animate() bool {
return m.task.Enabled()
}
func (m *TGIFAnimate) CurrentFrameIndex() int {
return m.index
}
@ -159,8 +259,8 @@ func (m *TGIFAnimate) doStop() {
}
func (m *TGIFAnimate) doStart() {
if m.onStop != nil {
m.onStop()
if m.onStart != nil {
m.onStart()
}
}
@ -211,3 +311,7 @@ func (m *TGIFAnimate) LoadFromBytes(data []byte) {
m.gif = g
m.load()
}
func Rect(left, top, right, bottom int32) types.TRect {
return types.TRect{Left: left, Top: top, Right: right, Bottom: bottom}
}

View File

@ -10,15 +10,20 @@
package gifanim
import "github.com/energye/golcl/lcl"
import (
"github.com/energye/golcl/lcl"
)
type Frame struct {
index int
x, y int32
w, h int32
delay uint32
data []byte
image *lcl.TGIFImage
index int
method byte
x, y int32
w, h int32
delay uint32
data []byte
//background lcl.IBitmap
//image *lcl.TGIFImage
image *lcl.TPngImage
}
func (m *Frame) Index() int {
@ -30,26 +35,43 @@ func (m *Frame) Point() (x, y int32) {
return
}
func (m *Frame) SetPoint(x, y int32) {
m.x, m.y = x, y
}
func (m *Frame) Rect() (width, height int32) {
width, height = m.w, m.h
return
}
func (m *Frame) SetRect(width, height int32) {
m.w, m.h = width, height
}
func (m *Frame) Delay() uint32 {
return m.delay
}
func (m *Frame) SetDelay(delay uint32) {
m.delay = delay
}
func (m *Frame) Data() []byte {
return m.data
}
func (m *Frame) Image() *lcl.TGIFImage {
return m.image
func (m *Frame) SetData(data []byte) {
m.data = data
}
//func (m *Frame) Image() *lcl.TGIFImage {
// return m.image
//}
func (m *Frame) scan() {
if m.image == nil {
m.image = lcl.NewGIFImage()
//m.image = lcl.NewGIFImage()
m.image = lcl.NewPngImage()
m.image.SetSize(m.w, m.h)
m.image.LoadFromBytes(m.data)
}