OSDN Git Service

[added] SpriteSheetPacker, SpriteSheet, and SpriteSheetTest.
authornathan.sweet <nathan.sweet@6c4fd544-2939-11df-bb46-9574ba5d0bfa>
Wed, 17 Nov 2010 06:28:53 +0000 (06:28 +0000)
committernathan.sweet <nathan.sweet@6c4fd544-2939-11df-bb46-9574ba5d0bfa>
Wed, 17 Nov 2010 06:28:53 +0000 (06:28 +0000)
extensions/image-packer/src/com/badlogic/gdx/imagepacker/SpriteSheetPacker.java [new file with mode: 0644]
gdx/src/com/badlogic/gdx/graphics/SpriteSheet.java [new file with mode: 0644]
tests/gdx-tests-android/assets/data/data1.png [new file with mode: 0644]
tests/gdx-tests-android/assets/data/pack [new file with mode: 0644]
tests/gdx-tests-jogl/data/data1.png [new file with mode: 0644]
tests/gdx-tests-jogl/data/pack [new file with mode: 0644]
tests/gdx-tests-lwjgl/data/data1.png [new file with mode: 0644]
tests/gdx-tests-lwjgl/data/pack [new file with mode: 0644]
tests/gdx-tests-lwjgl/src/com/badlogic/gdx/tests/lwjgl/LwjglTestStarter.java
tests/gdx-tests/src/com/badlogic/gdx/tests/SpriteSheetTest.java [new file with mode: 0644]
tests/gdx-tests/src/com/badlogic/gdx/tests/utils/GdxTests.java

diff --git a/extensions/image-packer/src/com/badlogic/gdx/imagepacker/SpriteSheetPacker.java b/extensions/image-packer/src/com/badlogic/gdx/imagepacker/SpriteSheetPacker.java
new file mode 100644 (file)
index 0000000..28b1cd1
--- /dev/null
@@ -0,0 +1,480 @@
+\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
diff --git a/gdx/src/com/badlogic/gdx/graphics/SpriteSheet.java b/gdx/src/com/badlogic/gdx/graphics/SpriteSheet.java
new file mode 100644 (file)
index 0000000..c5163bc
--- /dev/null
@@ -0,0 +1,201 @@
+\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
diff --git a/tests/gdx-tests-android/assets/data/data1.png b/tests/gdx-tests-android/assets/data/data1.png
new file mode 100644 (file)
index 0000000..4392e0a
Binary files /dev/null and b/tests/gdx-tests-android/assets/data/data1.png differ
diff --git a/tests/gdx-tests-android/assets/data/pack b/tests/gdx-tests-android/assets/data/pack
new file mode 100644 (file)
index 0000000..cf40bab
--- /dev/null
@@ -0,0 +1,43 @@
+
+data1.png
+none
+badlogic
+0
+0
+256
+256
+0
+0
+256
+256
+0
+particle-fire
+256
+0
+127
+122
+0
+0
+128
+128
+0
+particle-star
+383
+0
+63
+63
+0
+0
+64
+64
+0
+badlogicsmall
+383
+63
+32
+32
+0
+0
+32
+32
+0
diff --git a/tests/gdx-tests-jogl/data/data1.png b/tests/gdx-tests-jogl/data/data1.png
new file mode 100644 (file)
index 0000000..4392e0a
Binary files /dev/null and b/tests/gdx-tests-jogl/data/data1.png differ
diff --git a/tests/gdx-tests-jogl/data/pack b/tests/gdx-tests-jogl/data/pack
new file mode 100644 (file)
index 0000000..cf40bab
--- /dev/null
@@ -0,0 +1,43 @@
+
+data1.png
+none
+badlogic
+0
+0
+256
+256
+0
+0
+256
+256
+0
+particle-fire
+256
+0
+127
+122
+0
+0
+128
+128
+0
+particle-star
+383
+0
+63
+63
+0
+0
+64
+64
+0
+badlogicsmall
+383
+63
+32
+32
+0
+0
+32
+32
+0
diff --git a/tests/gdx-tests-lwjgl/data/data1.png b/tests/gdx-tests-lwjgl/data/data1.png
new file mode 100644 (file)
index 0000000..4392e0a
Binary files /dev/null and b/tests/gdx-tests-lwjgl/data/data1.png differ
diff --git a/tests/gdx-tests-lwjgl/data/pack b/tests/gdx-tests-lwjgl/data/pack
new file mode 100644 (file)
index 0000000..cf40bab
--- /dev/null
@@ -0,0 +1,43 @@
+
+data1.png
+none
+badlogic
+0
+0
+256
+256
+0
+0
+256
+256
+0
+particle-fire
+256
+0
+127
+122
+0
+0
+128
+128
+0
+particle-star
+383
+0
+63
+63
+0
+0
+64
+64
+0
+badlogicsmall
+383
+63
+32
+32
+0
+0
+32
+32
+0
index a370951..c1becef 100644 (file)
@@ -43,12 +43,15 @@ public class LwjglTestStarter {
                                @Override public void actionPerformed (ActionEvent e) {\r
                                        String testName = (String)list.getSelectedValue();\r
                                        GdxTest test = GdxTests.newTest(testName);\r
-                                       new LwjglApplication(test,testName, 480, 320, test.needsGL20());                                        \r
+                                       new LwjglApplication(test, testName, 480, 320, test.needsGL20());\r
                                }\r
                        });\r
 \r
                        add(pane, BorderLayout.CENTER);\r
                        add(button, BorderLayout.SOUTH);\r
+\r
+                       // GdxTest test = GdxTests.newTest("SpriteSheetTest");\r
+                       // new LwjglApplication(test, "Test", 480, 320, test.needsGL20());\r
                }\r
        }\r
 \r
diff --git a/tests/gdx-tests/src/com/badlogic/gdx/tests/SpriteSheetTest.java b/tests/gdx-tests/src/com/badlogic/gdx/tests/SpriteSheetTest.java
new file mode 100644 (file)
index 0000000..d939da9
--- /dev/null
@@ -0,0 +1,56 @@
+\r
+package com.badlogic.gdx.tests;\r
+\r
+import com.badlogic.gdx.Gdx;\r
+import com.badlogic.gdx.graphics.GL10;\r
+import com.badlogic.gdx.graphics.SpriteSheet;\r
+import com.badlogic.gdx.graphics.Sprite;\r
+import com.badlogic.gdx.graphics.SpriteBatch;\r
+import com.badlogic.gdx.tests.utils.GdxTest;\r
+\r
+public class SpriteSheetTest extends GdxTest {\r
+       SpriteBatch batch;\r
+       Sprite badlogic, badlogicSmall, star;\r
+       SpriteSheet spriteSheet;\r
+\r
+       public void create () {\r
+               batch = new SpriteBatch();\r
+\r
+               spriteSheet = new SpriteSheet(Gdx.files.internal("data/pack"), Gdx.files.internal("data"));\r
+               badlogic = spriteSheet.get("badlogic");\r
+               badlogicSmall = spriteSheet.get("badlogicsmall");\r
+               star = spriteSheet.get("particle-star");\r
+\r
+               badlogic.setPosition(50, 50);\r
+               badlogicSmall.setPosition(10, 10);\r
+               star.setPosition(10, 70);\r
+\r
+               Gdx.gl.glClearColor(0, 1, 0, 1);\r
+       }\r
+\r
+       public void render () {\r
+               Gdx.gl.glClear(GL10.GL_COLOR_BUFFER_BIT);\r
+               batch.begin();\r
+               badlogicSmall.draw(batch);\r
+               badlogic.draw(batch);\r
+               star.draw(batch);\r
+               batch.end();\r
+       }\r
+\r
+       public void resize (int width, int height) {\r
+       }\r
+\r
+       public void pause () {\r
+       }\r
+\r
+       public void resume () {\r
+       }\r
+\r
+       public void dispose () {\r
+               spriteSheet.dispose();\r
+       }\r
+\r
+       public boolean needsGL20 () {\r
+               return false;\r
+       }\r
+}\r
index 77871a3..85f7e43 100644 (file)
@@ -29,6 +29,7 @@ import com.badlogic.gdx.tests.Mpg123Test;
 import com.badlogic.gdx.tests.MultitouchTest;\r
 import com.badlogic.gdx.tests.MyFirstTriangle;\r
 import com.badlogic.gdx.tests.ObjTest;\r
+import com.badlogic.gdx.tests.SpriteSheetTest;\r
 import com.badlogic.gdx.tests.ParticleEmitterTest;\r
 import com.badlogic.gdx.tests.PixelsPerInchTest;\r
 import com.badlogic.gdx.tests.Pong;\r
@@ -87,6 +88,7 @@ public class GdxTests
                MultitouchTest.class,\r
                MyFirstTriangle.class,\r
                ObjTest.class,\r
+               SpriteSheetTest.class,\r
                ParticleEmitterTest.class,\r
                PixelsPerInchTest.class,\r
                Pong.class,\r