diff --git a/common/nori/animation.go b/common/nori/animation.go index d9bafb7..65a6422 100644 --- a/common/nori/animation.go +++ b/common/nori/animation.go @@ -109,7 +109,6 @@ func (a *Animation) Decode(rd *bufio.Reader, version int) error { return err } var cast uint32 - if err := binary.Read(rd, end, &cast); err != nil { return err } diff --git a/common/nori/export.go b/common/nori/export.go new file mode 100644 index 0000000..c51fd73 --- /dev/null +++ b/common/nori/export.go @@ -0,0 +1,19 @@ +package nori + +import "gitlab.com/gfxlabs/gfximg/apng" + +func (n *Nori) ExportAnimation() *apng.APNG { + g := n.Gawi + a := &apng.APNG{ + Frames: make([]apng.Frame, 0, len(g.Images)), + } + for i := 0; i < len(g.Images); i++ { + fr := apng.Frame{ + Image: g.Images[i].Frame, + DelayNumerator: uint16(g.Images[i].Delay), + DelayDenominator: 1000, + } + a.Frames = append(a.Frames, fr) + } + return a +} diff --git a/common/nori/image.go b/common/nori/image.go index b5b6a20..15a81b7 100644 --- a/common/nori/image.go +++ b/common/nori/image.go @@ -25,13 +25,24 @@ type Image struct { Frame image.Image } -func (i *Image) Decode(rd *bufio.Reader) error { + +func (i *Image) Decode(rd *bufio.Reader, palette *PaletteSection) error { cf := image.NewRGBA64(image.Rect(0, 0, int(i.Width), int(i.Height))) i.Frame = cf switch i.Bpp { case 8: - rd.Discard(int(i.Size)) - return fmt.Errorf("bpp = 8, but no palette") + if palette == nil { + return fmt.Errorf("bpp = 8, but no palette") + } + for idx := 0; idx < int(i.Size); idx++ { + b, err := rd.ReadByte() + if err != nil { + return err + } + col := palette.Palette[b] + cf.Set(idx%int(i.Width), idx/int(i.Width), col) + } + return nil case 16: var bgr uint16 for idx := 0; idx < int(i.Size/2); idx++ { @@ -69,3 +80,4 @@ func (i *Image) Decode(rd *bufio.Reader) error { return fmt.Errorf("unsupported bpp, %d", i.Bpp) } } + diff --git a/common/nori/nori.go b/common/nori/nori.go index 4544e1c..e1e3f49 100644 --- a/common/nori/nori.go +++ b/common/nori/nori.go @@ -1,14 +1,14 @@ package nori +import "image/color" + type PaletteSection struct { Version uint32 - Param01 uint32 - Param02 uint32 - Param03 uint32 - Param04 uint32 + Params [4]uint32 Divided bool DataLength uint32 - // Graphics::Palette color_data + Palette [256]color.Color + MainBound [2]uint32 } type GawiSection struct { diff --git a/common/nori/nori_test.go b/common/nori/nori_test.go index f18e194..b476d5c 100644 --- a/common/nori/nori_test.go +++ b/common/nori/nori_test.go @@ -1,8 +1,11 @@ package nori import ( + "bytes" "os" "testing" + + "gitlab.com/gfxlabs/gfximg/apng" ) func TestParseFile1(t *testing.T) { @@ -28,3 +31,24 @@ func TestParseFile2(t *testing.T) { } t.Logf("\n nori: %+v\n gawi: %+v", nori, nori.Gawi) } + +func TestParsePalette(t *testing.T) { + rd, err := os.OpenFile("./nori_test/palette.nri", os.O_RDONLY, 0666) + if err != nil { + t.Logf("open file: %s", err) + t.Fail() + } + nori, err := NewReader(rd).Decode() + if err != nil { + t.Errorf("decode: %s", err) + } + t.Logf("\n nori: %+v\n gawi: %+v\n palette: %+v\n", nori, nori.Gawi, nori.Gawi.Palette) + + out := new(bytes.Buffer) + a := nori.ExportAnimation() + err = apng.Encode(out, *a) + if err != nil { + t.Errorf("encode: %s", err) + } + os.WriteFile("./nori_test/palette.apng", out.Bytes(), 0740) +} diff --git a/common/nori/nori_test/palette.apng b/common/nori/nori_test/palette.apng new file mode 100755 index 0000000..1bdc8b8 Binary files /dev/null and b/common/nori/nori_test/palette.apng differ diff --git a/common/nori/nori_test/palette.nri b/common/nori/nori_test/palette.nri new file mode 100644 index 0000000..93d3ef8 Binary files /dev/null and b/common/nori/nori_test/palette.nri differ diff --git a/common/nori/reader.go b/common/nori/reader.go index 23a1deb..8b5570e 100644 --- a/common/nori/reader.go +++ b/common/nori/reader.go @@ -4,6 +4,7 @@ import ( "bufio" "encoding/binary" "fmt" + "image/color" "io" ) @@ -98,8 +99,11 @@ func (n *Reader) decodeGawi() error { return err } if g.HasPalette { - n.decodePalette() + if err := n.decodePalette(); err != nil { + return err + } } + fmt.Printf("\n-------\n n:'%+v'\n g:'%+v'", n, g) if _, err := n.r.Discard(4 * int(g.BmpCount)); err != nil { return err } @@ -118,9 +122,47 @@ func (n *Reader) decodeGawi() error { } func (n *Reader) decodePalette() error { + if _, err := io.ReadFull(n, n.nori.lastSignature[:]); err != nil { + return err + } if sig, target := string(n.nori.lastSignature[:]), "PAL_"; sig != target { return fmt.Errorf("bad sig: want %s, got %s", target, sig) } + cp := &PaletteSection{ + Palette: [256]color.Color{}, + } + n.nori.Gawi.Palette = cp + if err := binary.Read(n, end, &cp.Version); err != nil { + return err + } + if err := binary.Read(n, end, cp.Params[:]); err != nil { + return err + } + var cast uint32 + if err := binary.Read(n, end, &cast); err != nil { + return err + } + cp.Divided = (cast > 0) + if err := binary.Read(n, end, &cp.DataLength); err != nil { + return err + } + for i := 0; i < 256; i++ { + rgb := [3]byte{} + _, err := io.ReadFull(n, rgb[:]) + if err != nil { + return err + } + cp.Palette[i] = &color.RGBA{ + rgb[0], rgb[1], rgb[2], + 0xff, + } + } + + if cp.Divided { + if err := binary.Read(n, end, cp.MainBound[:]); err != nil { + return err + } + } return nil } func (n *Reader) decodeImage(img *Image) error { @@ -154,7 +196,7 @@ func (n *Reader) decodeImage(img *Image) error { if err := binary.Read(n, end, &img.OffsetY); err != nil { return err } - if err := img.Decode(n.r); err != nil { + if err := img.Decode(n.r, n.nori.Gawi.Palette); err != nil { return err } return nil diff --git a/go.mod b/go.mod index f62b7f0..25a5928 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,5 @@ module git.tuxpa.in/a/gotagonist go 1.18 + +require gitlab.com/gfxlabs/gfximg v0.0.5 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..16ecb5c --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +gitlab.com/gfxlabs/gfximg v0.0.5 h1:jtHE6In6axz0zGPy7YnLIZV9RFdcFEZzpdqoXb52K9s= +gitlab.com/gfxlabs/gfximg v0.0.5/go.mod h1:IAYZwCoqy3JFKwkKafragpfecp5Up6FFIzI7VvK2Zzo=