Skip to content

Commit

Permalink
clean
Browse files Browse the repository at this point in the history
  • Loading branch information
Zhao-hangtian committed Jun 13, 2023
0 parents commit 288f9ca
Show file tree
Hide file tree
Showing 49 changed files with 242 additions and 0 deletions.
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# ignore ALL .log files
*.log

# ignore ALL files in ANY directory named temp, output
temp/
output/

# ignore ALL .exe files
*.exe
8 changes: 8 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions .idea/go-sprite-split.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

40 changes: 40 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# 雪碧图切割工具
使用Go实现的一个简单的PNG图片素材切割工具,基于在Alpha(透明度通道)使用DFS(深度优先搜索)算法做非透明连续区域的切割,得到素材包的雪碧图位置信息。

## 作用
1. 实现素材包雪碧图位置区域的提取(到JSON格式文件),可以精准定位到所需素材。为什么需要雪碧图可以参考[这里](https://www.w3schools.com/css/css_image_sprites.asp#:~:text=An%20image%20sprite%20is%20a,server%20requests%20and%20save%20bandwidth.)或者[这里](https://m.imooc.com/wiki/csssprite-whysprite#:~:text=%E5%88%A9%E7%94%A8%E9%9B%AA%E7%A2%A7%E5%9B%BE%E8%83%BD%E5%A4%9F%E5%BE%88,%E7%9A%84%E5%A4%A7%E5%B0%8F%E8%BF%98%E8%A6%81%E5%B0%8F%E3%80%82)

2. 实现雪碧图的分割,即从一个大素材图里提取所有的元素。

## 扩展
1. 如果您有其他希望的输出格式,可以自由的修改此代码~

2. 如果您的素材不是带透明通道的PNG图片,可以使用GIMP,Photoshop等工具先进行见到那的编辑(增加透明图层、魔棒/色彩选择去除底部背景)。

## 编译
`go build main.go`

## 例子
原始素材:
![图标集](example/icons.png "Demo Icons Picture")

### 1. 提取雪碧图信息
- 运行
`./main -input example/icons.png -output output -mode sprite`
- 输出
![img.png](assets/img.png)
您将在目录`output`下得到分割得到JSON文件,里面包含了雪碧图信息:
![img.png](assets/img_2.png)
坐标以左上方为零点,h是高度,w是宽度,x1,y1是左上坐标,x2,y2是右下坐标。

格式化的素材位置JSON信息可以在您的游戏、应用开发过程中方便的使用。

### 2. 分割大图片
- 运行
`./main --input example/icons.png`
- 输出
![img_1.png](assets/img_1.png)

您将在目录`output`下得到分割得到小图片
![截图1](assets/Screenshot-1.png "输出截图")

Binary file added assets/Screenshot-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/img.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/img_1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/img_2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added example/icons.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 changes: 7 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
module go-sprite-split

go 1.18

require github.com/disintegration/imaging v1.6.2

require golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 // indirect
5 changes: 5 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
28 changes: 28 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package main

import (
"flag"
"fmt"
"go-sprite-split/src/go-sprite-split"
"time"
)

func main() {
start := time.Now()
var inputImgPath string
var outputFolderPath string
var mode string
flag.StringVar(&inputImgPath, "input", "example/rounded-rectangles.png", "the path of source image to split")
flag.StringVar(&outputFolderPath, "output", "output", "the path to save the sprites")
flag.StringVar(&mode, "mode", "sprite", "sprite: output json file; split: output split images")
flag.Parse()
fmt.Println("input path:", inputImgPath)
fmt.Println("output path:", outputFolderPath)
fmt.Println("parsing image ...")
ok := go_sprite_split.Process(inputImgPath, outputFolderPath, mode)
if ok == true {
fmt.Printf("finished! (in %s)", time.Since(start))
} else {
fmt.Printf("failure, please chekck the parameters and try again. (in %s)", time.Since(start))
}
}
Binary file added output/1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added output/10.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added output/11.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added output/12.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added output/13.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added output/14.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added output/15.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added output/16.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added output/17.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added output/18.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added output/19.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added output/2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added output/20.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added output/21.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added output/22.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added output/23.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added output/24.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added output/25.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added output/26.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added output/27.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added output/28.png
Binary file added output/29.png
Binary file added output/3.png
Binary file added output/30.png
Binary file added output/31.png
Binary file added output/32.png
Binary file added output/33.png
Binary file added output/4.png
Binary file added output/5.png
Binary file added output/6.png
Binary file added output/7.png
Binary file added output/8.png
Binary file added output/9.png
1 change: 1 addition & 0 deletions output/icons.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"1":{"h":1000,"w":2,"x1":0,"x2":1,"y1":0,"y2":999},"10":{"h":133,"w":135,"x1":420,"x2":554,"y1":760,"y2":892},"11":{"h":137,"w":107,"x1":425,"x2":531,"y1":562,"y2":698},"12":{"h":93,"w":153,"x1":489,"x2":641,"y1":111,"y2":203},"13":{"h":207,"w":193,"x1":538,"x2":730,"y1":304,"y2":510},"14":{"h":133,"w":135,"x1":589,"x2":723,"y1":760,"y2":892},"15":{"h":137,"w":108,"x1":598,"x2":705,"y1":562,"y2":698},"16":{"h":92,"w":244,"x1":684,"x2":927,"y1":112,"y2":203},"17":{"h":246,"w":279,"x1":756,"x2":1034,"y1":265,"y2":510},"18":{"h":119,"w":118,"x1":766,"x2":883,"y1":571,"y2":689},"19":{"h":135,"w":119,"x1":766,"x2":884,"y1":759,"y2":893},"2":{"h":117,"w":111,"x1":98,"x2":208,"y1":113,"y2":229},"20":{"h":135,"w":125,"x1":932,"x2":1056,"y1":759,"y2":893},"21":{"h":109,"w":113,"x1":942,"x2":1054,"y1":576,"y2":684},"22":{"h":79,"w":179,"x1":987,"x2":1165,"y1":432,"y2":510},"23":{"h":185,"w":184,"x1":992,"x2":1175,"y1":112,"y2":296},"24":{"h":33,"w":36,"x1":1104,"x2":1139,"y1":772,"y2":804},"25":{"h":111,"w":126,"x1":1109,"x2":1234,"y1":575,"y2":685},"26":{"h":35,"w":32,"x1":1109,"x2":1140,"y1":851,"y2":885},"27":{"h":78,"w":77,"x1":1127,"x2":1203,"y1":794,"y2":871},"28":{"h":35,"w":33,"x1":1185,"x2":1217,"y1":767,"y2":801},"29":{"h":32,"w":36,"x1":1187,"x2":1222,"y1":849,"y2":880},"3":{"h":119,"w":111,"x1":98,"x2":208,"y1":767,"y2":885},"30":{"h":137,"w":198,"x1":1204,"x2":1401,"y1":374,"y2":510},"31":{"h":180,"w":179,"x1":1223,"x2":1401,"y1":112,"y2":291},"32":{"h":133,"w":136,"x1":1264,"x2":1399,"y1":760,"y2":892},"33":{"h":123,"w":114,"x1":1288,"x2":1401,"y1":569,"y2":691},"4":{"h":117,"w":110,"x1":99,"x2":208,"y1":331,"y2":447},"5":{"h":118,"w":110,"x1":99,"x2":208,"y1":549,"y2":666},"6":{"h":97,"w":175,"x1":251,"x2":425,"y1":107,"y2":203},"7":{"h":252,"w":229,"x1":251,"x2":479,"y1":259,"y2":510},"8":{"h":137,"w":108,"x1":251,"x2":358,"y1":562,"y2":698},"9":{"h":133,"w":135,"x1":251,"x2":385,"y1":760,"y2":892}}
121 changes: 121 additions & 0 deletions src/go-sprite-split/func.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package go_sprite_split

import (
"encoding/json"
"fmt"
"github.com/disintegration/imaging"
"image"
"image/draw"
_ "image/png"
"os"
"path/filepath"
"strconv"
"strings"
)

func Process(inputImgPath, outputFolderPath, mode string) bool {
file, err := os.Open(inputImgPath)
if err != nil {
println("file can not be found! abort.")
return false
}
defer file.Close()
img, msg, err := image.Decode(file)
if err != nil {
println("file can not be decode, abort.", msg)
return false
}
sprite := make(map[string]interface{})

bounds := img.Bounds()
mask := make([][]bool, bounds.Dx())
for i := range mask {
mask[i] = make([]bool, bounds.Dy())
}

counter := 1
for x := 0; x < bounds.Dx(); x++ {
for y := 0; y < bounds.Dy(); y++ {
if _, _, _, a := img.At(x, y).RGBA(); a > 0 && !mask[x][y] {
minX, minY, maxX, maxY := dfs(x, y, img, mask)

if mode == "split" {
rect := image.Rect(minX, minY, maxX+1, maxY+1)
subImg := image.NewRGBA(rect)
draw.Draw(subImg, rect, img, rect.Min, draw.Src)
savaPath := fmt.Sprintf("%s/%d.png", outputFolderPath, counter)
fmt.Printf("saving %s \n", savaPath)
imaging.Save(subImg, savaPath)
} else if mode == "sprite" {
sprite[strconv.Itoa(counter)] = map[string]int{
"x1": minX,
"y1": minY,
"x2": maxX,
"y2": maxY,
"w": maxX - minX + 1,
"h": maxY - minY + 1,
}
}
counter++
}
}
}
fmt.Printf("number of sprite: %d\n", counter)
filename := filepath.Base(inputImgPath)
extension := filepath.Ext(filename)
newFilename := strings.TrimSuffix(filename, extension) + ".json"
outPath := fmt.Sprintf("%s/%s", outputFolderPath, newFilename)
outFile, _ := os.OpenFile(outPath, os.O_CREATE|os.O_WRONLY, 0666)
defer file.Close()
encoder := json.NewEncoder(outFile)
err = encoder.Encode(sprite)
if err != nil {
fmt.Println("encode and write output: fail", err)
return false
}
fmt.Println("encode and write output: success")
return true
}

func dfs(x, y int, img image.Image, mask [][]bool) (minX, minY, maxX, maxY int) {
dx := []int{-1, 0, 1, 0}
dy := []int{0, 1, 0, -1}

stack := [][]int{{x, y}}
mask[x][y] = true

minX, minY = x, y
maxX, maxY = x, y

for len(stack) > 0 {
x, y = stack[len(stack)-1][0], stack[len(stack)-1][1]
stack = stack[:len(stack)-1]

for i := 0; i < 4; i++ {
nx, ny := x+dx[i], y+dy[i]

_, _, _, a := img.At(x, y).RGBA()
if nx >= 0 && nx < len(mask) && ny >= 0 && ny < len(mask[0]) &&
!mask[nx][ny] && a > 0 {

mask[nx][ny] = true
stack = append(stack, []int{nx, ny})

if nx < minX {
minX = nx
}
if nx > maxX {
maxX = nx
}
if ny < minY {
minY = ny
}
if ny > maxY {
maxY = ny
}
}
}
}

return
}

0 comments on commit 288f9ca

Please sign in to comment.