gdx-studio
git clone https://git.pyrossh.dev/gdx-studio
An IDE for creating Games using libgdx and Java supported on all platforms Android, iOS, Desktop
src_libs/com/badlogic/gdx/tools/imagepacker/TexturePacker2.java
| a62d533 | 1 | /******************************************************************************* |
| a62d533 | 2 | * Copyright 2011 See AUTHORS file. |
| a62d533 | 3 | * |
| a62d533 | 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| a62d533 | 5 | * you may not use this file except in compliance with the License. |
| a62d533 | 6 | * You may obtain a copy of the License at |
| a62d533 | 7 | * |
| a62d533 | 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| a62d533 | 9 | * |
| a62d533 | 10 | * Unless required by applicable law or agreed to in writing, software |
| a62d533 | 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| a62d533 | 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| a62d533 | 13 | * See the License for the specific language governing permissions and |
| a62d533 | 14 | * limitations under the License. |
| a62d533 | 15 | ******************************************************************************/ |
| a62d533 | 16 | |
| a62d533 | 17 | package com.badlogic.gdx.tools.imagepacker; |
| a62d533 | 18 | |
| a62d533 | 19 | import java.awt.Color; |
| a62d533 | 20 | import java.awt.Graphics2D; |
| a62d533 | 21 | import java.awt.image.BufferedImage; |
| a62d533 | 22 | import java.io.File; |
| a62d533 | 23 | import java.io.FileWriter; |
| a62d533 | 24 | import java.io.IOException; |
| a62d533 | 25 | import java.util.HashSet; |
| a62d533 | 26 | import java.util.Iterator; |
| a62d533 | 27 | import java.util.Set; |
| a62d533 | 28 | |
| a62d533 | 29 | import javax.imageio.IIOImage; |
| a62d533 | 30 | import javax.imageio.ImageIO; |
| a62d533 | 31 | import javax.imageio.ImageWriteParam; |
| a62d533 | 32 | import javax.imageio.ImageWriter; |
| a62d533 | 33 | import javax.imageio.stream.ImageOutputStream; |
| a62d533 | 34 | |
| a62d533 | 35 | import com.badlogic.gdx.files.FileHandle; |
| a62d533 | 36 | import com.badlogic.gdx.graphics.Pixmap.Format; |
| a62d533 | 37 | import com.badlogic.gdx.graphics.Texture.TextureFilter; |
| a62d533 | 38 | import com.badlogic.gdx.graphics.Texture.TextureWrap; |
| a62d533 | 39 | import com.badlogic.gdx.graphics.g2d.TextureAtlas.TextureAtlasData; |
| a62d533 | 40 | import com.badlogic.gdx.graphics.g2d.TextureAtlas.TextureAtlasData.Region; |
| a62d533 | 41 | import com.badlogic.gdx.math.MathUtils; |
| a62d533 | 42 | import com.badlogic.gdx.utils.Array; |
| a62d533 | 43 | import com.badlogic.gdx.utils.GdxRuntimeException; |
| a62d533 | 44 | |
| a62d533 | 45 | /** @author Nathan Sweet */ |
| a62d533 | 46 | public class TexturePacker2 { |
| a62d533 | 47 | private final Settings settings; |
| a62d533 | 48 | private final MaxRectsPacker maxRectsPacker; |
| a62d533 | 49 | private final ImageProcessor imageProcessor; |
| a62d533 | 50 | |
| a62d533 | 51 | /** @param rootDir Can be null. */ |
| a62d533 | 52 | public TexturePacker2 (File rootDir, Settings settings) { |
| a62d533 | 53 | this.settings = settings; |
| a62d533 | 54 | |
| a62d533 | 55 | if (settings.pot) { |
| a62d533 | 56 | if (settings.maxWidth != MathUtils.nextPowerOfTwo(settings.maxWidth)) |
| a62d533 | 57 | throw new RuntimeException("If pot is true, maxWidth must be a power of two: " + settings.maxWidth); |
| a62d533 | 58 | if (settings.maxHeight != MathUtils.nextPowerOfTwo(settings.maxHeight)) |
| a62d533 | 59 | throw new RuntimeException("If pot is true, maxHeight must be a power of two: " + settings.maxHeight); |
| a62d533 | 60 | } |
| a62d533 | 61 | |
| a62d533 | 62 | maxRectsPacker = new MaxRectsPacker(settings); |
| a62d533 | 63 | imageProcessor = new ImageProcessor(rootDir, settings); |
| a62d533 | 64 | } |
| a62d533 | 65 | |
| a62d533 | 66 | public TexturePacker2 (Settings settings) { |
| a62d533 | 67 | this(null, settings); |
| a62d533 | 68 | } |
| a62d533 | 69 | |
| a62d533 | 70 | public void addImage (File file) { |
| a62d533 | 71 | imageProcessor.addImage(file); |
| a62d533 | 72 | } |
| a62d533 | 73 | |
| a62d533 | 74 | public void addImage (BufferedImage image, String name) { |
| a62d533 | 75 | imageProcessor.addImage(image, name); |
| a62d533 | 76 | } |
| a62d533 | 77 | |
| a62d533 | 78 | public void pack (File outputDir, String packFileName) { |
| a62d533 | 79 | outputDir.mkdirs(); |
| a62d533 | 80 | |
| a62d533 | 81 | if (packFileName.indexOf('.') == -1) packFileName += ".atlas"; |
| a62d533 | 82 | |
| a62d533 | 83 | Array<Page> pages = maxRectsPacker.pack(imageProcessor.getImages()); |
| a62d533 | 84 | writeImages(outputDir, pages, packFileName); |
| a62d533 | 85 | try { |
| a62d533 | 86 | writePackFile(outputDir, pages, packFileName); |
| a62d533 | 87 | } catch (IOException ex) { |
| a62d533 | 88 | throw new RuntimeException("Error writing pack file.", ex); |
| a62d533 | 89 | } |
| a62d533 | 90 | } |
| a62d533 | 91 | |
| a62d533 | 92 | private void writeImages (File outputDir, Array<Page> pages, String packFileName) { |
| a62d533 | 93 | String imageName = packFileName; |
| a62d533 | 94 | int dotIndex = imageName.lastIndexOf('.'); |
| a62d533 | 95 | if (dotIndex != -1) imageName = imageName.substring(0, dotIndex); |
| a62d533 | 96 | |
| a62d533 | 97 | int fileIndex = 0; |
| a62d533 | 98 | for (Page page : pages) { |
| a62d533 | 99 | int width = page.width, height = page.height; |
| a62d533 | 100 | int paddingX = settings.paddingX; |
| a62d533 | 101 | int paddingY = settings.paddingY; |
| a62d533 | 102 | if (settings.duplicatePadding) { |
| a62d533 | 103 | paddingX /= 2; |
| a62d533 | 104 | paddingY /= 2; |
| a62d533 | 105 | } |
| a62d533 | 106 | width -= settings.paddingX; |
| a62d533 | 107 | height -= settings.paddingY; |
| a62d533 | 108 | if (settings.edgePadding) { |
| a62d533 | 109 | page.x = paddingX; |
| a62d533 | 110 | page.y = paddingY; |
| a62d533 | 111 | width += paddingX * 2; |
| a62d533 | 112 | height += paddingY * 2; |
| a62d533 | 113 | } |
| a62d533 | 114 | if (settings.pot) { |
| a62d533 | 115 | width = MathUtils.nextPowerOfTwo(width); |
| a62d533 | 116 | height = MathUtils.nextPowerOfTwo(height); |
| a62d533 | 117 | } |
| a62d533 | 118 | width = Math.max(settings.minWidth, width); |
| a62d533 | 119 | height = Math.max(settings.minHeight, height); |
| a62d533 | 120 | |
| a62d533 | 121 | if (settings.forceSquareOutput) { |
| a62d533 | 122 | if (width > height) { |
| a62d533 | 123 | height = width; |
| a62d533 | 124 | } else { |
| a62d533 | 125 | width = height; |
| a62d533 | 126 | } |
| a62d533 | 127 | } |
| a62d533 | 128 | |
| a62d533 | 129 | File outputFile; |
| a62d533 | 130 | while (true) { |
| a62d533 | 131 | outputFile = new File(outputDir, imageName + (fileIndex++ == 0 ? "" : fileIndex) + "." + settings.outputFormat); |
| a62d533 | 132 | if (!outputFile.exists()) break; |
| a62d533 | 133 | } |
| a62d533 | 134 | page.imageName = outputFile.getName(); |
| a62d533 | 135 | |
| a62d533 | 136 | BufferedImage canvas = new BufferedImage(width, height, getBufferedImageType(settings.format)); |
| a62d533 | 137 | Graphics2D g = (Graphics2D)canvas.getGraphics(); |
| a62d533 | 138 | |
| a62d533 | 139 | System.out.println("Writing " + canvas.getWidth() + "x" + canvas.getHeight() + ": " + outputFile); |
| a62d533 | 140 | |
| a62d533 | 141 | for (Rect rect : page.outputRects) { |
| a62d533 | 142 | BufferedImage image = rect.image; |
| a62d533 | 143 | int iw = image.getWidth(); |
| a62d533 | 144 | int ih = image.getHeight(); |
| a62d533 | 145 | int rectX = page.x + rect.x, rectY = page.y + page.height - rect.y - rect.height; |
| a62d533 | 146 | if (settings.duplicatePadding) { |
| a62d533 | 147 | int amountX = settings.paddingX / 2; |
| a62d533 | 148 | int amountY = settings.paddingY / 2; |
| a62d533 | 149 | if (rect.rotated) { |
| a62d533 | 150 | // Copy corner pixels to fill corners of the padding. |
| a62d533 | 151 | for (int i = 1; i <= amountX; i++) { |
| a62d533 | 152 | for (int j = 1; j <= amountY; j++) { |
| a62d533 | 153 | plot(canvas, rectX - j, rectY + iw - 1 + i, image.getRGB(0, 0)); |
| a62d533 | 154 | plot(canvas, rectX + ih - 1 + j, rectY + iw - 1 + i, image.getRGB(0, ih - 1)); |
| a62d533 | 155 | plot(canvas, rectX - j, rectY - i, image.getRGB(iw - 1, 0)); |
| a62d533 | 156 | plot(canvas, rectX + ih - 1 + j, rectY - i, image.getRGB(iw - 1, ih - 1)); |
| a62d533 | 157 | } |
| a62d533 | 158 | } |
| a62d533 | 159 | // Copy edge pixels into padding. |
| a62d533 | 160 | for (int i = 1; i <= amountY; i++) { |
| a62d533 | 161 | for (int j = 0; j < iw; j++) { |
| a62d533 | 162 | plot(canvas, rectX - i, rectY + iw - 1 - j, image.getRGB(j, 0)); |
| a62d533 | 163 | plot(canvas, rectX + ih - 1 + i, rectY + iw - 1 - j, image.getRGB(j, ih - 1)); |
| a62d533 | 164 | } |
| a62d533 | 165 | } |
| a62d533 | 166 | for (int i = 1; i <= amountX; i++) { |
| a62d533 | 167 | for (int j = 0; j < ih; j++) { |
| a62d533 | 168 | plot(canvas, rectX + j, rectY - i, image.getRGB(iw - 1, j)); |
| a62d533 | 169 | plot(canvas, rectX + j, rectY + iw - 1 + i, image.getRGB(0, j)); |
| a62d533 | 170 | } |
| a62d533 | 171 | } |
| a62d533 | 172 | } else { |
| a62d533 | 173 | // Copy corner pixels to fill corners of the padding. |
| a62d533 | 174 | for (int i = 1; i <= amountX; i++) { |
| a62d533 | 175 | for (int j = 1; j <= amountY; j++) { |
| a62d533 | 176 | canvas.setRGB(rectX - i, rectY - j, image.getRGB(0, 0)); |
| a62d533 | 177 | canvas.setRGB(rectX - i, rectY + ih - 1 + j, image.getRGB(0, ih - 1)); |
| a62d533 | 178 | canvas.setRGB(rectX + iw - 1 + i, rectY - j, image.getRGB(iw - 1, 0)); |
| a62d533 | 179 | canvas.setRGB(rectX + iw - 1 + i, rectY + ih - 1 + j, image.getRGB(iw - 1, ih - 1)); |
| a62d533 | 180 | } |
| a62d533 | 181 | } |
| a62d533 | 182 | // Copy edge pixels into padding. |
| a62d533 | 183 | for (int i = 1; i <= amountY; i++) { |
| a62d533 | 184 | copy(image, 0, 0, iw, 1, canvas, rectX, rectY - i, rect.rotated); |
| a62d533 | 185 | copy(image, 0, ih - 1, iw, 1, canvas, rectX, rectY + ih - 1 + i, rect.rotated); |
| a62d533 | 186 | } |
| a62d533 | 187 | for (int i = 1; i <= amountX; i++) { |
| a62d533 | 188 | copy(image, 0, 0, 1, ih, canvas, rectX - i, rectY, rect.rotated); |
| a62d533 | 189 | copy(image, iw - 1, 0, 1, ih, canvas, rectX + iw - 1 + i, rectY, rect.rotated); |
| a62d533 | 190 | } |
| a62d533 | 191 | } |
| a62d533 | 192 | } |
| a62d533 | 193 | copy(image, 0, 0, iw, ih, canvas, rectX, rectY, rect.rotated); |
| a62d533 | 194 | if (settings.debug) { |
| a62d533 | 195 | g.setColor(Color.magenta); |
| a62d533 | 196 | g.drawRect(rectX, rectY, rect.width - settings.paddingX - 1, rect.height - settings.paddingY - 1); |
| a62d533 | 197 | } |
| a62d533 | 198 | } |
| a62d533 | 199 | |
| a62d533 | 200 | if (settings.bleed && !settings.premultiplyAlpha && !settings.outputFormat.equalsIgnoreCase("jpg")) { |
| a62d533 | 201 | canvas = new ColorBleedEffect().processImage(canvas, 2); |
| a62d533 | 202 | g = (Graphics2D)canvas.getGraphics(); |
| a62d533 | 203 | } |
| a62d533 | 204 | |
| a62d533 | 205 | if (settings.debug) { |
| a62d533 | 206 | g.setColor(Color.magenta); |
| a62d533 | 207 | g.drawRect(0, 0, width - 1, height - 1); |
| a62d533 | 208 | } |
| a62d533 | 209 | |
| a62d533 | 210 | ImageOutputStream ios = null; |
| a62d533 | 211 | try { |
| a62d533 | 212 | if (settings.outputFormat.equalsIgnoreCase("jpg")) { |
| a62d533 | 213 | BufferedImage newImage = new BufferedImage(canvas.getWidth(), canvas.getHeight(), BufferedImage.TYPE_3BYTE_BGR); |
| a62d533 | 214 | newImage.getGraphics().drawImage(canvas, 0, 0, null); |
| a62d533 | 215 | canvas = newImage; |
| a62d533 | 216 | |
| a62d533 | 217 | Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("jpg"); |
| a62d533 | 218 | ImageWriter writer = writers.next(); |
| a62d533 | 219 | ImageWriteParam param = writer.getDefaultWriteParam(); |
| a62d533 | 220 | param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT); |
| a62d533 | 221 | param.setCompressionQuality(settings.jpegQuality); |
| a62d533 | 222 | ios = ImageIO.createImageOutputStream(outputFile); |
| a62d533 | 223 | writer.setOutput(ios); |
| a62d533 | 224 | writer.write(null, new IIOImage(canvas, null, null), param); |
| a62d533 | 225 | } else { |
| a62d533 | 226 | if (settings.premultiplyAlpha) canvas.getColorModel().coerceData(canvas.getRaster(), true); |
| a62d533 | 227 | ImageIO.write(canvas, "png", outputFile); |
| a62d533 | 228 | } |
| a62d533 | 229 | } catch (IOException ex) { |
| a62d533 | 230 | throw new RuntimeException("Error writing file: " + outputFile, ex); |
| a62d533 | 231 | } finally { |
| a62d533 | 232 | if (ios != null) { |
| a62d533 | 233 | try { |
| a62d533 | 234 | ios.close(); |
| a62d533 | 235 | } catch (Exception ignored) { |
| a62d533 | 236 | } |
| a62d533 | 237 | } |
| a62d533 | 238 | } |
| a62d533 | 239 | } |
| a62d533 | 240 | } |
| a62d533 | 241 | |
| a62d533 | 242 | private static void plot (BufferedImage dst, int x, int y, int argb) { |
| a62d533 | 243 | if (0 <= x && x < dst.getWidth() && 0 <= y && y < dst.getHeight()) dst.setRGB(x, y, argb); |
| a62d533 | 244 | } |
| a62d533 | 245 | |
| a62d533 | 246 | private static void copy (BufferedImage src, int x, int y, int w, int h, BufferedImage dst, int dx, int dy, boolean rotated) { |
| a62d533 | 247 | if (rotated) { |
| a62d533 | 248 | for (int i = 0; i < w; i++) |
| a62d533 | 249 | for (int j = 0; j < h; j++) |
| a62d533 | 250 | dst.setRGB(dx + j, dy + w - i - 1, src.getRGB(x + i, y + j)); |
| a62d533 | 251 | } else { |
| a62d533 | 252 | for (int i = 0; i < w; i++) |
| a62d533 | 253 | for (int j = 0; j < h; j++) |
| a62d533 | 254 | dst.setRGB(dx + i, dy + j, src.getRGB(x + i, y + j)); |
| a62d533 | 255 | } |
| a62d533 | 256 | } |
| a62d533 | 257 | |
| a62d533 | 258 | private void writePackFile (File outputDir, Array<Page> pages, String packFileName) throws IOException { |
| a62d533 | 259 | File packFile = new File(outputDir, packFileName); |
| a62d533 | 260 | |
| a62d533 | 261 | if (packFile.exists()) { |
| a62d533 | 262 | // Make sure there aren't duplicate names. |
| a62d533 | 263 | TextureAtlasData textureAtlasData = new TextureAtlasData(new FileHandle(packFile), new FileHandle(packFile), false); |
| a62d533 | 264 | for (Page page : pages) { |
| a62d533 | 265 | for (Rect rect : page.outputRects) { |
| a62d533 | 266 | String rectName = Rect.getAtlasName(rect.name, settings.flattenPaths); |
| a62d533 | 267 | for (Region region : textureAtlasData.getRegions()) { |
| a62d533 | 268 | if (region.name.equals(rectName)) { |
| a62d533 | 269 | throw new GdxRuntimeException("A region with the name \"" + rectName + "\" has already been packed: " |
| a62d533 | 270 | + rect.name); |
| a62d533 | 271 | } |
| a62d533 | 272 | } |
| a62d533 | 273 | } |
| a62d533 | 274 | } |
| a62d533 | 275 | } |
| a62d533 | 276 | |
| a62d533 | 277 | FileWriter writer = new FileWriter(packFile, true); |
| a62d533 | 278 | // if (settings.jsonOutput) { |
| a62d533 | 279 | // } else { |
| a62d533 | 280 | for (Page page : pages) { |
| a62d533 | 281 | writer.write("\n" + page.imageName + "\n"); |
| a62d533 | 282 | writer.write("format: " + settings.format + "\n"); |
| a62d533 | 283 | writer.write("filter: " + settings.filterMin + "," + settings.filterMag + "\n"); |
| a62d533 | 284 | writer.write("repeat: " + getRepeatValue() + "\n"); |
| a62d533 | 285 | |
| a62d533 | 286 | for (Rect rect : page.outputRects) { |
| a62d533 | 287 | writeRect(writer, page, rect, rect.name); |
| a62d533 | 288 | for (Alias alias : rect.aliases) { |
| a62d533 | 289 | Rect aliasRect = new Rect(); |
| a62d533 | 290 | aliasRect.set(rect); |
| a62d533 | 291 | alias.apply(aliasRect); |
| a62d533 | 292 | writeRect(writer, page, aliasRect, alias.name); |
| a62d533 | 293 | } |
| a62d533 | 294 | } |
| a62d533 | 295 | } |
| a62d533 | 296 | // } |
| a62d533 | 297 | writer.close(); |
| a62d533 | 298 | } |
| a62d533 | 299 | |
| a62d533 | 300 | private void writeRect (FileWriter writer, Page page, Rect rect, String name) throws IOException { |
| a62d533 | 301 | writer.write(Rect.getAtlasName(name, settings.flattenPaths) + "\n"); |
| a62d533 | 302 | writer.write(" rotate: " + rect.rotated + "\n"); |
| a62d533 | 303 | writer.write(" xy: " + (page.x + rect.x) + ", " + (page.y + page.height - rect.height - rect.y) + "\n"); |
| a62d533 | 304 | writer.write(" size: " + rect.image.getWidth() + ", " + rect.image.getHeight() + "\n"); |
| a62d533 | 305 | if (rect.splits != null) { |
| a62d533 | 306 | writer |
| a62d533 | 307 | .write(" split: " + rect.splits[0] + ", " + rect.splits[1] + ", " + rect.splits[2] + ", " + rect.splits[3] + "\n"); |
| a62d533 | 308 | } |
| a62d533 | 309 | if (rect.pads != null) { |
| a62d533 | 310 | if (rect.splits == null) writer.write(" split: 0, 0, 0, 0\n"); |
| a62d533 | 311 | writer.write(" pad: " + rect.pads[0] + ", " + rect.pads[1] + ", " + rect.pads[2] + ", " + rect.pads[3] + "\n"); |
| a62d533 | 312 | } |
| a62d533 | 313 | writer.write(" orig: " + rect.originalWidth + ", " + rect.originalHeight + "\n"); |
| a62d533 | 314 | writer.write(" offset: " + rect.offsetX + ", " + (rect.originalHeight - rect.image.getHeight() - rect.offsetY) + "\n"); |
| a62d533 | 315 | writer.write(" index: " + rect.index + "\n"); |
| a62d533 | 316 | } |
| a62d533 | 317 | |
| a62d533 | 318 | private String getRepeatValue () { |
| a62d533 | 319 | if (settings.wrapX == TextureWrap.Repeat && settings.wrapY == TextureWrap.Repeat) return "xy"; |
| a62d533 | 320 | if (settings.wrapX == TextureWrap.Repeat && settings.wrapY == TextureWrap.ClampToEdge) return "x"; |
| a62d533 | 321 | if (settings.wrapX == TextureWrap.ClampToEdge && settings.wrapY == TextureWrap.Repeat) return "y"; |
| a62d533 | 322 | return "none"; |
| a62d533 | 323 | } |
| a62d533 | 324 | |
| a62d533 | 325 | private int getBufferedImageType (Format format) { |
| a62d533 | 326 | switch (settings.format) { |
| a62d533 | 327 | case RGBA8888: |
| a62d533 | 328 | case RGBA4444: |
| a62d533 | 329 | return BufferedImage.TYPE_INT_ARGB; |
| a62d533 | 330 | case RGB565: |
| a62d533 | 331 | case RGB888: |
| a62d533 | 332 | return BufferedImage.TYPE_INT_RGB; |
| a62d533 | 333 | case Alpha: |
| a62d533 | 334 | return BufferedImage.TYPE_BYTE_GRAY; |
| a62d533 | 335 | default: |
| a62d533 | 336 | throw new RuntimeException("Unsupported format: " + settings.format); |
| a62d533 | 337 | } |
| a62d533 | 338 | } |
| a62d533 | 339 | |
| a62d533 | 340 | /** @author Nathan Sweet */ |
| a62d533 | 341 | public static class Page { |
| a62d533 | 342 | public String imageName; |
| a62d533 | 343 | public Array<Rect> outputRects, remainingRects; |
| a62d533 | 344 | public float occupancy; |
| a62d533 | 345 | public int x, y, width, height; |
| a62d533 | 346 | } |
| a62d533 | 347 | |
| a62d533 | 348 | /** @author Regnarock |
| a62d533 | 349 | * @author Nathan Sweet */ |
| a62d533 | 350 | public static class Alias { |
| a62d533 | 351 | public String name; |
| a62d533 | 352 | public int index; |
| a62d533 | 353 | public int[] splits; |
| a62d533 | 354 | public int[] pads; |
| a62d533 | 355 | public int offsetX, offsetY, originalWidth, originalHeight; |
| a62d533 | 356 | |
| a62d533 | 357 | public Alias (Rect rect) { |
| a62d533 | 358 | name = rect.name; |
| a62d533 | 359 | index = rect.index; |
| a62d533 | 360 | splits = rect.splits; |
| a62d533 | 361 | pads = rect.pads; |
| a62d533 | 362 | offsetX = rect.offsetX; |
| a62d533 | 363 | offsetY = rect.offsetY; |
| a62d533 | 364 | originalWidth = rect.originalWidth; |
| a62d533 | 365 | originalHeight = rect.originalHeight; |
| a62d533 | 366 | } |
| a62d533 | 367 | |
| a62d533 | 368 | public void apply (Rect rect) { |
| a62d533 | 369 | rect.name = name; |
| a62d533 | 370 | rect.index = index; |
| a62d533 | 371 | rect.splits = splits; |
| a62d533 | 372 | rect.pads = pads; |
| a62d533 | 373 | rect.offsetX = offsetX; |
| a62d533 | 374 | rect.offsetY = offsetY; |
| a62d533 | 375 | rect.originalWidth = originalWidth; |
| a62d533 | 376 | rect.originalHeight = originalHeight; |
| a62d533 | 377 | } |
| a62d533 | 378 | } |
| a62d533 | 379 | |
| a62d533 | 380 | /** @author Nathan Sweet */ |
| a62d533 | 381 | public static class Rect { |
| a62d533 | 382 | public String name; |
| a62d533 | 383 | public BufferedImage image; |
| a62d533 | 384 | public int offsetX, offsetY, originalWidth, originalHeight; |
| a62d533 | 385 | public int x, y, width, height; |
| a62d533 | 386 | public int index; |
| a62d533 | 387 | public boolean rotated; |
| a62d533 | 388 | public Set<Alias> aliases = new HashSet<Alias>(); |
| a62d533 | 389 | public int[] splits; |
| a62d533 | 390 | public int[] pads; |
| a62d533 | 391 | public boolean canRotate = true; |
| a62d533 | 392 | |
| a62d533 | 393 | int score1, score2; |
| a62d533 | 394 | |
| a62d533 | 395 | Rect (BufferedImage source, int left, int top, int newWidth, int newHeight) { |
| a62d533 | 396 | image = new BufferedImage(source.getColorModel(), source.getRaster().createWritableChild(left, top, newWidth, newHeight, |
| a62d533 | 397 | 0, 0, null), source.getColorModel().isAlphaPremultiplied(), null); |
| a62d533 | 398 | offsetX = left; |
| a62d533 | 399 | offsetY = top; |
| a62d533 | 400 | originalWidth = source.getWidth(); |
| a62d533 | 401 | originalHeight = source.getHeight(); |
| a62d533 | 402 | width = newWidth; |
| a62d533 | 403 | height = newHeight; |
| a62d533 | 404 | } |
| a62d533 | 405 | |
| a62d533 | 406 | Rect () { |
| a62d533 | 407 | } |
| a62d533 | 408 | |
| a62d533 | 409 | Rect (Rect rect) { |
| a62d533 | 410 | setSize(rect); |
| a62d533 | 411 | } |
| a62d533 | 412 | |
| a62d533 | 413 | void setSize (Rect rect) { |
| a62d533 | 414 | x = rect.x; |
| a62d533 | 415 | y = rect.y; |
| a62d533 | 416 | width = rect.width; |
| a62d533 | 417 | height = rect.height; |
| a62d533 | 418 | } |
| a62d533 | 419 | |
| a62d533 | 420 | void set (Rect rect) { |
| a62d533 | 421 | name = rect.name; |
| a62d533 | 422 | image = rect.image; |
| a62d533 | 423 | offsetX = rect.offsetX; |
| a62d533 | 424 | offsetY = rect.offsetY; |
| a62d533 | 425 | originalWidth = rect.originalWidth; |
| a62d533 | 426 | originalHeight = rect.originalHeight; |
| a62d533 | 427 | x = rect.x; |
| a62d533 | 428 | y = rect.y; |
| a62d533 | 429 | width = rect.width; |
| a62d533 | 430 | height = rect.height; |
| a62d533 | 431 | index = rect.index; |
| a62d533 | 432 | rotated = rect.rotated; |
| a62d533 | 433 | aliases = rect.aliases; |
| a62d533 | 434 | splits = rect.splits; |
| a62d533 | 435 | pads = rect.pads; |
| a62d533 | 436 | canRotate = rect.canRotate; |
| a62d533 | 437 | score1 = rect.score1; |
| a62d533 | 438 | score2 = rect.score2; |
| a62d533 | 439 | } |
| a62d533 | 440 | |
| a62d533 | 441 | @Override |
| a62d533 | 442 | public boolean equals (Object obj) { |
| a62d533 | 443 | if (this == obj) return true; |
| a62d533 | 444 | if (obj == null) return false; |
| a62d533 | 445 | if (getClass() != obj.getClass()) return false; |
| a62d533 | 446 | Rect other = (Rect)obj; |
| a62d533 | 447 | if (name == null) { |
| a62d533 | 448 | if (other.name != null) return false; |
| a62d533 | 449 | } else if (!name.equals(other.name)) return false; |
| a62d533 | 450 | return true; |
| a62d533 | 451 | } |
| a62d533 | 452 | |
| a62d533 | 453 | @Override |
| a62d533 | 454 | public String toString () { |
| a62d533 | 455 | return name + "[" + x + "," + y + " " + width + "x" + height + "]"; |
| a62d533 | 456 | } |
| a62d533 | 457 | |
| a62d533 | 458 | static public String getAtlasName (String name, boolean flattenPaths) { |
| a62d533 | 459 | return flattenPaths ? new FileHandle(name).name() : name; |
| a62d533 | 460 | } |
| a62d533 | 461 | } |
| a62d533 | 462 | |
| a62d533 | 463 | /** @author Nathan Sweet */ |
| a62d533 | 464 | static public class Settings { |
| a62d533 | 465 | public boolean pot = true; |
| a62d533 | 466 | public int paddingX = 2, paddingY = 2; |
| a62d533 | 467 | public boolean edgePadding = true; |
| a62d533 | 468 | public boolean duplicatePadding = false; |
| a62d533 | 469 | public boolean rotation; |
| a62d533 | 470 | public int minWidth = 16, minHeight = 16; |
| a62d533 | 471 | public int maxWidth = 1024, maxHeight = 1024; |
| a62d533 | 472 | public boolean forceSquareOutput = false; |
| a62d533 | 473 | public boolean stripWhitespaceX, stripWhitespaceY; |
| a62d533 | 474 | public int alphaThreshold; |
| a62d533 | 475 | public TextureFilter filterMin = TextureFilter.Nearest, filterMag = TextureFilter.Nearest; |
| a62d533 | 476 | public TextureWrap wrapX = TextureWrap.ClampToEdge, wrapY = TextureWrap.ClampToEdge; |
| a62d533 | 477 | public Format format = Format.RGBA8888; |
| a62d533 | 478 | public boolean alias = true; |
| a62d533 | 479 | public String outputFormat = "png"; |
| a62d533 | 480 | public float jpegQuality = 0.9f; |
| a62d533 | 481 | public boolean ignoreBlankImages = true; |
| a62d533 | 482 | public boolean fast; |
| a62d533 | 483 | public boolean debug; |
| a62d533 | 484 | public boolean combineSubdirectories; |
| a62d533 | 485 | public boolean flattenPaths; |
| a62d533 | 486 | public boolean premultiplyAlpha; |
| a62d533 | 487 | public boolean useIndexes = true; |
| a62d533 | 488 | public boolean bleed = true; |
| a62d533 | 489 | |
| a62d533 | 490 | public Settings () { |
| a62d533 | 491 | } |
| a62d533 | 492 | |
| a62d533 | 493 | public Settings (Settings settings) { |
| a62d533 | 494 | fast = settings.fast; |
| a62d533 | 495 | rotation = settings.rotation; |
| a62d533 | 496 | pot = settings.pot; |
| a62d533 | 497 | minWidth = settings.minWidth; |
| a62d533 | 498 | minHeight = settings.minHeight; |
| a62d533 | 499 | maxWidth = settings.maxWidth; |
| a62d533 | 500 | maxHeight = settings.maxHeight; |
| a62d533 | 501 | paddingX = settings.paddingX; |
| a62d533 | 502 | paddingY = settings.paddingY; |
| a62d533 | 503 | edgePadding = settings.edgePadding; |
| a62d533 | 504 | duplicatePadding = settings.duplicatePadding; |
| a62d533 | 505 | alphaThreshold = settings.alphaThreshold; |
| a62d533 | 506 | ignoreBlankImages = settings.ignoreBlankImages; |
| a62d533 | 507 | stripWhitespaceX = settings.stripWhitespaceX; |
| a62d533 | 508 | stripWhitespaceY = settings.stripWhitespaceY; |
| a62d533 | 509 | alias = settings.alias; |
| a62d533 | 510 | format = settings.format; |
| a62d533 | 511 | jpegQuality = settings.jpegQuality; |
| a62d533 | 512 | outputFormat = settings.outputFormat; |
| a62d533 | 513 | filterMin = settings.filterMin; |
| a62d533 | 514 | filterMag = settings.filterMag; |
| a62d533 | 515 | wrapX = settings.wrapX; |
| a62d533 | 516 | wrapY = settings.wrapY; |
| a62d533 | 517 | debug = settings.debug; |
| a62d533 | 518 | combineSubdirectories = settings.combineSubdirectories; |
| a62d533 | 519 | flattenPaths = settings.flattenPaths; |
| a62d533 | 520 | premultiplyAlpha = settings.premultiplyAlpha; |
| a62d533 | 521 | forceSquareOutput = settings.forceSquareOutput; |
| a62d533 | 522 | useIndexes = settings.useIndexes; |
| a62d533 | 523 | bleed = settings.bleed; |
| a62d533 | 524 | } |
| a62d533 | 525 | } |
| a62d533 | 526 | |
| a62d533 | 527 | static public void process (String input, String output, String packFileName) { |
| a62d533 | 528 | try { |
| a62d533 | 529 | new TexturePackerFileProcessor(new Settings(), packFileName).process(new File(input), new File(output)); |
| a62d533 | 530 | } catch (Exception ex) { |
| a62d533 | 531 | throw new RuntimeException("Error packing files.", ex); |
| a62d533 | 532 | } |
| a62d533 | 533 | } |
| a62d533 | 534 | |
| a62d533 | 535 | static public void process (Settings settings, String input, String output, String packFileName) { |
| a62d533 | 536 | try { |
| a62d533 | 537 | new TexturePackerFileProcessor(settings, packFileName).process(new File(input), new File(output)); |
| a62d533 | 538 | } catch (Exception ex) { |
| a62d533 | 539 | throw new RuntimeException("Error packing files.", ex); |
| a62d533 | 540 | } |
| a62d533 | 541 | } |
| a62d533 | 542 | |
| a62d533 | 543 | /** @return true if the output file does not yet exist or its last modification date is before the last modification date of the |
| a62d533 | 544 | * input file */ |
| a62d533 | 545 | static public boolean isModified (String input, String output, String packFileName) { |
| a62d533 | 546 | String packFullFileName = output; |
| a62d533 | 547 | if (!packFullFileName.endsWith("/")) packFullFileName += "/"; |
| a62d533 | 548 | packFullFileName += packFileName; |
| a62d533 | 549 | File outputFile = new File(packFullFileName); |
| a62d533 | 550 | if (!outputFile.exists()) return true; |
| a62d533 | 551 | |
| a62d533 | 552 | File inputFile = new File(input); |
| a62d533 | 553 | if (!inputFile.exists()) throw new IllegalArgumentException("Input file does not exist: " + inputFile.getAbsolutePath()); |
| a62d533 | 554 | return inputFile.lastModified() > outputFile.lastModified(); |
| a62d533 | 555 | } |
| a62d533 | 556 | |
| a62d533 | 557 | static public void processIfModified (String input, String output, String packFileName) { |
| a62d533 | 558 | if (isModified(input, output, packFileName)) process(input, output, packFileName); |
| a62d533 | 559 | } |
| a62d533 | 560 | |
| a62d533 | 561 | static public void processIfModified (Settings settings, String input, String output, String packFileName) { |
| a62d533 | 562 | if (isModified(input, output, packFileName)) process(settings, input, output, packFileName); |
| a62d533 | 563 | } |
| a62d533 | 564 | } |