Mindustry/tools/build.gradle
2022-08-29 12:08:12 -04:00

360 lines
12 KiB
Groovy

sourceSets.main.java.srcDirs = ["src/"]
import arc.files.Fi
import arc.files.ZipFi
import arc.func.Func2
import arc.graphics.Color
import arc.graphics.Pixmap
import arc.packer.TexturePacker
import arc.struct.ObjectMap
import arc.struct.OrderedMap
import arc.struct.Seq
import arc.util.Http
import arc.util.Log
import arc.util.OS
import arc.util.Threads
import arc.util.io.PropertiesUtils
import arc.util.io.Streams
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
def genFolder = "../core/assets-raw/sprites_out/generated/"
def enableAA = true
@groovy.transform.CompileStatic
static int getRGB(Pixmap image, int ix, int iy) {
return image.getRaw(Math.max(Math.min(ix, image.width - 1), 0), Math.max(Math.min(iy, image.height - 1), 0))
}
@groovy.transform.CompileStatic
static void antialias(File file){
Pixmap image = new Pixmap(new Fi(file))
Pixmap out = image.copy()
Color color = new Color()
Color sum = new Color()
Color suma = new Color()
int[] p = new int[9]
for(int x = 0; x < image.width; x++){
for(int y = 0; y < image.height; y++){
int A = getRGB(image, x - 1, y + 1),
B = getRGB(image, x, y + 1),
C = getRGB(image, x + 1, y + 1),
D = getRGB(image, x - 1, y),
E = getRGB(image, x, y),
F = getRGB(image, x + 1, y),
G = getRGB(image, x - 1, y - 1),
H = getRGB(image, x, y - 1),
I = getRGB(image, x + 1, y - 1)
Arrays.fill(p, E)
if(D == B && D != H && B != F) p[0] = D
if((D == B && D != H && B != F && E != C) || (B == F && B != D && F != H && E != A)) p[1] = B
if(B == F && B != D && F != H) p[2] = F
if((H == D && H != F && D != B && E != A) || (D == B && D != H && B != F && E != G)) p[3] = D
if((B == F && B != D && F != H && E != I) || (F == H && F != B && H != D && E != C)) p[5] = F
if(H == D && H != F && D != B) p[6] = D
if((F == H && F != B && H != D && E != G) || (H == D && H != F && D != B && E != I)) p[7] = H
if(F == H && F != B && H != D) p[8] = F
suma.set(0)
for(int val : p){
color.rgba8888(val)
color.premultiplyAlpha()
suma.r(suma.r + color.r)
suma.g(suma.g + color.g)
suma.b(suma.b + color.b)
suma.a(suma.a + color.a)
}
float fm = suma.a <= 0.001f ? 0f : (float)(1f / suma.a)
suma.mul(fm, fm, fm, fm)
float total = 0
sum.set(0)
for(int val : p){
color.rgba8888(val)
float a = color.a
color.lerp(suma, (float) (1f - a))
sum.r(sum.r + color.r)
sum.g(sum.g + color.g)
sum.b(sum.b + color.b)
sum.a(sum.a + a)
total += 1f
}
fm = (float)(1f / total)
sum.mul(fm, fm, fm, fm)
out.setRaw(x, y, sum.rgba8888())
sum.set(0)
}
}
image.dispose()
out.dispose()
new Fi(file).writePng(out)
}
task antialiasImages(){
doLast{
for(def img : project.getProperty("images").split(",")){
println(project.getProperty("startdir") + "/" + img)
antialias(new File(project.getProperty("startdir") + "/" + img))
}
}
}
task tileImages(){
doLast{
for(def img : project.getProperty("images").split(",")){
println(project.getProperty("startdir") + "/" + img)
tileImage(new File(project.getProperty("startdir") + "/" + img))
}
}
}
task pack(dependsOn: [classes, configurations.runtimeClasspath]){
doLast{
//cleanup old sprites
delete{
delete "../core/assets-raw/sprites_out/"
}
//copy in new sprites
copy{
from "../core/assets-raw/sprites/"
into "../core/assets-raw/sprites_out/"
}
//run generation task; generate all needed sprites
file(genFolder).mkdirs()
javaexec{
main = "mindustry.tools.ImagePacker"
classpath = sourceSets.main.runtimeClasspath
workingDir = genFolder
}
copy{
from "../core/assets-raw/sprites_out/ui/icons"
into "../core/assets-raw/sprites_out/ui/"
}
delete{
delete "../core/assets-raw/sprites_out/ui/icons"
}
if(enableAA){
ExecutorService executor = Executors.newFixedThreadPool(OS.cores)
long ms = System.currentTimeMillis()
//antialias everything except UI elements
fileTree(dir: new File(rootDir, 'core/assets-raw/sprites_out/').absolutePath, include: "**/*.png").visit{ file ->
if(file.isDirectory() || (file.toString().replace("\\", "/").contains("/ui/") && file.toString().startsWith("icon-")) || file.toString().contains(".9.png") || file.toString().contains("aaaa")) return
executor.submit{
antialias(file.file)
}
}
Threads.await(executor)
println "Time taken for AA: ${(System.currentTimeMillis() - ms) / 1000f} seconds"
}
println("\n\nPacking normal 4096 sprites...\n\n")
//pack normal sprites
TexturePacker.process(new File(rootDir, "core/assets-raw/sprites_out/").absolutePath, new File(rootDir, "core/assets/sprites/").absolutePath, "sprites.aatls")
println("\n\nPacking fallback 2048 sprites...\n\n")
//replace config file contents
fileTree(dir: '../core/assets-raw/sprites_out/', include: "**/*.json").visit{ file ->
if(!file.isDirectory()) file.file.text = file.file.text.replace("4096", "2048")
}
//pack fallback 2048x2048 sprites - disabled when debugging
if(!project.hasProperty("args")){
TexturePacker.process(new File(rootDir, "core/assets-raw/sprites_out/").absolutePath, new File(rootDir, "core/assets/sprites/fallback/").absolutePath, "sprites.aatls")
}
}
}
task genSprites(dependsOn: classes, type: JavaExec){
finalizedBy 'antialiasGen'
mainClass = "mindustry.tools.ImagePacker"
classpath = sourceSets.main.runtimeClasspath
standardInput = System.in
workingDir = genFolder
}
task fontgen(dependsOn: classes, type: JavaExec){
/* icon font pipeline:
1. take set of pre-defined icons and SVGs
2. use Fontello API to get a font with these
3. combine fontello font and standard font, get output font
4. use json to generate a file with constants for every icon size+type (during annotation processing)
*/
doLast{
Fi folder = Fi.get("core/assets-raw/fontgen/out/")
folder.mkdirs()
Log.info("Session...")
OS.exec("curl", "--fail", "--output", "core/assets-raw/fontgen/out/session", "--form", "config=@core/assets-raw/fontgen/config.json", "https://fontello.com")
Log.info("Zip...")
String session = folder.child("session").readString()
Http.get("https://fontello.com/" + session + "/get").block(result -> {
Streams.copy(result.getResultAsStream(), folder.child("font.zip").write())
})
Log.info("Icon font...")
ZipFi zip = new ZipFi(folder.child("font.zip"))
Fi dest = folder.child("font.woff")
zip.list()[0].child("font").child("fontello.ttf").copyTo(dest)
dest.copyTo(Fi.get("core/assets/fonts/icon.ttf"))
Log.info("Merge...")
//TODO this is broken
Log.info(OS.exec("fontforge", "-script",
Fi.get("core/assets-raw/fontgen/merge.pe").absolutePath(),
Fi.get("core/assets/fonts/font.woff").absolutePath(),
Fi.get("core/assets-raw/fontgen/out/font.woff").absolutePath())
)
Log.info("Done.")
}
}
task icongen(dependsOn: classes, type: JavaExec){
mainClass = "mindustry.tools.IconConverter"
classpath = sourceSets.main.runtimeClasspath
standardInput = System.in
workingDir = "../core/assets-raw"
}
task updateScripts(dependsOn: classes, type: JavaExec){
mainClass = "mindustry.tools.ScriptMainGenerator"
classpath = sourceSets.main.runtimeClasspath
standardInput = System.in
workingDir = "../"
}
task updateBundles{
doLast{
def uniEscape = { String string ->
StringBuilder outBuffer = new StringBuilder()
int len = string.length()
for(int i = 0; i < len; i++){
char ch = string.charAt(i)
if((ch > 61) && (ch < 127)){
outBuffer.append(ch == (char)'\\' ? "\\\\" : ch)
continue
}
if(ch >= 0xE000 && ch <= 0xF8FF){
String hex = Integer.toHexString((int)ch)
outBuffer.append("\\u")
for(int j = 0; j < 4 - hex.length(); j++){
outBuffer.append('0')
}
outBuffer.append(hex)
}else{
outBuffer.append(ch)
}
}
return outBuffer.toString()
}
OrderedMap<String, String> base = new OrderedMap<>()
PropertiesUtils.load(base, Fi.get("core/assets/bundles/bundle.properties").reader())
Seq<String> removals = new Seq<>()
Log.info("Updating bundles...")
Fi.get("core/assets/bundles").walk(child -> {
if(child.name().equals("bundle.properties") || child.toString().contains("output")) return
Log.info("| @", child.nameWithoutExtension())
OrderedMap<String, String> other = new OrderedMap<>()
//find the last known comment of each line
ObjectMap<String, String> comments = new ObjectMap<>()
StringBuilder curComment = new StringBuilder()
for(String line : Seq.with(child.readString().split("\n", -1))){
if(line.startsWith("#") || line.isEmpty()){
curComment.append(line).append("\n")
}else if(line.contains("=")){
String lastKey = line.substring(0, line.indexOf("=")).trim()
if(curComment.length() != 0){
comments.put(lastKey, curComment.toString())
curComment.setLength(0)
}
}
}
ObjectMap<String, String> extras = new OrderedMap<>()
PropertiesUtils.load(other, child.reader())
removals.clear()
for(String key : other.orderedKeys()){
if(!base.containsKey(key) && key.contains(".details") && false){
extras.put(key, other.get(key))
}else if(!base.containsKey(key)){
removals.add(key)
Log.info("&lr- Removing unused key '@'...", key)
}
}
if(removals.size > 0) Log.info("&lr@ keys removed.", removals.size)
for(String s : removals){
other.remove(s)
}
int added = 0
for(String key : base.orderedKeys()){
if(other.get(key) == null || other.get(key).trim().isEmpty()){
other.put(key, base.get(key))
added++
Log.info("&lc- Adding missing key '@'...", key)
}
}
Func2<String, String, String> processor = (key, value) ->
(comments.containsKey(key) ? comments.get(key) : "") + //append last known comment if present
(key + " =" + (value.trim().isEmpty() ? "" : " ") + uniEscape(value)).replace("\n", "\\n") + "\n"
Fi output = child.sibling("output/" + child.name())
if(added > 0) Log.info("&lc@ keys added.", added)
if(removals.size + added > 0) Log.info("Writing bundle to @", output)
StringBuilder result = new StringBuilder()
//add everything ordered
for(String key : base.orderedKeys().copy().add(extras.keys().toSeq())){
if(other.get(key) == null) continue
result.append(processor.get(key, other.get(key)))
other.remove(key)
}
child.writeString(result.toString())
})
}
}