diff --git a/common/nori/export.go b/common/nori/export.go index 8f68dcc..f1eca4a 100644 --- a/common/nori/export.go +++ b/common/nori/export.go @@ -6,6 +6,7 @@ import ( "image/draw" "git.tuxpa.in/a/zlog/log" + "github.com/phrozen/blend" "gitlab.com/gfxlabs/gfximg/apng" ) @@ -30,54 +31,89 @@ func (n *Nori) ExportAnimation() (*apng.APNG, error) { a := &apng.APNG{ Frames: make([]apng.Frame, 0, len(g.Images)), } + images := make([]*image.NRGBA64, 0, len(n.Animations)) for i := 0; i < len(n.Animations); i++ { - for _, v := range n.Animations[i].Frames { - canvasRect := image.Rect(0, 0, 0, 0) - for _, plane := range v.Planes { - if int(plane.BitmapId) >= len(n.Gawi.Images) { - log.Printf("could not find bitmap %d, only have %d", plane.BitmapId, len(n.Gawi.Images)) - } - bitmap := n.Gawi.Images[plane.BitmapId] - canvasRect = canvasRect.Union(bitmap.Img.Bounds()) - } - img := image.NewRGBA64(canvasRect) - for _, plane := range v.Planes { - bitmap := n.Gawi.Images[plane.BitmapId] - transparent := false - //flipx - if plane.RenderFlag&1 != 0 { - } - //flipy - if plane.RenderFlag&2 != 0 { - } - // is transparent - if plane.RenderFlag&0x20 != 0 { - transparent = true - } - _ = transparent - switch plane.Blend { - case BlendMode_Alpha: - case BlendMode_Mul, BlendMode_Mul7: - case BlendMode_Add, BlendMode_Add8: - case BlendMode_InvertMul, BlendMode_InvertMul5: - case BlendMode_None: - break - default: - return nil, fmt.Errorf("unknown blend mode: %d", plane.Blend) - } - draw.Draw(img, - img.Rect, - bitmap.Img, - image.Point{X: int(plane.PlaneX), - Y: int(plane.PlaneY)}, draw.Over) - } - fr := apng.Frame{ - Image: g.Images[i].Img, - DelayNumerator: uint16(g.Images[i].Delay), - DelayDenominator: 1000, - } - a.Frames = append(a.Frames, fr) + if len(n.Animations[i].Frames) < 1 { + continue } + planes := n.Animations[i].Frames[0].Planes + var canvasRect image.Rectangle + for _, plane := range planes { + if int(plane.BitmapId) >= len(n.Gawi.Images) { + log.Printf("could not find bitmap %d, only have %d", plane.BitmapId, len(n.Gawi.Images)) + } + bitmap := n.Gawi.Images[plane.BitmapId] + pt := image.Pt(int(plane.PlaneX), int(plane.PlaneY)) // where to put the point + rc := bitmap.Img.Bounds() + canvasRect = canvasRect.Union(rc.Add(pt)) + } + img := image.NewNRGBA64(canvasRect) + for _, plane := range planes { + bitmap := n.Gawi.Images[plane.BitmapId] + transparent := false + //flipx + if plane.RenderFlag&1 != 0 { + } + //flipy + if plane.RenderFlag&2 != 0 { + } + // is transparent + if plane.RenderFlag&0x20 != 0 { + transparent = true + } + _ = transparent + pt := image.Pt(int(plane.PlaneX), int(plane.PlaneY)) // where to put the point + src := bitmap.Img + sr := src.Bounds() + r := image.Rectangle{pt, pt.Add(sr.Size())} + switch plane.Blend { + case BlendMode_Alpha: + case BlendMode_Mul, BlendMode_Mul7: + blend.BlendImage(img, bitmap.Img, blend.Multiply) + case BlendMode_Add, BlendMode_Add8: + blend.BlendImage(img, bitmap.Img, blend.Add) + case BlendMode_InvertMul, BlendMode_InvertMul5: + blend.BlendImage(img, bitmap.Img, blend.Multiply) + case BlendMode_None: + default: + return nil, fmt.Errorf("unknown blend mode: %d", plane.Blend) + } + draw.Draw(img, + r, + src, + sr.Min, + draw.Over, + ) + } + if len(planes) > 0 { + images = append(images, img) + + } + } + or := images[0].Bounds() + for _, realFrame := range images { + or = realFrame.Bounds().Union(or) + } + // TODO: this is bugged + for i, realFrame := range images { + big := image.NewNRGBA64(or) + draw.Draw( + big, + big.Rect, + realFrame, + realFrame.Bounds().Min, + draw.Src, + ) + fr := apng.Frame{ + Image: big, + DisposeOp: apng.DISPOSE_OP_PREVIOUS, + BlendOp: apng.BLEND_OP_OVER, + DelayDenominator: 1000, + } + if g.Images[i].Delay != 0 { + fr.DelayNumerator = uint16(g.Images[i].Delay) + } + a.Frames = append(a.Frames, fr) } return a, nil } diff --git a/common/nori/image.go b/common/nori/image.go index c45712e..fc485c5 100644 --- a/common/nori/image.go +++ b/common/nori/image.go @@ -51,11 +51,13 @@ func (i *Image) Decode(rd *bufio.Reader, palette *PaletteSection) error { red := uint8(((0x7c00 & bgr) >> 10) * 255 / 31) green := uint8(((0x3e0 & bgr) >> 5) * 255 / 31) blue := uint8((0x1f & bgr) * 255 / 31) - if red != 0 && blue != 0 && green != 0 { + alpha := uint8(0xff) + if red == 0xff && blue == 0xff { + alpha = 0x00 } col := &color.RGBA{ red, green, blue, - 0xff, + alpha, } cf.Set(idx%int(i.Width), idx/int(i.Width), col) } @@ -68,9 +70,13 @@ func (i *Image) Decode(rd *bufio.Reader, palette *PaletteSection) error { if err != nil { return err } + alpha := uint8(0xff) col := &color.RGBA{ rgb[0], rgb[1], rgb[2], - 0xff, + alpha, + } + if rgb[0] == 0xff && rgb[2] == 0xff { + alpha = 0x00 } cf.Set(idx%int(i.Width), idx/int(i.Width), col) } diff --git a/common/nori/nori_test.go b/common/nori/nori_test.go index 1b4988d..d48ed38 100644 --- a/common/nori/nori_test.go +++ b/common/nori/nori_test.go @@ -5,7 +5,6 @@ import ( "os" "testing" - "git.tuxpa.in/a/zlog/log" "gitlab.com/gfxlabs/gfximg/apng" ) @@ -59,14 +58,21 @@ func TestParsePalette(t *testing.T) { if err != nil { t.Errorf("export: %s", err) } - writeApng(a, "./nori_test/palette.apng") + err = writeApng(a, "./nori_test/palette.apng") + if err != nil { + t.Errorf("export: %s", err) + } } -func writeApng(a *apng.APNG, fp string) { +func writeApng(a *apng.APNG, fp string) error { out := new(bytes.Buffer) err := apng.Encode(out, *a) if err != nil { - log.Errorf("fail to encode apng %s", err) + return err } - os.WriteFile(fp, out.Bytes(), 0740) + err = os.WriteFile(fp, out.Bytes(), 0740) + if err != nil { + return err + } + return nil } diff --git a/common/nori/nori_test/palette.apng b/common/nori/nori_test/palette.apng index 34bf790..76b14be 100755 Binary files a/common/nori/nori_test/palette.apng and b/common/nori/nori_test/palette.apng differ diff --git a/common/nori/nori_test/test1.apng b/common/nori/nori_test/test1.apng index f5822a9..0ee8cdb 100755 Binary files a/common/nori/nori_test/test1.apng and b/common/nori/nori_test/test1.apng differ diff --git a/common/nori/nori_test/test2.apng b/common/nori/nori_test/test2.apng index 40d94af..99bf08d 100755 Binary files a/common/nori/nori_test/test2.apng and b/common/nori/nori_test/test2.apng differ diff --git a/go.mod b/go.mod index f9d1d6e..403c6ea 100644 --- a/go.mod +++ b/go.mod @@ -10,5 +10,6 @@ require ( require ( github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-isatty v0.0.14 // indirect + github.com/phrozen/blend v0.0.0-20210220204729-f26b6cf7a28e // indirect golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6 // indirect ) diff --git a/go.sum b/go.sum index 0fbb84f..629b124 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,8 @@ github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZb github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/phrozen/blend v0.0.0-20210220204729-f26b6cf7a28e h1:r8tWFp1HMiodzOwFtEVZ41Q0PuX/G5PWHZS14kAQMoI= +github.com/phrozen/blend v0.0.0-20210220204729-f26b6cf7a28e/go.mod h1:8LjAsvtcQgvNmMQZ/iSuduOKYgRA37KcsgPg8ZC6Krc= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= gitlab.com/gfxlabs/gfximg v0.0.5 h1:jtHE6In6axz0zGPy7YnLIZV9RFdcFEZzpdqoXb52K9s=