package nori import ( "bytes" "encoding/binary" "fmt" "image" "image/color" "io" ) type Image struct { Bpp uint32 Count uint32 SubImages []*SubImage } type SubImage struct { Delay uint32 OffsetX int32 OffsetY int32 Width uint32 Height uint32 Size uint32 Img image.Image rgb *image.RGBA64 } 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 } 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 } if err := binary.Read(rd, end, &i.Width); err != nil { return err } if err := binary.Read(rd, end, &i.Height); err != nil { return err } if err := binary.Read(rd, end, &i.Delay); err != nil { return err } if err := binary.Read(rd, end, &i.OffsetX); err != nil { return err } if err := binary.Read(rd, end, &i.OffsetY); err != nil { return err } if err := i.Decode(rd, img, g); err != nil { return err } } return nil }