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