--- /dev/null
+\r
+package com.badlogic.gdx.imagepacker;\r
+\r
+import java.awt.Color;\r
+import java.awt.Graphics;\r
+import java.awt.image.BufferedImage;\r
+import java.awt.image.WritableRaster;\r
+import java.io.File;\r
+import java.io.FileWriter;\r
+import java.io.FilenameFilter;\r
+import java.io.IOException;\r
+import java.util.ArrayList;\r
+import java.util.Arrays;\r
+import java.util.Collections;\r
+import java.util.Comparator;\r
+import java.util.Iterator;\r
+import java.util.regex.Matcher;\r
+import java.util.regex.Pattern;\r
+\r
+import javax.imageio.ImageIO;\r
+\r
+import com.badlogic.gdx.utils.MathUtils;\r
+\r
+public class SpriteSheetPacker {\r
+ static Pattern numberedImagePattern = Pattern.compile(".*?(\\d+)");\r
+\r
+ private ArrayList<Image> images = new ArrayList();\r
+ FileWriter writer;\r
+ final File inputDir;\r
+ private int uncompressedSize, compressedSize;\r
+ final Direction direction;\r
+ int xPadding, yPadding;\r
+ private final Filter filter;\r
+\r
+ // User configurable settings:\r
+ private int alphaThreshold = 11;\r
+ private boolean pot = true;\r
+ private int padding = 0;\r
+ private boolean debug = false;\r
+\r
+ public SpriteSheetPacker (File inputDir, Filter filter, Direction direction, File outputDir, File packFile) throws IOException {\r
+ this.inputDir = inputDir;\r
+ this.filter = filter;\r
+ this.direction = direction;\r
+\r
+ ArrayList<File> files = getFiles(inputDir, filter, direction);\r
+ if (files == null) return;\r
+\r
+ for (File file : files) {\r
+ if (file.isDirectory()) continue;\r
+ Image image = squeeze(file);\r
+ if (image != null) images.add(image);\r
+ }\r
+ if (images.isEmpty()) return;\r
+\r
+ System.out.println(inputDir);\r
+ if (filter != rgba8888) System.out.println("Format: " + filter.name);\r
+ if (direction != null) System.out.println("Direction: " + direction);\r
+ for (Image image : images)\r
+ System.out.println("Packing... " + image.file.getName());\r
+\r
+ Collections.sort(images, new Comparator<Image>() {\r
+ public int compare (Image image1, Image image2) {\r
+ return image1.getWidth() * image1.getHeight() - image2.getWidth() * image2.getHeight();\r
+ }\r
+ });\r
+\r
+ xPadding = images.size() > 1 && direction != Direction.x && direction != Direction.xy ? padding : 0;\r
+ yPadding = images.size() > 1 && direction != Direction.y && direction != Direction.xy ? padding : 0;\r
+\r
+ outputDir.mkdirs();\r
+ String prefix = inputDir.getParentFile().getName();\r
+\r
+ writer = new FileWriter(packFile, true);\r
+ try {\r
+ while (!images.isEmpty())\r
+ writePage(prefix, outputDir);\r
+ if (writer != null) {\r
+ System.out.println("Pixels eliminated: " + (1 - compressedSize / (float)uncompressedSize) * 100 + "%");\r
+ System.out.println();\r
+ }\r
+ } finally {\r
+ writer.close();\r
+ }\r
+ }\r
+\r
+ private void writePage (String prefix, File outputDir) throws IOException {\r
+ int imageNumber = 1;\r
+ File outputFile = new File(outputDir, prefix + imageNumber + ".png");\r
+ while (outputFile.exists())\r
+ outputFile = new File(outputDir, prefix + ++imageNumber + ".png");\r
+\r
+ writer.write("\n" + prefix + imageNumber + ".png\n");\r
+ writer.write(direction + "\n");\r
+\r
+ // Try reasonably hard to find the smallest size that is also the smallest POT.\r
+ Comparator bestComparator = null;\r
+ Comparator secondBestComparator = imageComparators.get(0);\r
+ int maxWidth = 1024, maxHeight = 1024;\r
+ int bestWidth = 99999, bestHeight = 99999;\r
+ int secondBestWidth = 99999, secondBestHeight = 99999;\r
+ int bestUsedPixels = 0;\r
+ int width = 64, height = 64;\r
+ int grownPixels = 0, grownPixels2 = 0;\r
+ int i = 0, ii = 0;\r
+ while (true) {\r
+ if (width > maxWidth && height > maxHeight) break;\r
+ for (Comparator comparator : imageComparators) {\r
+ Collections.sort(images, comparator);\r
+\r
+ int usedPixels = insert(null, new ArrayList(images), width, height);\r
+ if (usedPixels > bestUsedPixels) {\r
+ secondBestComparator = comparator;\r
+ secondBestWidth = width;\r
+ secondBestHeight = height;\r
+ }\r
+ if (usedPixels == -1) {\r
+ if (width * height < bestWidth * bestHeight) {\r
+ bestComparator = comparator;\r
+ bestWidth = width;\r
+ bestHeight = height;\r
+ }\r
+ }\r
+ }\r
+ if (bestComparator != null) break;\r
+ if (pot) {\r
+ if (i % 3 == 0) {\r
+ width *= 2;\r
+ i++;\r
+ } else if (i % 3 == 1) {\r
+ width /= 2;\r
+ height *= 2;\r
+ i++;\r
+ } else {\r
+ width *= 2;\r
+ i++;\r
+ }\r
+ } else {\r
+ if (i % 3 == 0) {\r
+ width++;\r
+ grownPixels++;\r
+ if (width == MathUtils.nextPowerOfTwo(width)) {\r
+ width -= grownPixels;\r
+ grownPixels = 0;\r
+ i++;\r
+ }\r
+ } else if (i % 3 == 1) {\r
+ height++;\r
+ grownPixels++;\r
+ if (height == MathUtils.nextPowerOfTwo(height)) {\r
+ height -= grownPixels;\r
+ grownPixels = 0;\r
+ i++;\r
+ }\r
+ } else {\r
+ if (width == MathUtils.nextPowerOfTwo(width) && height == MathUtils.nextPowerOfTwo(height)) ii++;\r
+ if (ii % 2 == 1)\r
+ width++;\r
+ else\r
+ height++;\r
+ i++;\r
+ }\r
+ }\r
+ }\r
+ if (bestComparator != null) {\r
+ Collections.sort(images, bestComparator);\r
+ } else {\r
+ Collections.sort(images, secondBestComparator);\r
+ bestWidth = secondBestWidth;\r
+ bestHeight = secondBestHeight;\r
+ }\r
+ width = bestWidth;\r
+ height = bestHeight;\r
+\r
+ if (pot) {\r
+ width = MathUtils.nextPowerOfTwo(width);\r
+ height = MathUtils.nextPowerOfTwo(height);\r
+ }\r
+\r
+ BufferedImage canvas = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);\r
+ insert(canvas, images, bestWidth, bestHeight);\r
+ System.out.println("Writing " + canvas.getWidth() + "x" + canvas.getHeight() + ": " + outputFile);\r
+ ImageIO.write(canvas, "png", outputFile);\r
+ compressedSize += canvas.getWidth() * canvas.getHeight();\r
+ }\r
+\r
+ private int insert (BufferedImage canvas, ArrayList<Image> images, int width, int height) throws IOException {\r
+ if (debug && canvas != null) {\r
+ Graphics g = canvas.getGraphics();\r
+ g.setColor(Color.green);\r
+ g.drawRect(0, 0, width - 1, height - 1);\r
+ }\r
+ // Pretend image is larger so padding on right and bottom edges is ignored.\r
+ if (direction != Direction.x && direction != Direction.xy) width += xPadding;\r
+ if (direction != Direction.y && direction != Direction.xy) height += yPadding;\r
+ Node root = new Node(0, 0, width, height);\r
+ int usedPixels = 0;\r
+ for (int i = images.size() - 1; i >= 0; i--) {\r
+ Image image = images.get(i);\r
+ Node node = root.insert(image, canvas);\r
+ if (node == null) continue;\r
+ usedPixels += image.getWidth() * image.getHeight();\r
+ images.remove(i);\r
+ if (canvas != null) {\r
+ Graphics g = canvas.getGraphics();\r
+ g.drawImage(image, node.left, node.top, null);\r
+ if (debug) {\r
+ g.setColor(Color.magenta);\r
+ g.drawRect(node.left, node.top, image.getWidth() - 1, image.getHeight() - 1);\r
+ }\r
+ }\r
+ }\r
+ return images.isEmpty() ? -1 : usedPixels;\r
+ }\r
+\r
+ private Image squeeze (File file) throws IOException {\r
+ BufferedImage source = ImageIO.read(file);\r
+ if (source == null) return null;\r
+ uncompressedSize += source.getWidth() * source.getHeight();\r
+ WritableRaster alphaRaster = source.getAlphaRaster();\r
+ if (alphaRaster == null) return new Image(file, source, 0, 0, source.getWidth(), source.getHeight());\r
+ final byte[] a = new byte[1];\r
+ int top = 0;\r
+ outer:\r
+ for (int y = 0; y < source.getHeight(); y++) {\r
+ for (int x = 0; x < source.getWidth(); x++) {\r
+ alphaRaster.getDataElements(x, y, a);\r
+ int alpha = a[0];\r
+ if (alpha < 0) alpha += 256;\r
+ if (alpha > alphaThreshold) break outer;\r
+ }\r
+ top++;\r
+ }\r
+ int bottom = source.getHeight() - 1;\r
+ outer:\r
+ for (int y = source.getHeight(); --y >= top;) {\r
+ for (int x = 0; x < source.getWidth(); x++) {\r
+ alphaRaster.getDataElements(x, y, a);\r
+ int alpha = a[0];\r
+ if (alpha < 0) alpha += 256;\r
+ if (alpha > alphaThreshold) break outer;\r
+ }\r
+ bottom--;\r
+ }\r
+ int left = 0;\r
+ outer:\r
+ for (int x = 0; x < source.getWidth(); x++) {\r
+ for (int y = top; y <= bottom; y++) {\r
+ alphaRaster.getDataElements(x, y, a);\r
+ int alpha = a[0];\r
+ if (alpha < 0) alpha += 256;\r
+ if (alpha > alphaThreshold) break outer;\r
+ }\r
+ left++;\r
+ }\r
+ int right = source.getWidth() - 1;\r
+ outer:\r
+ for (int x = source.getWidth(); --x >= left;) {\r
+ for (int y = top; y <= bottom; y++) {\r
+ alphaRaster.getDataElements(x, y, a);\r
+ int alpha = a[0];\r
+ if (alpha < 0) alpha += 256;\r
+ if (alpha > alphaThreshold) break outer;\r
+ }\r
+ right--;\r
+ }\r
+ int newWidth = right - left;\r
+ int newHeight = bottom - top;\r
+ if (newWidth <= 0 || newHeight <= 0) {\r
+ System.out.println("Ignoring blank input image: " + file.getAbsolutePath());\r
+ return null;\r
+ }\r
+ return new Image(file, source, left, top, newWidth, newHeight);\r
+ }\r
+\r
+ private class Node {\r
+ final int left, top, width, height;\r
+ Node child1, child2;\r
+ Image image;\r
+\r
+ public Node (int left, int top, int width, int height) {\r
+ this.left = left;\r
+ this.top = top;\r
+ this.width = width;\r
+ this.height = height;\r
+ }\r
+\r
+ public Node insert (Image image, BufferedImage canvas) throws IOException {\r
+ if (this.image != null) return null;\r
+ if (child1 != null) {\r
+ Node newNode = child1.insert(image, canvas);\r
+ if (newNode != null) return newNode;\r
+ return child2.insert(image, canvas);\r
+ }\r
+ int neededWidth = image.getWidth() + xPadding;\r
+ int neededHeight = image.getHeight() + yPadding;\r
+ if (neededWidth > width || neededHeight > height) return null;\r
+ if (neededWidth == width && neededHeight == height) {\r
+ this.image = image;\r
+ write(canvas);\r
+ return this;\r
+ }\r
+ int dw = width - neededWidth;\r
+ int dh = height - neededHeight;\r
+ if (dw > dh) {\r
+ child1 = new Node(left, top, neededWidth, height);\r
+ child2 = new Node(left + neededWidth, top, width - neededWidth, height);\r
+ } else {\r
+ child1 = new Node(left, top, width, neededHeight);\r
+ child2 = new Node(left, top + neededHeight, width, height - neededHeight);\r
+ }\r
+ return child1.insert(image, canvas);\r
+ }\r
+\r
+ private void write (BufferedImage canvas) throws IOException {\r
+ if (canvas == null) return;\r
+\r
+ String imageName = image.file.getAbsolutePath().substring(inputDir.getAbsolutePath().length()) + "\n";\r
+ if (imageName.startsWith("/") || imageName.startsWith("\\")) imageName = imageName.substring(1);\r
+ int dotIndex = imageName.lastIndexOf('.');\r
+ if (dotIndex != -1) imageName = imageName.substring(0, dotIndex);\r
+ if (imageName.endsWith("_4444")) imageName = imageName.substring(0, imageName.length() - 5);\r
+ if (imageName.endsWith("_565")) imageName = imageName.substring(0, imageName.length() - 4);\r
+ if (imageName.endsWith("_a")) imageName = imageName.substring(0, imageName.length() - 2);\r
+ if (imageName.endsWith("_pre")) imageName = imageName.substring(0, imageName.length() - 2);\r
+\r
+ writer.write(imageName.replace("\\", "/") + "\n");\r
+ writer.write(left + "\n");\r
+ writer.write(top + "\n");\r
+ writer.write(image.getWidth() + "\n");\r
+ writer.write(image.getHeight() + "\n");\r
+ writer.write(image.offsetX + "\n");\r
+ writer.write(image.offsetY + "\n");\r
+ writer.write(image.originalWidth + "\n");\r
+ writer.write(image.originalHeight + "\n");\r
+\r
+ Matcher matcher = numberedImagePattern.matcher(imageName);\r
+ if (matcher.matches())\r
+ writer.write(Integer.parseInt(matcher.group(1)) + "\n");\r
+ else\r
+ writer.write("0\n");\r
+ }\r
+ }\r
+\r
+ static private class Image extends BufferedImage {\r
+ final File file;\r
+ final int offsetX, offsetY;\r
+ final int originalWidth, originalHeight;\r
+\r
+ public Image (File file, BufferedImage src, int left, int top, int newWidth, int newHeight) {\r
+ super(src.getColorModel(), src.getRaster().createWritableChild(left, top, newWidth, newHeight, 0, 0, null), src\r
+ .getColorModel().isAlphaPremultiplied(), null);\r
+ this.file = file;\r
+ offsetX = left;\r
+ offsetY = top;\r
+ originalWidth = src.getWidth();\r
+ originalHeight = src.getHeight();\r
+ }\r
+\r
+ public String toString () {\r
+ return file.toString();\r
+ }\r
+ }\r
+\r
+ static private ArrayList<Comparator> imageComparators = new ArrayList();\r
+ static {\r
+ imageComparators.add(new Comparator<Image>() {\r
+ public int compare (Image image1, Image image2) {\r
+ int diff = image1.getHeight() - image2.getHeight();\r
+ if (diff != 0) return diff;\r
+ return image1.getWidth() - image2.getWidth();\r
+ }\r
+ });\r
+ imageComparators.add(new Comparator<Image>() {\r
+ public int compare (Image image1, Image image2) {\r
+ int diff = image1.getWidth() - image2.getWidth();\r
+ if (diff != 0) return diff;\r
+ return image1.getHeight() - image2.getHeight();\r
+ }\r
+ });\r
+ imageComparators.add(new Comparator<Image>() {\r
+ public int compare (Image image1, Image image2) {\r
+ return image1.getWidth() * image1.getHeight() - image2.getWidth() * image2.getHeight();\r
+ }\r
+ });\r
+ }\r
+\r
+ static private Filter rgba8888 = new Filter("RGBA8888") {\r
+ public boolean accept (File dir, String name) {\r
+ return !name.endsWith("_4444") && !name.endsWith("_565") && !name.endsWith("_a");\r
+ }\r
+ };\r
+ static private Filter rgba4444 = new Filter("RGBA4444") {\r
+ public boolean accept (File dir, String name) {\r
+ return name.endsWith("_4444");\r
+ }\r
+ };\r
+ static private Filter rgb565 = new Filter("RGB565") {\r
+ public boolean accept (File dir, String name) {\r
+ return name.endsWith("_565");\r
+ }\r
+ };\r
+ static private Filter alpha = new Filter("Alpha") {\r
+ public boolean accept (File dir, String name) {\r
+ return name.endsWith("_a");\r
+ }\r
+ };\r
+\r
+ static abstract private class Filter implements FilenameFilter {\r
+ String name;\r
+\r
+ public Filter (String name) {\r
+ this.name = name;\r
+ }\r
+ }\r
+\r
+ static private enum Direction {\r
+ x, y, xy, none\r
+ }\r
+\r
+ static private ArrayList<File> getFiles (File inputDir, Filter filter, Direction direction) {\r
+ ArrayList<File> files = new ArrayList();\r
+ files.addAll(Arrays.asList(inputDir.listFiles(filter)));\r
+ for (Iterator<File> iter = files.iterator(); iter.hasNext();) {\r
+ File file = iter.next();\r
+ String name = file.getName();\r
+ switch (direction) {\r
+ case none:\r
+ if (name.contains("_x") || name.contains("_y")) iter.remove();\r
+ break;\r
+ case x:\r
+ if (!name.contains("_x") || name.contains("_xy")) iter.remove();\r
+ break;\r
+ case y:\r
+ if (!name.contains("_y")) iter.remove();\r
+ break;\r
+ case xy:\r
+ if (!name.contains("_xy")) iter.remove();\r
+ break;\r
+ }\r
+ }\r
+ return files;\r
+ }\r
+\r
+ static private void process (File inputDir, File outputDir, File packFile) throws Exception {\r
+ if (outputDir.exists()) {\r
+ String prefix = inputDir.getParentFile().getName();\r
+ for (File file : outputDir.listFiles())\r
+ if (file.getName().startsWith(prefix)) file.delete();\r
+ }\r
+\r
+ Direction[] directions = Direction.values();\r
+ for (int i = 0; i < directions.length; i++) {\r
+ Direction direction = directions[i];\r
+ new SpriteSheetPacker(inputDir, rgba8888, direction, outputDir, packFile);\r
+ new SpriteSheetPacker(inputDir, rgba4444, direction, outputDir, packFile);\r
+ new SpriteSheetPacker(inputDir, rgb565, direction, outputDir, packFile);\r
+ new SpriteSheetPacker(inputDir, alpha, direction, outputDir, packFile);\r
+ }\r
+ File[] files = inputDir.listFiles();\r
+ if (files == null) return;\r
+ for (File file : files)\r
+ if (file.isDirectory()) process(file, new File(outputDir, file.getName()), packFile);\r
+ }\r
+\r
+ static public void process (String inputDir, String outputDir) throws Exception {\r
+ File input = new File(inputDir);\r
+ if (!input.isDirectory()) {\r
+ System.out.println("Not a directory: " + input);\r
+ return;\r
+ }\r
+ File packFile = new File(outputDir, "pack");\r
+ packFile.delete();\r
+ process(new File(inputDir), new File(outputDir), packFile);\r
+ }\r
+\r
+ public static void main (String[] args) throws Exception {\r
+ process("C:/Dev/libgdx/tests/gdx-tests-lwjgl/data/New folder", "c:/temp/pack-out");\r
+ }\r
+}\r
--- /dev/null
+\r
+package com.badlogic.gdx.graphics;\r
+\r
+import java.io.BufferedReader;\r
+import java.io.IOException;\r
+import java.io.InputStreamReader;\r
+import java.util.ArrayList;\r
+import java.util.Comparator;\r
+import java.util.List;\r
+import java.util.PriorityQueue;\r
+\r
+import com.badlogic.gdx.Files.FileType;\r
+import com.badlogic.gdx.Gdx;\r
+import com.badlogic.gdx.files.FileHandle;\r
+import com.badlogic.gdx.graphics.Texture.TextureWrap;\r
+\r
+import static com.badlogic.gdx.graphics.Texture.TextureWrap.*;\r
+import static com.badlogic.gdx.graphics.Texture.TextureFilter.*;\r
+import com.badlogic.gdx.utils.GdxRuntimeException;\r
+\r
+/**\r
+ * Loads images from texture atlases created by SpriteSheetPacker.<br>\r
+ * <br>\r
+ * A SpriteSheet must be disposed to free up the resources consumed by the backing textures.\r
+ */\r
+public class SpriteSheet {\r
+ private final ArrayList<Texture> textures = new ArrayList(4);\r
+ private final PackedSprite[] images;\r
+\r
+ public SpriteSheet (FileHandle packFile, FileHandle imagesDir) {\r
+ PriorityQueue<PackedSprite> sortedSprites = new PriorityQueue(16, indexComparator);\r
+\r
+ BufferedReader reader = new BufferedReader(new InputStreamReader(packFile.read()), 64);\r
+ try {\r
+ Sprite pageImage = null;\r
+ while (true) {\r
+ String line = reader.readLine();\r
+ if (line == null) break;\r
+ if (line.trim().length() == 0)\r
+ pageImage = null;\r
+ else if (pageImage == null) {\r
+ FileHandle file = imagesDir.child(line);\r
+\r
+ // BOZO - Get filter from file?\r
+\r
+ String direction = reader.readLine();\r
+ TextureWrap wrapX = ClampToEdge;\r
+ TextureWrap wrapY = ClampToEdge;\r
+ if (direction.equals("x"))\r
+ wrapX = Repeat;\r
+ else if (direction.equals("y"))\r
+ wrapY = Repeat;\r
+ else if (direction.equals("xy")) {\r
+ wrapX = Repeat;\r
+ wrapY = Repeat;\r
+ }\r
+\r
+ Texture texture = Gdx.graphics.newTexture(file, Linear, Linear, ClampToEdge, ClampToEdge);\r
+ textures.add(texture);\r
+\r
+ pageImage = new Sprite(texture);\r
+ } else {\r
+ int left = Integer.parseInt(reader.readLine());\r
+ int top = Integer.parseInt(reader.readLine());\r
+ int width = Integer.parseInt(reader.readLine());\r
+ int height = Integer.parseInt(reader.readLine());\r
+ int offsetX = Integer.parseInt(reader.readLine());\r
+ int offsetY = Integer.parseInt(reader.readLine());\r
+ int originalWidth = Integer.parseInt(reader.readLine());\r
+ int originalHeight = Integer.parseInt(reader.readLine());\r
+ PackedSprite image = new PackedSprite(pageImage, left, top, width, height);\r
+ image.setPosition(offsetX, offsetY);\r
+ image.name = line;\r
+ image.offsetX = offsetX;\r
+ image.offsetY = offsetY;\r
+ image.originalWidth = originalWidth;\r
+ image.originalHeight = originalHeight;\r
+ image.index = Integer.parseInt(reader.readLine());\r
+ if (image.index == -1) image.index = Integer.MAX_VALUE;\r
+ sortedSprites.add(image);\r
+ }\r
+ }\r
+ } catch (IOException ex) {\r
+ throw new GdxRuntimeException("Error reading pack file: " + packFile);\r
+ } finally {\r
+ try {\r
+ reader.close();\r
+ } catch (IOException ignored) {\r
+ }\r
+ }\r
+\r
+ int n = sortedSprites.size();\r
+ images = new PackedSprite[n];\r
+ for (int i = 0; i < n; i++)\r
+ images[i] = sortedSprites.poll();\r
+ }\r
+\r
+ /**\r
+ * Returns the first sprite found with the specified name.<br>\r
+ * <br>\r
+ * This method uses string comparison to find the sprite, so the result should be cached rather than calling this method every\r
+ * frame.\r
+ */\r
+ public PackedSprite get (String name) {\r
+ for (int i = 0, n = images.length; i < n; i++)\r
+ if (images[i].name.equals(name)) return images[i];\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * Returns all sprites found with the specified name, ordered by smallest to largest {@link PackedSprite#getIndex() index}.<br>\r
+ * <br>\r
+ * This method uses string comparison to find the sprite, so the result should be cached rather than calling this method every\r
+ * frame.\r
+ */\r
+ public List<PackedSprite> getAll (String name) {\r
+ ArrayList<PackedSprite> matched = new ArrayList();\r
+ for (int i = 0, n = images.length; i < n; i++)\r
+ if (images[i].name.equals(name)) matched.add(images[i]);\r
+ return matched;\r
+ }\r
+\r
+ /**\r
+ * Releases all resources associated with this PackedSprite instance. This releases all the textures backing all the sprites,\r
+ * so the sprites should no longer be used after calling dispose.\r
+ */\r
+ public void dispose () {\r
+ for (int i = 0, n = textures.size(); i < n; i++)\r
+ textures.get(i).dispose();\r
+ }\r
+\r
+ static private final Comparator<PackedSprite> indexComparator = new Comparator<PackedSprite>() {\r
+ public int compare (PackedSprite image1, PackedSprite image2) {\r
+ return image1.index - image2.index;\r
+ }\r
+ };\r
+\r
+ /**\r
+ * A sprite that provides additional information about the packed image it represents. A PackedSprite's position is relative to\r
+ * the bottom left of the original image, before whitespace was removed for packing.\r
+ */\r
+ static public class PackedSprite extends Sprite {\r
+ int index;\r
+ String name;\r
+ int offsetX, offsetY;\r
+ int originalWidth, originalHeight;\r
+\r
+ PackedSprite (Sprite image, int textureLeft, int textureTop, int textureRight, int textureBottom) {\r
+ super(image, textureLeft, textureTop, textureRight, textureBottom);\r
+ }\r
+\r
+ // BOZO - Test offset works and flip is handled.\r
+ public void setPosition (float x, float y) {\r
+ super.setPosition(x + offsetX, y + offsetY);\r
+ }\r
+\r
+ public void setBounds (float x, float y, float width, float height) {\r
+ super.setBounds(x + offsetX, y + offsetY, width, height);\r
+ }\r
+\r
+ /**\r
+ * The name of the original image file, with any trailing numbers or special flags removed.\r
+ */\r
+ public String getName () {\r
+ return name;\r
+ }\r
+\r
+ /**\r
+ * The number at the end of the original image file name, or Integer.MAX_VALUE if none.<br>\r
+ * <br>\r
+ * When sprites are packed, if the original file name ends with a number, it is stored as the index and is not considered as\r
+ * part of the sprite's name. This is useful for keeping animation frames in order.\r
+ * @see SpriteSheet#getAll(String)\r
+ */\r
+ public int getIndex () {\r
+ return index;\r
+ }\r
+\r
+ public int getOriginalWidth () {\r
+ return originalWidth;\r
+ }\r
+\r
+ public int getOriginalHeight () {\r
+ return originalHeight;\r
+ }\r
+\r
+ /**\r
+ * The offset from the left of the original image to the left of the packed image, after whitespace has been removed.\r
+ */\r
+ public int getOffsetX () {\r
+ return offsetX;\r
+ }\r
+\r
+ /**\r
+ * The offset from the bottom of the original image to the bottom of the packed image, after whitespace has been removed.\r
+ */\r
+ public int getOffsetY () {\r
+ return offsetY;\r
+ }\r
+ }\r
+}\r