205 lines
4.5 KiB
Go
205 lines
4.5 KiB
Go
package sprite
|
|
|
|
import (
|
|
"fmt"
|
|
"image"
|
|
"image/color"
|
|
"image/png"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/pkg/errors"
|
|
"github.com/srwiley/oksvg"
|
|
"github.com/srwiley/rasterx"
|
|
"golang.org/x/image/draw"
|
|
)
|
|
|
|
type Direction int
|
|
|
|
const (
|
|
Horizontal = iota
|
|
Vertical
|
|
)
|
|
|
|
func (d Direction) String() string {
|
|
switch d {
|
|
case Horizontal:
|
|
return "Horizontal"
|
|
case Vertical:
|
|
return "Vertical"
|
|
default:
|
|
return "Unknown"
|
|
}
|
|
}
|
|
|
|
type Image struct {
|
|
Name string
|
|
Width, Height int
|
|
X, Y int
|
|
}
|
|
|
|
type Sprite struct {
|
|
x, y int
|
|
padding int
|
|
direction Direction
|
|
standard int
|
|
count int
|
|
Images []Image
|
|
}
|
|
|
|
func NewSprite(padding int, direct int, count int, standard int) (*Sprite, error) {
|
|
|
|
if Direction(direct).String() == "Unknown" {
|
|
return nil, errors.Errorf("direction %d is unknown", direct)
|
|
}
|
|
|
|
if count <= 0 {
|
|
return nil, errors.Errorf("count %d need to max than 0", count)
|
|
}
|
|
|
|
if standard <= 0 {
|
|
return nil, errors.Errorf("standard %d need to max than 0", standard)
|
|
}
|
|
|
|
return &Sprite{
|
|
x: 0,
|
|
y: 0,
|
|
padding: padding,
|
|
direction: Direction(direct),
|
|
count: count,
|
|
standard: standard,
|
|
Images: []Image{},
|
|
}, nil
|
|
}
|
|
|
|
func (s *Sprite) MergeImages(infos []ImageInfo) (image.Image, error) {
|
|
width, height := 0, 0
|
|
max := 0
|
|
spritesheet := image.NewRGBA(image.Rect(0, 0, 0, 0))
|
|
for i, info := range infos {
|
|
img, err := info.ReadInfo()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
x, y := 0, 0
|
|
imgWidth, imgHeight := img.Bounds().Dx(), img.Bounds().Dy()
|
|
perCount := (i + 1) / s.count
|
|
nowCount := i % s.count
|
|
|
|
if s.direction == Horizontal {
|
|
scale := float64(s.standard) / float64(imgHeight)
|
|
imgWidth = int(float64(imgWidth) * scale)
|
|
imgHeight = s.standard
|
|
|
|
if perCount == 0 {
|
|
height = nowCount * (s.standard + s.padding*2)
|
|
} else {
|
|
height = s.count * (s.standard + s.padding*2)
|
|
}
|
|
|
|
y = (s.standard+2*s.padding)*nowCount + s.padding
|
|
if nowCount == 0 {
|
|
x = width + s.padding
|
|
max = imgWidth
|
|
width = width + imgWidth + s.padding*2
|
|
} else {
|
|
if imgWidth > max {
|
|
width = width + (imgWidth - max)
|
|
max = imgWidth
|
|
}
|
|
x = width - max - s.padding
|
|
}
|
|
}
|
|
|
|
if s.direction == Vertical {
|
|
scale := float64(s.standard) / float64(imgWidth)
|
|
imgHeight = int(float64(imgHeight) * scale)
|
|
imgWidth = s.standard
|
|
|
|
if perCount == 0 {
|
|
width = (nowCount + 1) * (s.standard + s.padding*2)
|
|
} else {
|
|
width = s.count * (s.standard + s.padding*2)
|
|
}
|
|
|
|
x = (s.standard+2*s.padding)*nowCount + s.padding
|
|
if nowCount == 0 {
|
|
y = height + s.padding
|
|
max = imgHeight
|
|
height = height + imgHeight + s.padding*2
|
|
} else {
|
|
if imgHeight > max {
|
|
height = height + (imgHeight - max)
|
|
max = imgHeight
|
|
}
|
|
y = height - max - s.padding
|
|
}
|
|
}
|
|
|
|
newspritesheet := image.NewRGBA(image.Rect(0, 0, width, height))
|
|
|
|
if spritesheet.Bounds().Dx() > 0 && spritesheet.Bounds().Dy() > 0 {
|
|
draw.Draw(newspritesheet, spritesheet.Bounds(), spritesheet, image.Point{}, draw.Src)
|
|
}
|
|
|
|
draw.ApproxBiLinear.Scale(newspritesheet, image.Rect(x, y, x+imgWidth, y+imgHeight), img, img.Bounds(), draw.Src, nil)
|
|
spritesheet = newspritesheet
|
|
|
|
s.Images = append(s.Images, Image{
|
|
Name: info.Name,
|
|
Width: imgWidth,
|
|
Height: imgHeight,
|
|
X: x,
|
|
Y: y,
|
|
})
|
|
fmt.Println(info.Name, imgWidth, imgHeight, x, y)
|
|
}
|
|
return spritesheet, nil
|
|
}
|
|
|
|
type ImageInfo struct {
|
|
Name, Path string
|
|
}
|
|
|
|
func (i *ImageInfo) ReadInfo() (image.Image, error) {
|
|
if strings.HasSuffix(i.Path, ".png") {
|
|
pngFile, err := os.Open(i.Path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer pngFile.Close()
|
|
|
|
pngImg, err := png.Decode(pngFile)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return pngImg, nil
|
|
}
|
|
|
|
if strings.HasSuffix(i.Path, ".svg") {
|
|
svgFile, err := os.Open(i.Path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer svgFile.Close()
|
|
|
|
svgInfo, err := oksvg.ReadIconStream(svgFile, oksvg.WarnErrorMode)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
svgImg := image.NewRGBA(image.Rect(int(svgInfo.ViewBox.X), int(svgInfo.ViewBox.Y), int(svgInfo.ViewBox.W), int(svgInfo.ViewBox.H)))
|
|
draw.Draw(svgImg, svgImg.Bounds(), &image.Uniform{color.Transparent}, image.Point{}, draw.Src)
|
|
svgInfo.SetTarget(0, 0, float64(svgInfo.ViewBox.W), float64(svgInfo.ViewBox.H))
|
|
scaner := rasterx.NewScannerGV(int(svgInfo.ViewBox.W), int(svgInfo.ViewBox.H), svgImg, svgImg.Bounds())
|
|
raster := rasterx.NewDasher(int(svgInfo.ViewBox.W), int(svgInfo.ViewBox.H), scaner)
|
|
svgInfo.Draw(raster, 1.0)
|
|
|
|
return svgImg, nil
|
|
}
|
|
|
|
return nil, nil
|
|
}
|