Skip to content

Instantly share code, notes, and snippets.

@Varriount
Created June 28, 2012 02:17
Show Gist options
  • Save Varriount/3008342 to your computer and use it in GitHub Desktop.
Save Varriount/3008342 to your computer and use it in GitHub Desktop.
SkinPacker - fixed version for null element cases.
/*******************************************************************************
* Copyright 2011 See AUTHORS file.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/
package com.badlogic.gdx.tools.skins;
import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.Writer;
import java.util.concurrent.CountDownLatch;
import javax.imageio.ImageIO;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.backends.jogl.JoglApplication;
import com.badlogic.gdx.backends.jogl.JoglApplicationConfiguration;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Pixmap.Format;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.Texture.TextureFilter;
import com.badlogic.gdx.graphics.g2d.NinePatch;
import com.badlogic.gdx.graphics.g2d.TextureAtlas.TextureAtlasData;
import com.badlogic.gdx.graphics.g2d.TextureAtlas.TextureAtlasData.Region;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.tools.FileProcessor;
import com.badlogic.gdx.tools.imagepacker.TexturePacker;
import com.badlogic.gdx.tools.imagepacker.TexturePacker.Settings;
import com.badlogic.gdx.utils.GdxRuntimeException;
import com.badlogic.gdx.utils.Json;
import com.badlogic.gdx.utils.ObjectMap;
import com.badlogic.gdx.utils.OrderedMap;
public class SkinPacker {
static public void process (Settings settings, final File inputDir, final File skinFile, final File imageFile)
throws Exception {
Texture.setEnforcePotImages(false);
final File packedDir = new File("temp-packed");
new FileHandle(packedDir).deleteDirectory();
imageFile.delete();
final ObjectMap<String, Splits> nameToSplits = new ObjectMap();
final TexturePacker texturePacker = new TexturePacker(settings);
FileProcessor regionProcessor = new FileProcessor() {
protected void processFile (InputFile inputFile) throws Exception {
texturePacker.addImage(ImageIO.read(inputFile.inputFile), inputFile.outputFile.getName());
}
};
regionProcessor.setRecursive(false);
regionProcessor.setInputFilter(new FilenameFilter() {
public boolean accept (File dir, String name) {
return name.endsWith(".png") && !name.endsWith(".9.png");
}
});
regionProcessor.setOutputSuffix("");
regionProcessor.process(inputDir, inputDir);
FileProcessor ninePatchProcessor = new FileProcessor() {
protected void processFile (InputFile inputFile) throws Exception {
BufferedImage image = ImageIO.read(inputFile.inputFile);
String name = inputFile.outputFile.getName();
name = name.substring(0, name.length() - 2);
image = split(image, name);
texturePacker.addImage(image, name);
}
private BufferedImage split (BufferedImage image, String name) {
nameToSplits.put(name, getSplits(image, name));
BufferedImage newImage = new BufferedImage(image.getWidth() - 2, image.getHeight() - 2, BufferedImage.TYPE_4BYTE_ABGR);
newImage.getGraphics().drawImage(image, 0, 0, newImage.getWidth(), newImage.getHeight(), 1, 1, image.getWidth() - 1,
image.getHeight() - 1, null);
return newImage;
}
};
ninePatchProcessor.setRecursive(false);
ninePatchProcessor.addInputSuffix(".9.png");
ninePatchProcessor.setOutputSuffix("");
ninePatchProcessor.process(inputDir, inputDir);
final File packFile = new File(packedDir, "pack");
texturePacker.process(packedDir, packFile, "skin");
if (!packFile.exists())
throw new RuntimeException("No images were packed from input directory: " + inputDir.getAbsolutePath());
JoglApplicationConfiguration config = new JoglApplicationConfiguration();
config.width = 1;
config.height = 1;
config.title = "SkinPacker";
final CountDownLatch latch = new CountDownLatch(2);
new JoglApplication(new ApplicationListener() {
public void create () {
try {
Skin skin = new Skin();
TextureAtlasData atlas = new TextureAtlasData(new FileHandle(packFile), new FileHandle(packedDir), true);
if (atlas.getPages().size > 1)
throw new GdxRuntimeException("Skin images could not be packed on to a single image!");
Texture texture = new Texture(1, 1, Format.Alpha);
for (Region region : atlas.getRegions()) {
Splits splits = nameToSplits.get(region.name);
TextureRegion textureRegion = new TextureRegion(texture, region.left, region.top, region.width, region.height);
if (splits == null) {
skin.addResource(region.name, textureRegion);
} else {
if (splits.singleRegion)
skin.addResource(region.name, new NinePatch(textureRegion));
else {
skin.addResource(region.name, new NinePatch(textureRegion, splits.startX, region.width - splits.endX,
splits.startY, region.height - splits.endY));
}
}
}
FileHandle newSkinFile = new FileHandle(new File(inputDir, "temp-skin"));
skin.save(newSkinFile);
atlas.getPages().get(0).textureFile.moveTo(new FileHandle(imageFile));
new FileHandle(packedDir).deleteDirectory();
Json json = new Json();
if (skinFile != null) {
FileHandle oldSkinFile = new FileHandle(skinFile);
OrderedMap oldSkin = json.fromJson(OrderedMap.class, oldSkinFile);
OrderedMap newSkin = json.fromJson(OrderedMap.class, newSkinFile);
OrderedMap oldResources = (OrderedMap)oldSkin.get("resources");
OrderedMap newResources = (OrderedMap)newSkin.get("resources");
if (newResources != null) {
OrderedMap newPatches = (OrderedMap)newResources.get(NinePatch.class.getName());
if (newPatches != null) {
newPatches.orderedKeys().sort();
oldResources.put(NinePatch.class.getName(), newPatches);}
OrderedMap newRegions = (OrderedMap)newResources.get(TextureRegion.class.getName());
if (newRegions != null) {
newRegions.orderedKeys().sort();
oldResources.put(TextureRegion.class.getName(), newRegions);
}}
Writer writer = oldSkinFile.writer(false);
try {
writer.write(json.prettyPrint(oldSkin, 130));
writer.close();
} catch (IOException ex) {
throw new RuntimeException(ex);
}
} else {
newSkinFile.moveTo(new FileHandle(inputDir).child("skin.json"));
}
newSkinFile.delete();
} finally {
Gdx.app.exit();
latch.countDown();
}
}
public void resume () {
}
public void resize (int width, int height) {
}
public void render () {
}
public void pause () {
}
public void dispose () {
}
}, config);
latch.countDown();
latch.await();
}
static public Splits getSplits (BufferedImage image, String name) {
WritableRaster raster = image.getRaster();
int[] rgba = new int[4];
int startX = 1;
for (int x = 1; x < raster.getWidth() - 1; x++) {
raster.getPixel(x, 0, rgba);
if (rgba[3] == 0) continue;
if (rgba[0] != 0 || rgba[1] != 0 || rgba[2] != 0) throw new RuntimeException("Unknown pixel:" + x + ",0: " + name);
startX = x;
break;
}
int endX;
for (endX = startX; endX < raster.getWidth() - 1; endX++) {
raster.getPixel(endX, 0, rgba);
if (rgba[3] == 0) break;
if (rgba[0] != 0 || rgba[1] != 0 || rgba[2] != 0) throw new RuntimeException("Unknown pixel " + endX + ",0: " + name);
}
for (int x = endX + 1; x < raster.getWidth() - 1; x++) {
raster.getPixel(x, 0, rgba);
if (rgba[3] != 0) throw new RuntimeException("Unknown pixel " + x + ",0: " + name);
}
int startY = 1;
for (int y = 1; y < raster.getHeight() - 1; y++) {
raster.getPixel(0, y, rgba);
if (rgba[3] == 0) continue;
if (rgba[0] != 0 || rgba[1] != 0 || rgba[2] != 0) throw new RuntimeException("Unknown pixel: 0," + y + ": " + name);
startY = y;
break;
}
int endY;
for (endY = startY; endY < raster.getHeight() - 1; endY++) {
raster.getPixel(0, endY, rgba);
if (rgba[3] == 0) break;
if (rgba[0] != 0 || rgba[1] != 0 || rgba[2] != 0) throw new RuntimeException("Unknown pixel 0," + endY + ": " + name);
}
for (int y = endY + 1; y < raster.getHeight() - 1; y++) {
raster.getPixel(0, y, rgba);
if (rgba[3] != 0) throw new RuntimeException("Unknown pixel 0," + y + ": " + name);
}
// No splits, or all splits.
boolean singleRegion = startX == 1 && endX == 1 && startY == 1 && endY == 1;
if (singleRegion) {
endX = raster.getWidth();
endY = raster.getHeight();
}
Splits splits = new Splits();
splits.startX = startX - 1;
splits.endX = endX - 1;
splits.startY = startY - 1;
splits.endY = endY - 1;
splits.singleRegion = singleRegion;
return splits;
}
static public class Splits {
public int startX, endX;
public int startY, endY;
public boolean singleRegion;
}
static public void main (String[] args) throws Exception {
File inputDir = new File(args[0]);
File skinFile = new File(args[1]);
File imageFile = new File(args[2]);
Settings settings = new Settings();
settings.defaultFilterMag = TextureFilter.Linear;
settings.defaultFilterMin = TextureFilter.Linear;
settings.duplicatePadding = false;
SkinPacker.process(settings, inputDir, skinFile, imageFile);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment