2022-03-25 06:00:01 +00:00
|
|
|
package nori
|
|
|
|
|
2022-03-25 07:33:23 +00:00
|
|
|
import (
|
|
|
|
"fmt"
|
2022-12-19 09:09:29 +00:00
|
|
|
"image"
|
|
|
|
"image/color"
|
|
|
|
"image/draw"
|
|
|
|
|
2022-03-25 07:33:23 +00:00
|
|
|
"git.tuxpa.in/a/zlog/log"
|
2022-03-26 06:52:59 +00:00
|
|
|
"github.com/disintegration/imaging"
|
2022-03-25 10:23:31 +00:00
|
|
|
"github.com/phrozen/blend"
|
2022-03-25 07:33:23 +00:00
|
|
|
"gitlab.com/gfxlabs/gfximg/apng"
|
|
|
|
)
|
|
|
|
|
2022-03-26 06:52:59 +00:00
|
|
|
func copyImage(img image.Image) image.Image {
|
|
|
|
copied := image.NewNRGBA(img.Bounds())
|
|
|
|
draw.Draw(copied, img.Bounds(), img, img.Bounds().Min, draw.Over)
|
|
|
|
return copied
|
2022-03-25 22:19:32 +00:00
|
|
|
}
|
|
|
|
|
2022-03-26 06:52:59 +00:00
|
|
|
type RenderFunc = func(img image.Image, delay int)
|
|
|
|
|
|
|
|
func RenderAnimation(n *Nori, num int, f RenderFunc) error {
|
2022-03-25 07:33:23 +00:00
|
|
|
g := n.Gawi
|
2022-03-25 18:32:40 +00:00
|
|
|
if len(n.Animations[num].Frames) == 0 {
|
2022-03-26 06:52:59 +00:00
|
|
|
return fmt.Errorf("no frames found for animation")
|
2022-03-25 18:32:40 +00:00
|
|
|
}
|
2022-03-26 06:52:59 +00:00
|
|
|
images := make([]image.Image, 0, len(n.Animations[num].Frames))
|
|
|
|
durations := make([]uint32, 0, len(n.Animations[num].Frames))
|
2022-03-25 18:32:40 +00:00
|
|
|
for _, frame := range n.Animations[num].Frames {
|
|
|
|
planes := frame.Planes
|
2022-03-25 10:23:31 +00:00
|
|
|
var canvasRect image.Rectangle
|
2022-03-25 19:24:52 +00:00
|
|
|
for i, plane := range planes {
|
2022-03-26 06:52:59 +00:00
|
|
|
if int(plane.BitmapId) >= len(g.Images) {
|
|
|
|
log.Printf("could not find bitmap %d, only have %d", plane.BitmapId, len(g.Images))
|
2022-03-25 10:23:31 +00:00
|
|
|
}
|
2022-03-26 06:52:59 +00:00
|
|
|
bitmap := g.Images[plane.BitmapId]
|
2022-12-19 09:09:29 +00:00
|
|
|
if len(bitmap.SubImages) == 0 {
|
|
|
|
return fmt.Errorf("no subimages in bitmap")
|
|
|
|
}
|
2022-03-25 10:23:31 +00:00
|
|
|
pt := image.Pt(int(plane.PlaneX), int(plane.PlaneY)) // where to put the point
|
2022-12-19 09:09:29 +00:00
|
|
|
rc := bitmap.SubImages[0].Img.Bounds().Add(pt) // translate rectangle to put in the global canvas
|
2022-03-25 19:24:52 +00:00
|
|
|
if i == 0 {
|
|
|
|
canvasRect = rc
|
|
|
|
} else {
|
|
|
|
canvasRect = canvasRect.Union(rc)
|
|
|
|
}
|
2022-03-25 10:23:31 +00:00
|
|
|
}
|
2022-03-26 06:52:59 +00:00
|
|
|
img := image.NewNRGBA(canvasRect)
|
2022-03-25 10:23:31 +00:00
|
|
|
for _, plane := range planes {
|
2022-03-26 06:52:59 +00:00
|
|
|
bitmap := g.Images[plane.BitmapId]
|
2022-12-19 09:09:29 +00:00
|
|
|
drawBitmap := bitmap.SubImages[0].Img
|
2022-03-25 10:23:31 +00:00
|
|
|
transparent := false
|
|
|
|
//flipx
|
|
|
|
if plane.RenderFlag&1 != 0 {
|
2022-12-19 09:09:29 +00:00
|
|
|
if bitmap.SubImages[0].Img == drawBitmap {
|
|
|
|
drawBitmap = copyImage(bitmap.SubImages[0].Img)
|
2022-03-26 06:52:59 +00:00
|
|
|
}
|
|
|
|
drawBitmap = imaging.FlipH(drawBitmap)
|
2022-03-25 10:23:31 +00:00
|
|
|
}
|
|
|
|
//flipy
|
|
|
|
if plane.RenderFlag&2 != 0 {
|
2022-12-19 09:09:29 +00:00
|
|
|
if bitmap.SubImages[0].Img == drawBitmap {
|
|
|
|
drawBitmap = copyImage(bitmap.SubImages[0].Img)
|
2022-03-26 06:52:59 +00:00
|
|
|
}
|
|
|
|
drawBitmap = imaging.FlipV(drawBitmap)
|
2022-03-25 07:33:23 +00:00
|
|
|
}
|
2022-03-25 10:23:31 +00:00
|
|
|
// is transparent
|
|
|
|
if plane.RenderFlag&0x20 != 0 {
|
|
|
|
transparent = true
|
2022-03-25 07:33:23 +00:00
|
|
|
}
|
2022-03-25 10:23:31 +00:00
|
|
|
_ = transparent
|
|
|
|
pt := image.Pt(int(plane.PlaneX), int(plane.PlaneY)) // where to put the point
|
2022-03-26 06:52:59 +00:00
|
|
|
rc := drawBitmap.Bounds().Add(pt)
|
|
|
|
src := drawBitmap
|
2022-03-25 10:23:31 +00:00
|
|
|
switch plane.Blend {
|
2022-03-26 06:52:59 +00:00
|
|
|
case BlendMode_InvertMul, BlendMode_InvertMul5:
|
|
|
|
img = imaging.Invert(img)
|
|
|
|
fallthrough
|
2022-03-25 10:23:31 +00:00
|
|
|
case BlendMode_Alpha:
|
|
|
|
case BlendMode_Mul, BlendMode_Mul7:
|
2022-03-26 06:52:59 +00:00
|
|
|
blend.BlendImage(img, drawBitmap, blend.Multiply)
|
2022-03-25 10:23:31 +00:00
|
|
|
case BlendMode_Add, BlendMode_Add8:
|
2022-03-26 06:52:59 +00:00
|
|
|
blend.BlendImage(img, drawBitmap, blend.Add)
|
2022-03-25 10:23:31 +00:00
|
|
|
case BlendMode_None:
|
|
|
|
default:
|
2022-03-26 06:52:59 +00:00
|
|
|
return fmt.Errorf("unknown blend mode: %d", plane.Blend)
|
|
|
|
}
|
|
|
|
|
|
|
|
mask := (image.Image)(nil)
|
|
|
|
if transparent {
|
|
|
|
mask = &image.Uniform{C: color.RGBA{A: 160}}
|
2022-03-25 07:33:23 +00:00
|
|
|
}
|
2022-03-26 06:52:59 +00:00
|
|
|
|
|
|
|
draw.DrawMask(
|
2022-03-25 19:24:52 +00:00
|
|
|
img,
|
|
|
|
rc,
|
2022-03-25 10:23:31 +00:00
|
|
|
src,
|
2022-03-25 19:24:52 +00:00
|
|
|
src.Bounds().Min,
|
2022-03-26 06:52:59 +00:00
|
|
|
mask,
|
|
|
|
src.Bounds().Min,
|
2022-03-25 19:38:46 +00:00
|
|
|
draw.Over,
|
2022-03-25 10:23:31 +00:00
|
|
|
)
|
2022-03-25 07:33:23 +00:00
|
|
|
}
|
2022-03-25 18:32:40 +00:00
|
|
|
images = append(images, img)
|
2022-03-26 06:52:59 +00:00
|
|
|
durations = append(durations, frame.Duration)
|
2022-03-25 10:23:31 +00:00
|
|
|
}
|
|
|
|
or := images[0].Bounds()
|
|
|
|
for _, realFrame := range images {
|
|
|
|
or = realFrame.Bounds().Union(or)
|
|
|
|
}
|
2022-03-25 18:32:40 +00:00
|
|
|
if or.Size().X == 0 || or.Size().Y == 0 {
|
2022-03-26 06:52:59 +00:00
|
|
|
return fmt.Errorf("no frames found for animation")
|
2022-03-25 18:32:40 +00:00
|
|
|
}
|
2022-03-25 10:23:31 +00:00
|
|
|
for i, realFrame := range images {
|
2022-03-25 19:38:46 +00:00
|
|
|
//log.Info().Int("frame", num).Interface("rect", or).Interface("frame", realFrame.Bounds()).Msg("")
|
2022-03-25 19:24:52 +00:00
|
|
|
src := realFrame
|
2022-03-25 10:23:31 +00:00
|
|
|
big := image.NewNRGBA64(or)
|
|
|
|
draw.Draw(
|
|
|
|
big,
|
2022-03-25 19:24:52 +00:00
|
|
|
src.Bounds(),
|
|
|
|
src,
|
|
|
|
src.Bounds().Min,
|
2022-03-25 10:23:31 +00:00
|
|
|
draw.Src,
|
|
|
|
)
|
|
|
|
fr := apng.Frame{
|
|
|
|
Image: big,
|
|
|
|
DelayDenominator: 1000,
|
2022-03-26 06:52:59 +00:00
|
|
|
DelayNumerator: uint16(durations[i]),
|
2022-03-25 10:23:31 +00:00
|
|
|
}
|
2022-03-26 06:52:59 +00:00
|
|
|
f(big, int(fr.DelayNumerator))
|
2022-03-25 07:33:23 +00:00
|
|
|
}
|
2022-03-26 06:52:59 +00:00
|
|
|
return nil
|
|
|
|
|
2022-03-25 07:33:23 +00:00
|
|
|
}
|