sourceSets.main.java.srcDirs = ["src/"] import arc.struct.* import arc.graphics.* import arc.packer.* import arc.util.Tmp import javax.imageio.ImageIO import java.awt.* import java.awt.image.BufferedImage import java.util.List import java.util.concurrent.ExecutorService import java.util.concurrent.Executors import java.util.concurrent.TimeUnit def genFolder = "../core/assets-raw/sprites_out/generated/" def doAntialias = !project.hasProperty("disableAntialias") def colorMap = new IntMap>(), colorIndexMap = new IntIntMap() //on my machine, I have a native Nim AA implementation that is ~10x faster //it's not compiled for other platforms so they don't get it def useFastAA = System.getProperty("user.name") == "anuke" def transformColors = { List> list -> list.each{ colors -> def newColors = [] colors.each{ hexc -> newColors += Color.valueOf(hexc) } newColors.each{ color -> colorMap.put(color.argb8888(), newColors) colorIndexMap.put(color.argb8888(), newColors.indexOf(color)) } } } //d4816b transformColors([["a387ea", "8a73c6", "5c5e9f"], ["6e7080", "989aa4", "b0bac0"], ["bc5452", "ea8878", "feb380"], ["de9458", "f8c266", "ffe18f"], ["feb380", "ea8878", "bc5452"], ["d4816b", "eab678", "ffd37f"]]) def antialias = { File file -> if(!doAntialias) return if(useFastAA){ "antialias ${file.absolutePath}".execute().waitFor() return } def image = ImageIO.read(file) def out = ImageIO.read(file) def getRGB = { int ix, int iy -> //if(ix <= 0 || iy <= 0 || ix >= image.width || iy >= image.height) return 0 return image.getRGB(Math.max(Math.min(ix, image.width - 1), 0), Math.max(Math.min(iy, image.height - 1), 0)) } def color = new Color() def sum = new Color() def suma = new Color() int[] p = new int[9] for(int x = 0; x < image.getWidth(); x++){ for(int y = 0; y < image.getHeight(); y++){ int A = getRGB(x - 1, y + 1), B = getRGB(x, y + 1), C = getRGB(x + 1, y + 1), D = getRGB(x - 1, y), E = getRGB(x, y), F = getRGB(x + 1, y), G = getRGB(x - 1, y - 1), H = getRGB(x, y - 1), I = getRGB(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.argb8888(val) suma.r += color.r * color.a suma.g += color.g * color.a suma.b += color.b * color.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.argb8888(val) float a = color.a color.lerp(suma, (float) (1f - a)) sum.r += color.r sum.g += color.g sum.b += color.b sum.a += a total += 1f } fm = (float)(1f / total) sum.mul(fm, fm, fm, fm) out.setRGB(x, y, sum.argb8888()) sum.set(0) } } ImageIO.write(out, "png", file) } def medianBlur = { File file -> def image = ImageIO.read(file) def result = new BufferedImage(image.width, image.height, BufferedImage.TYPE_INT_ARGB) def radius = 4 IntSeq array = new IntSeq() def getRGB = { int ix, int iy -> return image.getRGB(Math.max(Math.min(ix, image.width - 1), 0), Math.max(Math.min(iy, image.height - 1), 0)) } for(int x = 0; x < image.width; x++){ for(int y = 0; y < image.height; y++){ array.clear() for(int dx = -radius; dx <= radius; dx ++){ for(int dy = -radius; dy <= radius; dy ++){ if(dx*dx + dy*dy <= radius*radius){ array.add(getRGB(x + dx, y + dy)) } } } array.sort() result.setRGB(x, y, array.get((int)(array.size / 2))) } } ImageIO.write(result, "png", file) } def scaleImage = { File file -> def image = ImageIO.read(file) for(int iteration in 0..1){ def scaled = new BufferedImage(image.width * 2, image.height * 2, BufferedImage.TYPE_INT_ARGB) def getRGB = { int ix, int iy -> return image.getRGB(Math.max(Math.min(ix, image.width - 1), 0), Math.max(Math.min(iy, image.height - 1), 0)) } for(int x = 0; x < image.width; x++){ for(int y = 0; y < image.height; y++){ int p = image.getRGB(x, y) int p1 = p, p2 = p, p3 = p, p4 = p int A = getRGB(x - 1, y + 1), B = getRGB(x, y + 1), C = getRGB(x + 1, y + 1), D = getRGB(x - 1, y), E = getRGB(x, y), F = getRGB(x + 1, y), G = getRGB(x - 1, y - 1), H = getRGB(x, y - 1), I = getRGB(x + 1, y - 1), J = getRGB(x, y + 2), K = getRGB(x - 2, y), L = getRGB(x + 2, y), M = getRGB(x, y - 2) if(B == D && B != F && D != H && (E != A || E == C || E == G || A == J || A == K)) p1 = B if(B == F && B != D && F != H && (E != C || E == A || E == I || C == J || C == L)) p2 = F if(D == H && B != D && F != H && (E != G || E == A || E == I || G == K || G == M)) p3 = D if(F == H && B != F && D != H && (E != I || E == C || E == G || I == L || I == M)) p4 = H scaled.setRGB(x * 2, y * 2 + 1, p1) scaled.setRGB(x * 2 + 1, y * 2 + 1, p2) scaled.setRGB(x * 2, y * 2, p3) scaled.setRGB(x * 2 + 1, y * 2, p4) } } image = scaled } ImageIO.write(image, "png", file) } def tileImage = { File file -> def image = ImageIO.read(file) for(x in 0..image.width-1){ for(y in 0..image.height-1){ if(x > (image.height - 1 - y)){ def rx = image.height - 1 - y def ry = x image.setRGB(x, y, image.getRGB(rx, image.height - 1 - ry)) } } } def result = new BufferedImage(image.width * 2, image.height * 2, image.getType()) Graphics2D graphics = result.createGraphics() graphics.drawImage(image, image.width, 0, -image.width, image.height, null) graphics.drawImage(image, image.width, 0, image.width, image.height, null) graphics.drawImage(image, image.width, image.height*2, -image.width, -image.height, null) graphics.drawImage(image, image.width, image.height*2, image.width, -image.height, null) for(int x = 0; x < result.width; x++){ for(int y = 0; y < result.height; y++){ int p = result.getRGB(x, y) if(x <= y){ List list = colorMap.get(p) int index = colorIndexMap.get(p, -1) if(index != -1){ int resultIndex = (x == y ? 1 : index == 2 ? 0 : index == 0 ? 2 : 1); result.setRGB(x, y, list[resultIndex].argb8888()) } } } } ImageIO.write(result, "png", file) } task swapColors(){ doLast{ if(project.hasProperty("colors")){ def carr = new File(getProperty("colors")).text.split("\n") def map = [:] def swaps = 0 carr.each{ str -> map[Color.valueOf(str.split("=")[0]).argb8888()] = Color.valueOf(str.split("=")[1]).argb8888() } def tmpc = new Color() fileTree(dir: '../core/assets-raw/sprites', include: "**/*.png").visit{ file -> if(file.isDirectory()) return boolean found = false def img = ImageIO.read(file.file) for(x in (0..img.getWidth() - 1)){ for(y in (0..img.getHeight() - 1)){ def c = img.getRGB(x, y) tmpc.argb8888(c) if(tmpc.a < 0.1f) continue if(map.containsKey(c)){ img.setRGB(x, y, (int)map.get(c)) found = true } } } if(found){ swaps++ ImageIO.write(img, "png", file.file) } } println "Swapped $swaps images." }else{ throw new InvalidUserDataException("No replacement colors specified. Use -Pcolors=\"\"") } } } task genPalette(){ doLast{ def total = 0 def size = 32 def outImage = new BufferedImage(size, size, BufferedImage.TYPE_INT_ARGB) def colorsUsed = new IntSet() fileTree(dir: '../core/assets-raw/sprites/blocks', include: "**/*.png").visit{ file -> if(file.isDirectory()) return def img = ImageIO.read(file.file) for(x in (0..img.getWidth() - 1)){ for(y in (0..img.getHeight() - 1)){ def c = img.getRGB(x, y) if(Tmp.c1.argb8888(c).a > 0.999f && colorsUsed.add(c)){ outImage.setRGB((int)(total / size), total % size, c) total ++ } } } } ImageIO.write(outImage, "png", new File("palette.png")) println "Found $total colors." } } task antialiasImages(){ doLast{ for(def img : project.getProperty("images").split(",")){ println(project.getProperty("startdir") + "/" + img) antialias(new File(project.getProperty("startdir") + "/" + img)) } } } task scaleImages(){ doLast{ for(def img : project.getProperty("images").split(",")){ println(project.getProperty("startdir") + "/" + img) scaleImage(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){ 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" } ExecutorService executor = Executors.newFixedThreadPool(16) long ms = System.currentTimeMillis() //antialias everything except UI elements fileTree(dir: '../core/assets-raw/sprites_out/', include: "**/*.png").visit{ file -> if(file.isDirectory() || (file.toString().replace("\\", "/").contains("/ui/") && file.toString().startsWith("icon-")) || file.toString().contains(".9.png")) return executor.submit{ antialias(file.file) } } executor.shutdown() try{ executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS) }catch(InterruptedException e){ e.printStackTrace() } println "Time taken for AA: ${(System.currentTimeMillis() - ms) / 1000f}" println("\n\nPacking normal 4096 sprites...\n\n") //pack normal sprites TexturePacker.process("core/assets-raw/sprites_out/", "core/assets/sprites/", "sprites.atlas") 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 TexturePacker.process("core/assets-raw/sprites_out/", "core/assets/sprites/fallback/", "sprites.atlas") } } task genSprites(dependsOn: classes, type: JavaExec){ finalizedBy 'antialiasGen' main = "mindustry.tools.ImagePacker" classpath = sourceSets.main.runtimeClasspath jvmArgs("-Djava.awt.headless=true") standardInput = System.in workingDir = genFolder } task genSectorData(dependsOn: classes, type: JavaExec){ main = "mindustry.tools.SectorDataGenerator" classpath = sourceSets.main.runtimeClasspath standardInput = System.in workingDir = "../core/assets/" } task updateCache(dependsOn: [genSectorData]){ } task updateBundles(dependsOn: classes, type: JavaExec){ file(genFolder).mkdirs() main = "mindustry.tools.BundleLauncher" classpath = sourceSets.main.runtimeClasspath standardInput = System.in workingDir = "../core/assets/bundles/" } task fontgen(dependsOn: classes, type: JavaExec){ main = "mindustry.tools.FontGenerator" classpath = sourceSets.main.runtimeClasspath standardInput = System.in workingDir = "../" }