support subimages and compressed
After Width: | Height: | Size: 5.8 KiB |
After Width: | Height: | Size: 5.8 KiB |
After Width: | Height: | Size: 7.0 KiB |
After Width: | Height: | Size: 7.8 KiB |
After Width: | Height: | Size: 7.4 KiB |
After Width: | Height: | Size: 7.8 KiB |
After Width: | Height: | Size: 6.8 KiB |
After Width: | Height: | Size: 5.8 KiB |
After Width: | Height: | Size: 6.2 KiB |
After Width: | Height: | Size: 8.7 KiB |
After Width: | Height: | Size: 8.8 KiB |
After Width: | Height: | Size: 6.1 KiB |
After Width: | Height: | Size: 7.0 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 53 KiB |
After Width: | Height: | Size: 60 KiB |
After Width: | Height: | Size: 60 KiB |
After Width: | Height: | Size: 54 KiB |
After Width: | Height: | Size: 5.8 KiB |
After Width: | Height: | Size: 7.8 KiB |
After Width: | Height: | Size: 7.8 KiB |
After Width: | Height: | Size: 5.8 KiB |
After Width: | Height: | Size: 7.8 KiB |
After Width: | Height: | Size: 5.8 KiB |
After Width: | Height: | Size: 7.8 KiB |
After Width: | Height: | Size: 7.8 KiB |
After Width: | Height: | Size: 5.8 KiB |
After Width: | Height: | Size: 5.8 KiB |
After Width: | Height: | Size: 7.8 KiB |
After Width: | Height: | Size: 7.8 KiB |
After Width: | Height: | Size: 5.8 KiB |
After Width: | Height: | Size: 7.4 KiB |
After Width: | Height: | Size: 7.8 KiB |
After Width: | Height: | Size: 6.8 KiB |
After Width: | Height: | Size: 5.8 KiB |
After Width: | Height: | Size: 5.8 KiB |
After Width: | Height: | Size: 5.8 KiB |
|
@ -4,11 +4,12 @@ import (
|
|||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"git.tuxpa.in/a/nori/renderer"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"git.tuxpa.in/a/nori/renderer"
|
||||
|
||||
"git.tuxpa.in/a/nori"
|
||||
"git.tuxpa.in/a/nori/utils"
|
||||
"git.tuxpa.in/a/zlog/log"
|
||||
|
@ -54,7 +55,7 @@ func dump(filename string, output string) {
|
|||
printIf("reading file '%s'\n", filename)
|
||||
n, err := nori.FromFile(filename)
|
||||
if err != nil {
|
||||
log.Panicln("decode: %s", err)
|
||||
log.Panicf("decode: %s", err)
|
||||
}
|
||||
out := new(bytes.Buffer)
|
||||
printIf("rendering %d animation(s)\n", n.AnimationCount)
|
|
@ -1,7 +1,6 @@
|
|||
package nori
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
@ -16,7 +15,7 @@ func FromFile(fp string) (*Nori, error) {
|
|||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
return Decode(bufio.NewReader(file))
|
||||
return Decode(file)
|
||||
}
|
||||
|
||||
func Decode(r io.Reader) (*Nori, error) {
|
||||
|
|
2
gawi.go
|
@ -77,7 +77,7 @@ func (g *GawiSection) Decode(rd io.Reader) error {
|
|||
g.Images = make([]*Image, int(g.BmpCount))
|
||||
for i := range g.Images {
|
||||
g.Images[i] = &Image{Bpp: g.Bpp}
|
||||
if err := g.Images[i].Decode(rd, g.Palette); err != nil {
|
||||
if err := g.Images[i].Decode(rd, g); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
|
3
go.mod
|
@ -5,13 +5,12 @@ go 1.18
|
|||
require (
|
||||
git.tuxpa.in/a/zlog v1.32.0
|
||||
github.com/disintegration/imaging v1.6.2
|
||||
github.com/go-chi/chi/v5 v5.0.7
|
||||
github.com/phrozen/blend v0.0.0-20210220204729-f26b6cf7a28e
|
||||
gitlab.com/gfxlabs/gfximg v0.0.5
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/chai2010/webp v1.1.1 // indirect
|
||||
github.com/go-chi/chi/v5 v5.0.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.12 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
golang.org/x/image v0.0.0-20211028202545-6944b10bf410 // indirect
|
||||
|
|
3
go.sum
|
@ -1,7 +1,5 @@
|
|||
git.tuxpa.in/a/zlog v1.32.0 h1:KKXbRF1x8kJDSzUoGz/pivo+4TVY6xT5sVtdFZ6traY=
|
||||
git.tuxpa.in/a/zlog v1.32.0/go.mod h1:vUa2Qhu6DLPLqmfRy99FiPqaY2eb6/KQjtMekW3UNnA=
|
||||
github.com/chai2010/webp v1.1.1 h1:jTRmEccAJ4MGrhFOrPMpNGIJ/eybIgwKpcACsrTEapk=
|
||||
github.com/chai2010/webp v1.1.1/go.mod h1:0XVwvZWdjjdxpUEIf7b9g9VkHFnInUSYujwqTLEuldU=
|
||||
github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
||||
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
||||
|
@ -18,7 +16,6 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
|||
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
gitlab.com/gfxlabs/gfximg v0.0.5 h1:jtHE6In6axz0zGPy7YnLIZV9RFdcFEZzpdqoXb52K9s=
|
||||
gitlab.com/gfxlabs/gfximg v0.0.5/go.mod h1:IAYZwCoqy3JFKwkKafragpfecp5Up6FFIzI7VvK2Zzo=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 h1:hVwzHzIUGRjiF7EcUjqNxk3NCfkPxbDKRdnNE1Rpg0U=
|
||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20211028202545-6944b10bf410 h1:hTftEOvwiOq2+O8k2D5/Q7COC7k5Qcrgc2TFURJYnvQ=
|
||||
golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM=
|
||||
|
|
203
image.go
|
@ -1,6 +1,7 @@
|
|||
package nori
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"image"
|
||||
|
@ -11,6 +12,10 @@ import (
|
|||
type Image struct {
|
||||
Bpp uint32
|
||||
Count uint32
|
||||
SubImages []*SubImage
|
||||
}
|
||||
|
||||
type SubImage struct {
|
||||
Delay uint32
|
||||
OffsetX int32
|
||||
OffsetY int32
|
||||
|
@ -19,19 +24,143 @@ type Image struct {
|
|||
Height uint32
|
||||
|
||||
Size uint32
|
||||
Data []byte
|
||||
|
||||
Img image.Image
|
||||
rgb *image.RGBA64
|
||||
}
|
||||
|
||||
func (i *Image) Decode(rd io.Reader, palette *PaletteSection) error {
|
||||
if err := binary.Read(rd, end, &i.Count); err != nil {
|
||||
func (i *SubImage) decode(rd io.Reader, img *Image, size int, g *GawiSection) error {
|
||||
switch img.Bpp {
|
||||
case 8:
|
||||
if g.Palette == nil {
|
||||
return fmt.Errorf("bpp = 8, but no palette")
|
||||
}
|
||||
for idx := 0; idx < int(size); idx++ {
|
||||
b, err := readByte(rd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if i.Count != 1 {
|
||||
return fmt.Errorf("img count should be 1")
|
||||
col := g.Palette.Palette[b]
|
||||
i.rgb.Set(idx%int(i.Width), idx/int(i.Width), col)
|
||||
}
|
||||
case 16:
|
||||
var bgr uint16
|
||||
for idx := 0; idx < int(size/2); idx++ {
|
||||
if err := binary.Read(rd, end, &bgr); err != nil {
|
||||
return err
|
||||
}
|
||||
red := uint8(((0x7c00 & bgr) >> 10) * 255 / 31)
|
||||
green := uint8(((0x3e0 & bgr) >> 5) * 255 / 31)
|
||||
blue := uint8((0x1f & bgr) * 255 / 31)
|
||||
alpha := uint8(0xff)
|
||||
if red == 0xff && green == 0x00 && blue == 0xff {
|
||||
alpha = 0x00
|
||||
}
|
||||
if red == 0x00 && blue == 0x00 && green == 0xff {
|
||||
alpha = 0x00
|
||||
}
|
||||
col := &color.RGBA{
|
||||
red, green, blue,
|
||||
alpha,
|
||||
}
|
||||
i.rgb.Set(idx%int(i.Width), idx/int(i.Width), col)
|
||||
}
|
||||
case 24:
|
||||
for idx := 0; idx < int(size/3); idx++ {
|
||||
rgb := [3]byte{}
|
||||
_, err := io.ReadFull(rd, rgb[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
alpha := uint8(0xff)
|
||||
|
||||
if rgb[2] == 0x00 && rgb[1] == 0xff && rgb[0] == 0x00 {
|
||||
alpha = 0x00
|
||||
}
|
||||
if rgb[2] == 0xff && rgb[1] == 0x00 && rgb[0] == 0xff {
|
||||
alpha = 0x00
|
||||
}
|
||||
|
||||
col := &color.RGBA{
|
||||
rgb[2], rgb[1], rgb[0],
|
||||
alpha,
|
||||
}
|
||||
i.rgb.Set(idx%int(i.Width), idx/int(i.Width), col)
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unsupported bpp, %d", img.Bpp)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (i *SubImage) decodeCompressed(rd io.Reader, img *Image, g *GawiSection) error {
|
||||
var u16 uint16
|
||||
ob := new(bytes.Buffer)
|
||||
for k := 0; k < int(i.Height); k++ {
|
||||
pixelCount := 0
|
||||
if err := binary.Read(rd, end, &u16); err != nil {
|
||||
return fmt.Errorf("compressed size: %w", err)
|
||||
}
|
||||
want := make([]byte, int(u16)-2)
|
||||
if _, err := io.ReadFull(rd, want); err != nil {
|
||||
return fmt.Errorf("compressed want (%d): %w", u16, err)
|
||||
}
|
||||
buf := bytes.NewBuffer(want)
|
||||
for buf.Len() > 0 {
|
||||
var paddingSize, foregroundSize uint16
|
||||
if err := binary.Read(buf, end, &paddingSize); err != nil {
|
||||
return fmt.Errorf("padding size: %w", err)
|
||||
}
|
||||
if err := binary.Read(buf, end, &foregroundSize); err != nil {
|
||||
return fmt.Errorf("foreground size: %w", err)
|
||||
}
|
||||
for x := 0; x < int(paddingSize); x++ {
|
||||
switch img.Bpp {
|
||||
case 8:
|
||||
ob.Write([]byte{0})
|
||||
case 16:
|
||||
ob.Write([]byte{0x1f, 0x7c})
|
||||
case 24:
|
||||
ob.Write([]byte{0xff, 0, 0xff})
|
||||
default:
|
||||
ob.Write([]byte{0})
|
||||
}
|
||||
}
|
||||
foregroundBytes := make([]byte, int(foregroundSize)*(int(g.Bpp)/8))
|
||||
if _, err := io.ReadFull(buf, foregroundBytes); err != nil {
|
||||
return fmt.Errorf("foreground bytes: %w", err)
|
||||
}
|
||||
for y := 0; y < len(foregroundBytes); y++ {
|
||||
ob.WriteByte(foregroundBytes[y])
|
||||
}
|
||||
pixelCount = pixelCount + int(paddingSize) + int(foregroundSize)
|
||||
}
|
||||
if pixelCount != int(i.Width) {
|
||||
return fmt.Errorf("scanline has fewer pixels than the width (expected %d, got %d)", i.Width, pixelCount)
|
||||
}
|
||||
}
|
||||
return i.decode(ob, img, ob.Len(), g)
|
||||
}
|
||||
|
||||
func (i *SubImage) Decode(rd io.Reader, img *Image, g *GawiSection) error {
|
||||
cf := image.NewRGBA64(image.Rect(0, 0, int(i.Width), int(i.Height)))
|
||||
i.rgb = cf
|
||||
i.Img = i.rgb
|
||||
if g.IsCompressed {
|
||||
return i.decodeCompressed(rd, img, g)
|
||||
}
|
||||
return i.decode(rd, img, int(i.Size), g)
|
||||
}
|
||||
|
||||
func (img *Image) Decode(rd io.Reader, g *GawiSection) error {
|
||||
if err := binary.Read(rd, end, &img.Count); err != nil {
|
||||
return err
|
||||
}
|
||||
img.SubImages = make([]*SubImage, 0, img.Count)
|
||||
for k := 0; k < int(img.Count); k++ {
|
||||
i := &SubImage{}
|
||||
img.SubImages = append(img.SubImages, i)
|
||||
if err := binary.Read(rd, end, &i.Size); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -55,71 +184,9 @@ func (i *Image) Decode(rd io.Reader, palette *PaletteSection) error {
|
|||
if err := binary.Read(rd, end, &i.OffsetY); err != nil {
|
||||
return err
|
||||
}
|
||||
cf := image.NewRGBA64(image.Rect(0, 0, int(i.Width), int(i.Height)))
|
||||
i.Img = cf
|
||||
switch i.Bpp {
|
||||
case 8:
|
||||
if palette == nil {
|
||||
return fmt.Errorf("bpp = 8, but no palette")
|
||||
}
|
||||
for idx := 0; idx < int(i.Size); idx++ {
|
||||
b, err := readByte(rd)
|
||||
if err != nil {
|
||||
if err := i.Decode(rd, img, g); 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++ {
|
||||
if err := binary.Read(rd, end, &bgr); err != nil {
|
||||
return err
|
||||
}
|
||||
red := uint8(((0x7c00 & bgr) >> 10) * 255 / 31)
|
||||
green := uint8(((0x3e0 & bgr) >> 5) * 255 / 31)
|
||||
blue := uint8((0x1f & bgr) * 255 / 31)
|
||||
alpha := uint8(0xff)
|
||||
if red == 0xff && green == 0x00 && blue == 0xff {
|
||||
alpha = 0x00
|
||||
}
|
||||
if red == 0x00 && blue == 0x00 && green == 0xff {
|
||||
alpha = 0x00
|
||||
}
|
||||
col := &color.RGBA{
|
||||
red, green, blue,
|
||||
alpha,
|
||||
}
|
||||
cf.Set(idx%int(i.Width), idx/int(i.Width), col)
|
||||
}
|
||||
return nil
|
||||
case 24:
|
||||
i.Data = make([]byte, i.Size)
|
||||
for idx := 0; idx < int(i.Size/3); idx++ {
|
||||
rgb := [3]byte{}
|
||||
_, err := io.ReadFull(rd, rgb[:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
alpha := uint8(0xff)
|
||||
|
||||
if rgb[2] == 0x00 && rgb[1] == 0xff && rgb[0] == 0x00 {
|
||||
alpha = 0x00
|
||||
}
|
||||
if rgb[2] == 0xff && rgb[1] == 0x00 && rgb[0] == 0xff {
|
||||
alpha = 0x00
|
||||
}
|
||||
|
||||
col := &color.RGBA{
|
||||
rgb[2], rgb[1], rgb[0],
|
||||
alpha,
|
||||
}
|
||||
|
||||
cf.Set(idx%int(i.Width), idx/int(i.Width), col)
|
||||
}
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("unsupported bpp, %d", i.Bpp)
|
||||
}
|
||||
}
|
||||
|
|
14
reader.go
|
@ -44,5 +44,17 @@ func reflectReader(r io.Reader) io.Reader {
|
|||
if _, ok := r.(readByteSeeker); ok {
|
||||
return r
|
||||
}
|
||||
return bufio.NewReader(r)
|
||||
return fileByteReader{
|
||||
ReadSeeker: r.(io.ReadSeeker),
|
||||
}
|
||||
}
|
||||
|
||||
type fileByteReader struct {
|
||||
io.ReadSeeker
|
||||
}
|
||||
|
||||
func (o *fileByteReader) ReadByte() (byte, error) {
|
||||
b := [1]byte{}
|
||||
_, err := o.Read(b[:])
|
||||
return b[0], err
|
||||
}
|
||||
|
|
22
render.go
|
@ -2,13 +2,14 @@ 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"
|
||||
"image"
|
||||
"image/color"
|
||||
"image/draw"
|
||||
)
|
||||
|
||||
func copyImage(img image.Image) image.Image {
|
||||
|
@ -34,8 +35,11 @@ func RenderAnimation(n *Nori, num int, f RenderFunc) error {
|
|||
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.Img.Bounds().Add(pt) // translate rectangle to put in the global canvas
|
||||
rc := bitmap.SubImages[0].Img.Bounds().Add(pt) // translate rectangle to put in the global canvas
|
||||
if i == 0 {
|
||||
canvasRect = rc
|
||||
} else {
|
||||
|
@ -45,19 +49,19 @@ func RenderAnimation(n *Nori, num int, f RenderFunc) error {
|
|||
img := image.NewNRGBA(canvasRect)
|
||||
for _, plane := range planes {
|
||||
bitmap := g.Images[plane.BitmapId]
|
||||
drawBitmap := bitmap.Img
|
||||
drawBitmap := bitmap.SubImages[0].Img
|
||||
transparent := false
|
||||
//flipx
|
||||
if plane.RenderFlag&1 != 0 {
|
||||
if bitmap.Img == drawBitmap {
|
||||
drawBitmap = copyImage(bitmap.Img)
|
||||
if bitmap.SubImages[0].Img == drawBitmap {
|
||||
drawBitmap = copyImage(bitmap.SubImages[0].Img)
|
||||
}
|
||||
drawBitmap = imaging.FlipH(drawBitmap)
|
||||
}
|
||||
//flipy
|
||||
if plane.RenderFlag&2 != 0 {
|
||||
if bitmap.Img == drawBitmap {
|
||||
drawBitmap = copyImage(bitmap.Img)
|
||||
if bitmap.SubImages[0].Img == drawBitmap {
|
||||
drawBitmap = copyImage(bitmap.SubImages[0].Img)
|
||||
}
|
||||
drawBitmap = imaging.FlipV(drawBitmap)
|
||||
}
|
||||
|
|