-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 288f9ca
Showing
49 changed files
with
242 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 "输出截图") | ||
|
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) | ||
} | ||
} |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.
Oops, something went wrong.
Oops, something went wrong.
Oops, something went wrong.
Oops, something went wrong.
Oops, something went wrong.
Oops, something went wrong.
Oops, something went wrong.
Oops, something went wrong.
Oops, something went wrong.
Oops, something went wrong.
Oops, something went wrong.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |