Mindustry/tools/build.gradle
buthed010203 594b0b5d6a
Better bundle comment handling (#10704)
* Better bundle comment handling

* Formatting
2025-04-21 23:07:54 -04:00

358 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.IntMap
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())
//map of line number to comment (or just empty string for blank line) content
IntMap<String> commentAndBlankLines = new IntMap<>()
var lines = Fi.get("core/assets/bundles/bundle.properties").reader().readLines()
int offset = 0
for(int i = 0; i < lines.size(); i++){
var line = lines.get(i)
if(i > 0 && lines.get(i - 1).endsWith("\\")) offset++ //multiline value escaped with \\ at end of line
else if(line.isEmpty() || line.startsWith("#")) commentAndBlankLines.put(i - offset, line)
}
Log.info("Updating bundles...")
Fi.get("core/assets/bundles").walk(child -> {
if(child.name() == "bundle.properties" || child.toString().contains("output")) return
if(project.hasProperty("bundle") && child.name() != project.property("bundle")) return
Log.info("| @", child.nameWithoutExtension())
OrderedMap<String, String> other = new OrderedMap<>()
ObjectMap<String, String> extras = new OrderedMap<>()
Seq<String> removals = new Seq<>()
PropertiesUtils.load(other, child.reader())
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) ->
(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()
int i = 0
//add everything ordered
for(String key : base.orderedKeys().copy().add(extras.keys().toSeq())){
//append any comments or blank lines as needed to match the english bundle
while(commentAndBlankLines.containsKey(i++)) result.append(commentAndBlankLines.get(i - 1) + "\n")
if(other.get(key) == null) continue
result.append(processor.get(key, other.get(key)))
other.remove(key)
}
child.writeString(result.toString())
})
}
}