Package com.badlogic.gdx.tools.imagepacker

Source Code of com.badlogic.gdx.tools.imagepacker.TexturePacker$Settings

/*******************************************************************************
* Copyright 2011 See AUTHORS file.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
******************************************************************************/

package com.badlogic.gdx.tools.imagepacker;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.awt.image.WritableRaster;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.FilenameFilter;
import java.io.IOException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.CRC32;

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;

import com.badlogic.gdx.graphics.Pixmap.Format;
import com.badlogic.gdx.graphics.Texture.TextureFilter;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.utils.GdxRuntimeException;

/** Use {@link TexturePacker2}.
* @deprecated */
public class TexturePacker {
  static Pattern indexPattern = Pattern.compile(".+_(\\d+)(_.*|$)");
  static public boolean quiet;

  ArrayList<Image> images = new ArrayList();
  HashMap<String, Image> imageCrcs = new HashMap();
  FileWriter writer;
  int uncompressedSize, compressedSize;
  int xPadding, yPadding;
  final Filter filter;
  int minWidth, minHeight;
  int maxWidth, maxHeight;
  final Settings settings;

  /** Used by squeeze method when ignoreBlankImages is false to add empty region for blank input images during the pack. */
  BufferedImage emptyImage = new BufferedImage(1, 1, BufferedImage.TYPE_4BYTE_ABGR);

  public TexturePacker (Settings settings) {
    this.settings = settings;
    this.filter = new Filter(Direction.none, null, null, -1, -1, null, null);
  }

  public TexturePacker (Settings settings, File inputDir, Filter filter, File outputDir, File packFile) throws IOException {
    this.settings = settings;
    this.filter = filter;

    // Collect and squeeze images.
    File[] files = inputDir.listFiles(filter);
    if (files == null) return;
    for (File file : files) {
      if (file.isDirectory()) continue;
      String imageName = file.getAbsolutePath().substring(inputDir.getAbsolutePath().length()) + "\n";
      if (imageName.startsWith("/") || imageName.startsWith("\\")) imageName = imageName.substring(1);
      int dotIndex = imageName.lastIndexOf('.');
      if (dotIndex != -1) imageName = imageName.substring(0, dotIndex);
      addImage(ImageIO.read(file), imageName);
    }

    if (images.isEmpty()) return;

    log(inputDir.toString());
    if (filter.format != null)
      log("Format: " + filter.format);
    else
      log("Format: " + settings.defaultFormat + " (default)");
    if (filter.minFilter != null && filter.magFilter != null)
      log("Filter: " + filter.minFilter + ", " + filter.magFilter);
    else
      log("Filter: " + settings.defaultFilterMin + ", " + settings.defaultFilterMag + " (default)");
    if (filter.direction != Direction.none) log("Repeat: " + filter.direction);

    process(outputDir, packFile, inputDir.getName());
  }

  static void log (String message) {
    if (!quiet) System.out.println(message);
  }

  public void addImage (BufferedImage image, String name) {
    Image squeezed = squeeze(image, name, false);
    if (squeezed != null) {
      if (settings.alias) {
        String crc = hash(squeezed);
        Image existing = imageCrcs.get(crc);
        if (existing != null) {
          existing.aliases.add(squeezed);
          return;
        }
        imageCrcs.put(crc, squeezed);
      }
      images.add(squeezed);
    }
  }

  public void process (File outputDir, File packFile, String prefix) throws IOException {
    if (images.isEmpty()) return;

    minWidth = filter.width != -1 ? filter.width : settings.minWidth;
    minHeight = filter.height != -1 ? filter.height : settings.minHeight;
    maxWidth = filter.width != -1 ? filter.width : settings.maxWidth;
    maxHeight = filter.height != -1 ? filter.height : settings.maxHeight;
    if (settings.edgePadding) {
      xPadding = !filter.direction.isX() ? settings.padding : 0;
      yPadding = !filter.direction.isY() ? settings.padding : 0;
    } else {
      xPadding = images.size() > 1 && !filter.direction.isX() ? settings.padding : 0;
      yPadding = images.size() > 1 && !filter.direction.isY() ? settings.padding : 0;
    }

    outputDir.mkdirs();
    writer = new FileWriter(packFile, true);
    try {
      while (!images.isEmpty())
        if (!writePage(prefix, outputDir)) break;
      if (writer != null) {
        log("Pixels eliminated: " + (1 - compressedSize / (float)uncompressedSize) * 100 + "%");
        log("");
      }
    } finally {
      writer.close();
    }
  }

  private boolean writePage (String prefix, File outputDir) throws IOException {
    // Try reasonably hard to pack images into the smallest POT size.
    Comparator bestComparator = null;
    Comparator secondBestComparator = imageComparators.get(0);
    int bestWidth = 99999, bestHeight = 99999;
    int secondBestWidth = 99999, secondBestHeight = 99999;
    int bestUsedPixels = 0;
    int width = minWidth, height = minHeight;
    int grownPixels = 0, grownPixels2 = 0;
    int i = 0, ii = 0;
    while (true) {
      for (Comparator comparator : imageComparators) {
        // Pack as many images as possible, sorting the images different
        // ways.
        Collections.sort(images, comparator);
        int usedPixels = insert(null, new ArrayList(images), width, height);
        // Store the best pack, in case not all images fit on the max
        // texture size.
        if (usedPixels > bestUsedPixels) {
          secondBestComparator = comparator;
          secondBestWidth = width;
          secondBestHeight = height;
        }
        // If all images fit and this sort is the best so far, take
        // note.
        if (usedPixels == -1) {
          if (width * height < bestWidth * bestHeight) {
            bestComparator = comparator;
            bestWidth = width;
            bestHeight = height;
          }
        }
      }
      if (width == maxWidth && height == maxHeight) break;
      if (bestComparator != null) break;
      if (settings.pot) {
        // 64,64 -> 128,64 -> 256,64 etc 64,128 -> 64,256 etc -> 128,128
        // -> 256,128 etc.
        if (i % 3 == 0) {
          grownPixels += MathUtils.nextPowerOfTwo(width + 1) - width;
          width = MathUtils.nextPowerOfTwo(width + 1);
          if (width > maxWidth) {
            i++;
            width -= grownPixels;
            grownPixels = 0;
          }
        } else if (i % 3 == 1) {
          grownPixels += MathUtils.nextPowerOfTwo(height + 1) - height;
          height = MathUtils.nextPowerOfTwo(height + 1);
          if (height > maxHeight) {
            i++;
            height -= grownPixels;
            grownPixels = 0;
          }
        } else {
          ii++;
          if (ii % 2 == 1)
            width = MathUtils.nextPowerOfTwo(width + 1);
          else
            height = MathUtils.nextPowerOfTwo(height + 1);
          i++;
        }
      } else {
        // 64-127,64 -> 64,64-127 -> 128-255,128 -> 128,128-255 etc.
        int incr = 2;
        if (i % 3 == 0) {
          if (width + incr >= MathUtils.nextPowerOfTwo(width)) {
            width -= grownPixels;
            grownPixels = 0;
            i++;
          } else {
            width += incr;
            grownPixels += incr;
          }
        } else if (i % 3 == 1) {
          if (height + incr >= MathUtils.nextPowerOfTwo(height)) {
            height -= grownPixels;
            grownPixels = 0;
            i++;
          } else {
            height += incr;
            grownPixels += incr;
          }
        } else {
          if (width == MathUtils.nextPowerOfTwo(width) && height == MathUtils.nextPowerOfTwo(height)) ii++;
          if (ii % 2 == 1)
            width += incr;
          else
            height += incr;
          i++;
        }
      }
      width = Math.min(maxWidth, width);
      height = Math.min(maxHeight, height);
    }
    if (bestComparator != null) {
      Collections.sort(images, bestComparator);
    } else {
      Collections.sort(images, secondBestComparator);
      bestWidth = secondBestWidth;
      bestHeight = secondBestHeight;
    }
    width = bestWidth;
    height = bestHeight;
    if (settings.pot) {
      width = MathUtils.nextPowerOfTwo(width);
      height = MathUtils.nextPowerOfTwo(height);
    }
    if (width > maxWidth || height > maxHeight) {
      System.out.println("ERROR: Images do not fit on max size: " + maxWidth + "x" + maxHeight);
      return false;
    }

    int type;
    switch (filter.format != null ? filter.format : settings.defaultFormat) {
    case RGBA8888:
    case RGBA4444:
      type = BufferedImage.TYPE_INT_ARGB;
      break;
    case RGB565:
    case RGB888:
      type = BufferedImage.TYPE_INT_RGB;
      break;
    case Alpha:
      type = BufferedImage.TYPE_BYTE_GRAY;
      break;
    default:
      throw new RuntimeException("Luminance Alpha format is not supported.");
    }

    FileFormat fileFormat;
    if (filter.fileFormat != null) {
      fileFormat = filter.fileFormat;
    } else {
      fileFormat = settings.defaultFileFormat;
    }

    float imageQuality = settings.defaultImageQuality;

    int imageNumber = 1;
    File outputFile = new File(outputDir, prefix + imageNumber + "." + getFileExtension(fileFormat));
    while (outputFile.exists())
      outputFile = new File(outputDir, prefix + ++imageNumber + "." + getFileExtension(fileFormat));

    writer.write("\n" + outputFile.getName() + "\n");
    Format format;
    if (filter.format != null) {
      writer.write("format: " + filter.format + "\n");
      format = filter.format;
    } else {
      writer.write("format: " + settings.defaultFormat + "\n");
      format = settings.defaultFormat;
    }
    if (filter.minFilter == null || filter.magFilter == null)
      writer.write("filter: " + settings.defaultFilterMin + "," + settings.defaultFilterMag + "\n");
    else
      writer.write("filter: " + filter.minFilter + "," + filter.magFilter + "\n");
    writer.write("repeat: " + filter.direction + "\n");

    BufferedImage canvas = new BufferedImage(width, height, type);
    insert(canvas, images, bestWidth, bestHeight);
    log("Writing " + canvas.getWidth() + "x" + canvas.getHeight() + ": " + outputFile);

    if (fileFormat == FileFormat.JPEG) {
      Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("jpg");
      ImageWriter writer = (ImageWriter)writers.next();
      ImageWriteParam param = writer.getDefaultWriteParam();
      param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
      param.setCompressionQuality(imageQuality);
      ImageOutputStream ios = ImageIO.createImageOutputStream(outputFile);
      writer.setOutput(ios);
      writer.write(null, new IIOImage(canvas, null, null), param);

      if (!settings.pot) {
        ios = ImageIO.createImageOutputStream(outputFile);
        writer.setOutput(ios);
        writer.write(null, new IIOImage(canvas, null, null), param);
      }
    } else {
      ImageIO.write(canvas, getFileExtension(fileFormat), outputFile);
      if (!settings.pot) ImageIO.write(squeeze(ImageIO.read(outputFile), "", true), getFileExtension(fileFormat), outputFile);
    }

    compressedSize += canvas.getWidth() * canvas.getHeight();
    return true;
  }

  private static String getFileExtension (FileFormat fileFormat) {
    String retVal = "";

    switch (fileFormat) {
    case PNG:
      retVal = "png";
      break;

    case JPEG:
      retVal = "jpg";
      break;
    }

    return retVal;
  }

  private int insert (BufferedImage canvas, ArrayList<Image> images, int width, int height) throws IOException {
    if (settings.debug && canvas != null) {
      Graphics g = canvas.getGraphics();
      g.setColor(Color.green);
      g.drawRect(0, 0, width - 1, height - 1);
    }
    int x = 0, y = 0;
    if (settings.edgePadding) {
      if (!filter.direction.isX()) {
        x = xPadding;
        width -= xPadding;
      }
      if (!filter.direction.isY()) {
        y = yPadding;
        height -= yPadding;
      }
    } else {
      // Pretend image is larger so padding on right and bottom edges is
      // ignored.
      if (!filter.direction.isX()) width += xPadding;
      if (!filter.direction.isY()) height += yPadding;
    }
    Node root = new Node(x, y, width, height);
    int usedPixels = 0;
    for (int i = images.size() - 1; i >= 0; i--) {
      Image image = images.get(i);
      Node node = root.insert(image, false);
      if (node == null) {
        if (settings.rotate) node = root.insert(image, true);
        if (node == null) continue;
      }
      usedPixels += image.getWidth() * image.getHeight();
      images.remove(i);
      if (canvas != null) {
        node.writePackEntry();
        Graphics2D g = (Graphics2D)canvas.getGraphics();
        if (image.rotate) {
          g.translate(node.left, node.top);
          g.rotate(-90 * MathUtils.degreesToRadians);
          g.translate(-node.left, -node.top);
          g.translate(-image.getWidth(), 0);
        }
        if (settings.duplicatePadding) {
          int amount = settings.padding / 2;
          int imageWidth = image.getWidth();
          int imageHeight = image.getHeight();
          // Copy corner pixels to fill corners of the padding.
          g.drawImage(image, node.left - amount, node.top - amount, node.left, node.top, 0, 0, 1, 1, null);
          g.drawImage(image, node.left + imageWidth, node.top - amount, node.left + imageWidth + amount, node.top, 0, 0, 1,
            1, null);
          g.drawImage(image, node.left - amount, node.top + imageHeight, node.left, node.top + imageHeight + amount, 0, 0,
            1, 1, null);
          g.drawImage(image, node.left + imageWidth, node.top + imageHeight, node.left + imageWidth + amount, node.top
            + imageHeight + amount, 0, 0, 1, 1, null);
          // Copy edge picels into padding.
          g.drawImage(image, node.left, node.top - amount, node.left + imageWidth, node.top, 0, 0, imageWidth, 1, null);
          g.drawImage(image, node.left, node.top + imageHeight, node.left + imageWidth, node.top + imageHeight + amount, 0,
            imageHeight - 1, imageWidth, imageHeight, null);
          g.drawImage(image, node.left - amount, node.top, node.left, node.top + imageHeight, 0, 0, 1, imageHeight, null);
          g.drawImage(image, node.left + imageWidth, node.top, node.left + imageWidth + amount, node.top + imageHeight,
            imageWidth - 1, 0, imageWidth, imageHeight, null);
        }
        g.drawImage(image, node.left, node.top, null);
        if (image.rotate) {
          g.translate(image.getWidth(), 0);
          g.translate(node.left, node.top);
          g.rotate(90 * MathUtils.degreesToRadians);
          g.translate(-node.left, -node.top);
        }
        if (settings.debug) {
          g.setColor(Color.magenta);
          int imageWidth = image.getWidth();
          int imageHeight = image.getHeight();
          if (image.rotate)
            g.drawRect(node.left, node.top, imageHeight - 1, imageWidth - 1);
          else
            g.drawRect(node.left, node.top, imageWidth - 1, imageHeight - 1);
        }
      }
    }
    return images.isEmpty() ? -1 : usedPixels;
  }

  private Image squeeze (BufferedImage source, String name, boolean skipTopLeft) {
    if (source == null) return null;
    if (!filter.accept(source)) return null;
    uncompressedSize += source.getWidth() * source.getHeight();
    WritableRaster alphaRaster = source.getAlphaRaster();
    if (alphaRaster == null || !settings.stripWhitespace || name.contains("_ws"))
      return new Image(name, source, 0, 0, source.getWidth(), source.getHeight());
    final byte[] a = new byte[1];
    int top = 0;
    int bottom = source.getHeight();
    if (!filter.direction.isY()) {
      if (!skipTopLeft) {
        outer:
        for (int y = 0; y < source.getHeight(); y++) {
          for (int x = 0; x < source.getWidth(); x++) {
            alphaRaster.getDataElements(x, y, a);
            int alpha = a[0];
            if (alpha < 0) alpha += 256;
            if (alpha > settings.alphaThreshold) break outer;
          }
          top++;
        }
      }
      outer:
      for (int y = source.getHeight(); --y >= top;) {
        for (int x = 0; x < source.getWidth(); x++) {
          alphaRaster.getDataElements(x, y, a);
          int alpha = a[0];
          if (alpha < 0) alpha += 256;
          if (alpha > settings.alphaThreshold) break outer;
        }
        bottom--;
      }
    }
    int left = 0;
    int right = source.getWidth();
    if (!filter.direction.isX()) {
      if (!skipTopLeft) {
        outer:
        for (int x = 0; x < source.getWidth(); x++) {
          for (int y = top; y < bottom; y++) {
            alphaRaster.getDataElements(x, y, a);
            int alpha = a[0];
            if (alpha < 0) alpha += 256;
            if (alpha > settings.alphaThreshold) break outer;
          }
          left++;
        }
      }
      outer:
      for (int x = source.getWidth(); --x >= left;) {
        for (int y = top; y < bottom; y++) {
          alphaRaster.getDataElements(x, y, a);
          int alpha = a[0];
          if (alpha < 0) alpha += 256;
          if (alpha > settings.alphaThreshold) break outer;
        }
        right--;
      }
    }
    int newWidth = right - left;
    int newHeight = bottom - top;
    if (newWidth <= 0 || newHeight <= 0) {
      if (settings.ignoreBlankImages) {
        log("Ignoring blank input image: " + name);
        return null;
      } else {
        return new Image(name, emptyImage, 0, 0, 1, 1);
      }
    }
    return new Image(name, source, left, top, newWidth, newHeight);
  }

  static private String hash (BufferedImage image) {
    try {
      MessageDigest digest = MessageDigest.getInstance("SHA1");
      WritableRaster raster = image.getRaster();
      final byte[] pixel = new byte[4];
      for (int y = 0; y < image.getHeight(); y++) {
        for (int x = 0; x < image.getWidth(); x++) {
          raster.getDataElements(x, y, pixel);
          digest.update(pixel);
        }
      }
      return new BigInteger(1, digest.digest()).toString(16);
    } catch (NoSuchAlgorithmException ex) {
      throw new RuntimeException(ex);
    }
  }

  private class Node {
    int left, top, width, height;
    Node child1, child2;
    Image image;

    public Node (int left, int top, int width, int height) {
      this.left = left;
      this.top = top;
      this.width = width;
      this.height = height;
    }

    /** Returns true if the image was inserted. If canvas != null, an entry is written to the pack file. */
    public Node insert (Image image, boolean rotate) throws IOException {
      if (this.image != null) return null;
      if (child1 != null) {
        Node newNode = child1.insert(image, rotate);
        if (newNode != null) return newNode;
        return child2.insert(image, rotate);
      }
      int imageWidth = image.getWidth();
      int imageHeight = image.getHeight();
      if (rotate) {
        int temp = imageWidth;
        imageWidth = imageHeight;
        imageHeight = temp;
      }
      int neededWidth = imageWidth + xPadding;
      int neededHeight = imageHeight + yPadding;
      if (neededWidth > width || neededHeight > height) return null;
      if (neededWidth == width && neededHeight == height) {
        this.image = image;
        image.rotate = rotate;
        return this;
      }
      int dw = width - neededWidth;
      int dh = height - neededHeight;
      if (dw > dh) {
        child1 = new Node(left, top, neededWidth, height);
        child2 = new Node(left + neededWidth, top, width - neededWidth, height);
      } else {
        child1 = new Node(left, top, width, neededHeight);
        child2 = new Node(left, top + neededHeight, width, height - neededHeight);
      }
      return child1.insert(image, rotate);
    }

    void writePackEntry () throws IOException {
      writePackEntry(image, null);
      for (Image alias : image.aliases)
        writePackEntry(alias, image);
    }

    /** @param image The Image to be written to the pack.
     * @param source If the Image is an alias, the source is the original Image, otherwise null is expected. */
    private void writePackEntry (Image image, Image source) throws IOException {
      String imageName = image.name;
      imageName = imageName.replace("\\", "/");

      log("Packing... " + imageName + (source != null ? " (alias)" : ""));

      Matcher matcher = indexPattern.matcher(imageName);
      int index = -1;
      if (matcher.matches()) index = Integer.parseInt(matcher.group(1));

      int underscoreIndex = imageName.indexOf('_');
      if (underscoreIndex != -1) imageName = imageName.substring(0, underscoreIndex);

      boolean rotate = source != null ? source.rotate : image.rotate;

      writer.write(imageName + "\n");
      writer.write("  rotate: " + rotate + "\n");
      writer.write("  xy: " + left + ", " + top + "\n");
      writer.write("  size: " + image.getWidth() + ", " + image.getHeight() + "\n");
      writer.write("  orig: " + image.originalWidth + ", " + image.originalHeight + "\n");
      writer.write("  offset: " + image.offsetX + ", " + (image.originalHeight - image.getHeight() - image.offsetY) + "\n");
      writer.write("  index: " + index + "\n");
    }
  }

  static private class Image extends BufferedImage {
    final String name;
    final int offsetX, offsetY;
    final int originalWidth, originalHeight;
    boolean rotate;
    ArrayList<Image> aliases = new ArrayList();

    public Image (String name, BufferedImage src, int left, int top, int newWidth, int newHeight) {
      super(src.getColorModel(), src.getRaster().createWritableChild(left, top, newWidth, newHeight, 0, 0, null), src
        .getColorModel().isAlphaPremultiplied(), null);
      this.name = name;
      offsetX = left;
      offsetY = top;
      originalWidth = src.getWidth();
      originalHeight = src.getHeight();
    }

    public String toString () {
      return name;
    }
  }

  static private ArrayList<Comparator> imageComparators = new ArrayList();
  static {
    imageComparators.add(new Comparator<Image>() {
      public int compare (Image image1, Image image2) {
        int diff = image1.getHeight() - image2.getHeight();
        if (diff != 0) return diff;
        return image1.getWidth() - image2.getWidth();
      }
    });
    imageComparators.add(new Comparator<Image>() {
      public int compare (Image image1, Image image2) {
        int diff = image1.getWidth() - image2.getWidth();
        if (diff != 0) return diff;
        return image1.getHeight() - image2.getHeight();
      }
    });
    imageComparators.add(new Comparator<Image>() {
      public int compare (Image image1, Image image2) {
        return image1.getWidth() * image1.getHeight() - image2.getWidth() * image2.getHeight();
      }
    });
  }

  public enum FileFormat {
    PNG, JPEG;
  }

  static private class Filter implements FilenameFilter {
    Direction direction;
    FileFormat fileFormat;
    Format format;
    TextureFilter minFilter;
    TextureFilter magFilter;
    int width = -1;
    int height = -1;
    Settings settings;

    public Filter (Direction direction, FileFormat fileFormat, Format format, int width, int height, TextureFilter minFilter,
      TextureFilter magFilter) {
      this.direction = direction;
      this.fileFormat = fileFormat;
      this.format = format;
      this.width = width;
      this.height = height;
      this.minFilter = minFilter;
      this.magFilter = magFilter;
    }

    public boolean accept (File dir, String name) {
      switch (direction) {
      case none:
        if (name.contains("_x") || name.contains("_y")) return false;
        break;
      case x:
        if (!name.contains("_x") || name.contains("_xy")) return false;
        break;
      case y:
        if (!name.contains("_y") || name.contains("_xy")) return false;
        break;
      case xy:
        if (!name.contains("_xy")) return false;
        break;
      }

      if (format != null) {
        if (!name.contains("_" + formatToAbbrev.get(format))) return false;
      } else {
        // Return if name has a format.
        for (String f : formatToAbbrev.values())
          if (name.contains("_" + f)) return false;
      }

      if (minFilter != null && magFilter != null) {
        if (!name.contains("_" + filterToAbbrev.get(minFilter) + "," + filterToAbbrev.get(magFilter) + ".")
          && !name.contains("_" + filterToAbbrev.get(minFilter) + "," + filterToAbbrev.get(magFilter) + "_")) return false;
      } else {
        // Return if the name has a filter.
        for (String f : filterToAbbrev.values()) {
          String tag = "_" + f + ",";
          int tagIndex = name.indexOf(tag);
          if (tagIndex != -1) {
            String rest = name.substring(tagIndex + tag.length());
            for (String f2 : filterToAbbrev.values())
              if (rest.startsWith(f2 + ".") || rest.startsWith(f2 + "_")) return false;
          }
        }
      }

      return true;
    }

    public boolean accept (BufferedImage image) {
      if (width != -1 && image.getWidth() != width) return false;
      if (height != -1 && image.getHeight() != height) return false;
      return true;
    }
  }

  static private enum Direction {
    x, y, xy, none;

    public boolean isX () {
      return this == x || this == xy;
    }

    public boolean isY () {
      return this == y || this == xy;
    }
  }

  static final HashMap<TextureFilter, String> filterToAbbrev = new HashMap();
  static {
    filterToAbbrev.put(TextureFilter.Linear, "l");
    filterToAbbrev.put(TextureFilter.Nearest, "n");
    filterToAbbrev.put(TextureFilter.MipMap, "m");
    filterToAbbrev.put(TextureFilter.MipMapLinearLinear, "mll");
    filterToAbbrev.put(TextureFilter.MipMapLinearNearest, "mln");
    filterToAbbrev.put(TextureFilter.MipMapNearestLinear, "mnl");
    filterToAbbrev.put(TextureFilter.MipMapNearestNearest, "mnn");
  }

  static final HashMap<Format, String> formatToAbbrev = new HashMap();
  static {
    formatToAbbrev.put(Format.RGBA8888, "rgba8");
    formatToAbbrev.put(Format.RGBA4444, "rgba4");
    formatToAbbrev.put(Format.RGB565, "rgb565");
    formatToAbbrev.put(Format.Alpha, "a");
  }

  /** Use {@link TexturePacker2}.
   * @deprecated */
  static public class Settings {
    public Format defaultFormat = Format.RGBA8888;
    public float defaultImageQuality = 0.9f;
    public FileFormat defaultFileFormat = FileFormat.PNG;
    public TextureFilter defaultFilterMin = TextureFilter.Nearest;
    public TextureFilter defaultFilterMag = TextureFilter.Nearest;
    public int alphaThreshold = 0;
    public boolean pot = true;
    public int padding = 2;
    public boolean duplicatePadding = true;
    public boolean debug = false;
    public boolean rotate = false;
    public int minWidth = 128;
    public int minHeight = 128;
    public int maxWidth = 1024;
    public int maxHeight = 1024;
    public boolean stripWhitespace = false;
    public boolean incremental;
    public boolean alias = true;
    public boolean edgePadding = true;

    /** True if blank images should be ignored when building the texture pack or false if empty regions should be created for
     * them. */
    public boolean ignoreBlankImages = true;

    /** Specifies the crc file path used when incremental is enabled, if not specified the file is created in the user folder,
     * inside the .texturepacker folder. */
    public String incrementalFilePath = null;

    HashMap<String, Long> crcs = new HashMap();
    HashMap<String, String> packSections = new HashMap();
  }

  static private void process (Settings settings, File rootDir, File inputDir, File outputDir, File packFile) throws IOException {
    if (inputDir.getName().startsWith(".")) return;

    // Abort if nothing has changed.
    boolean skip = false;
    if (settings.incremental) {
      File[] files = inputDir.listFiles();
      if (files == null) return;
      boolean noneHaveChanged = true;
      int childCountNow = 0;

      // added flag to generate the crc file using local paths in case the
      // incrementalFilePath is
      // specified.
      boolean useAbsolutePaths = true;

      if (settings.incrementalFilePath != null) useAbsolutePaths = false;

      for (File file : files) {
        if (file.isDirectory()) continue;
        String path = file.getAbsolutePath();

        if (!useAbsolutePaths) {
          String rootFolderAbsolutePath = rootDir.getAbsolutePath();
          if (isSubPath(rootFolderAbsolutePath, path)) path = removeSubPath(rootFolderAbsolutePath, path);
        }

        Long crcOld = settings.crcs.get(path);
        long crcNow = crc(file);
        if (crcOld == null || crcOld != crcNow) noneHaveChanged = false;

        settings.crcs.put(path, crcNow);
        childCountNow++;
      }

      String path = inputDir.getAbsolutePath();

      if (!useAbsolutePaths) {
        String rootFolderAbsolutePath = rootDir.getAbsolutePath();
        if (isSubPath(rootFolderAbsolutePath, path)) path = removeSubPath(rootFolderAbsolutePath, path);
      }

      Long childCountOld = settings.crcs.get(path);
      if (childCountOld == null || childCountNow != childCountOld) noneHaveChanged = false;
      settings.crcs.put(path, (long)childCountNow);

      if (outputDir.exists()) {
        boolean foundPage = false;
        String prefix = inputDir.getName();
        for (File file : outputDir.listFiles()) {
          if (file.getName().startsWith(prefix)) {
            foundPage = true;
            break;
          }
        }
        if (!foundPage) noneHaveChanged = false;
      }

      String section = settings.packSections.get(inputDir.getName());
      if (noneHaveChanged && section != null) {
        FileWriter writer = new FileWriter(packFile, true);
        writer.append(section);
        writer.close();

        log(inputDir.toString());
        log("Skipping unchanged directory.");
        log("");
        skip = true;
      }
    }

    if (!skip) {
      // Clean existing page images.
      if (outputDir.exists()) {
        String prefix = inputDir.getName();
        for (File file : outputDir.listFiles())
          if (file.getName().startsWith(prefix)
            && file.getName().endsWith("." + getFileExtension(settings.defaultFileFormat))) file.delete();
      }

      // Just check all combinations, because we are extremely lazy.
      ArrayList<TextureFilter> filters = new ArrayList();
      filters.add(null);
      filters.addAll(Arrays.asList(TextureFilter.values()));
      ArrayList<Format> formats = new ArrayList();
      formats.add(null);
      formats.addAll(Arrays.asList(Format.values()));
      for (int i = 0, n = formats.size(); i < n; i++) {
        Format format = formats.get(i);
        for (int ii = 0, nn = filters.size(); ii < nn; ii++) {
          TextureFilter min = filters.get(ii);
          for (int iii = 0; iii < nn; iii++) {
            TextureFilter mag = filters.get(iii);
            if ((min == null && mag != null) || (min != null && mag == null)) continue;

            Filter filter = new Filter(Direction.none, settings.defaultFileFormat, format, -1, -1, min, mag);
            new TexturePacker(settings, inputDir, filter, outputDir, packFile);

            for (int width = settings.minWidth; width <= settings.maxWidth; width <<= 1) {
              filter = new Filter(Direction.x, settings.defaultFileFormat, format, width, -1, min, mag);
              new TexturePacker(settings, inputDir, filter, outputDir, packFile);
            }

            for (int height = settings.minHeight; height <= settings.maxHeight; height <<= 1) {
              filter = new Filter(Direction.y, settings.defaultFileFormat, format, -1, height, min, mag);
              new TexturePacker(settings, inputDir, filter, outputDir, packFile);
            }

            for (int width = settings.minWidth; width <= settings.maxWidth; width <<= 1) {
              for (int height = settings.minHeight; height <= settings.maxHeight; height <<= 1) {
                filter = new Filter(Direction.xy, settings.defaultFileFormat, format, width, height, min, mag);
                new TexturePacker(settings, inputDir, filter, outputDir, packFile);
              }
            }
          }
        }
      }
    }

    // Process subdirectories.
    File[] files = inputDir.listFiles();
    if (files == null) return;
    for (File file : files)
      if (file.isDirectory()) process(settings, rootDir, file, outputDir, packFile);
  }

  static public void process (Settings settings, String input, String output) {
    process(settings, input, output, "pack");
  }

  static public void process (Settings settings, String input, String output, String packFileName) {
    try {
      File inputDir = new File(input);
      File outputDir = new File(output);

      if (!inputDir.isDirectory()) {
        System.out.println("Not a directory: " + inputDir);
        return;
      }

      File packFile = new File(outputDir, packFileName);

      // Load incremental file.
      File incrmentalFile = null;
      if (settings.incremental && packFile.exists()) {
        settings.crcs.clear();

        // if localIncrementFile
        String incrementalFilePath = settings.incrementalFilePath;

        if (incrementalFilePath == null)
          incrementalFilePath = System.getProperty("user.home") + "/.texturepacker/" + hash(inputDir.getAbsolutePath());

        incrmentalFile = new File(incrementalFilePath);

        if (incrmentalFile.exists()) {
          BufferedReader reader = new BufferedReader(new FileReader(incrmentalFile));
          while (true) {
            String path = reader.readLine();
            if (path == null) break;
            String crc = reader.readLine();
            if (crc == null) break;
            settings.crcs.put(path, Long.parseLong(crc));
          }
          reader.close();
        }

        // Store the pack file text for each section.
        BufferedReader reader = new BufferedReader(new FileReader(packFile));
        StringBuilder buffer = new StringBuilder(2048);
        while (true) {
          String imageName = reader.readLine();
          if (imageName == null) break;
          if (imageName.length() == 0) continue;

          String pageName = imageName.replaceAll("\\d+." + getFileExtension(settings.defaultFileFormat) + "$", "");
          String section = settings.packSections.get(pageName);
          if (section != null) buffer.append(section);

          // buffer.append("****start\n");
          buffer.append('\n');
          buffer.append(imageName);
          buffer.append('\n');
          while (true) {
            String line = reader.readLine();
            if (line == null || line.length() == 0) break;
            buffer.append(line);
            buffer.append('\n');
          }
          settings.packSections.put(pageName, buffer.toString());
          buffer.setLength(0);
        }
        reader.close();
      }

      // Clean pack file.
      packFile.delete();

      process(settings, inputDir, inputDir, outputDir, packFile);

      // Write incrmental file.
      if (incrmentalFile != null) {
        incrmentalFile.getParentFile().mkdirs();
        FileWriter writer = new FileWriter(incrmentalFile);
        for (Entry<String, Long> entry : settings.crcs.entrySet()) {
          writer.write(entry.getKey() + "\n");
          writer.write(entry.getValue() + "\n");
        }
        writer.close();
      }
    } catch (IOException ex) {
      throw new GdxRuntimeException("Error packing images: " + input, ex);
    }
  }

  static private String hash (String value) {
    try {
      MessageDigest digest = MessageDigest.getInstance("SHA1");
      digest.update(value.getBytes());
      return new BigInteger(1, digest.digest()).toString(16);
    } catch (NoSuchAlgorithmException ex) {
      throw new RuntimeException(ex);
    }
  }

  static private long crc (File file) {
    try {
      FileInputStream input = new FileInputStream(file);
      byte[] buffer = new byte[4096];
      CRC32 crc32 = new CRC32();
      while (true) {
        int length = input.read(buffer);
        if (length == -1) break;
        crc32.update(buffer, 0, length);
      }
      input.close();
      return crc32.getValue();
    } catch (IOException ex) {
      throw new RuntimeException(ex);
    }
  }

  static boolean isSubPath (String path, String subPath) {
    if (subPath.length() < path.length()) return false;
    String subPathSubString = subPath.substring(0, path.length());
    return subPathSubString.equals(path);
  }

  static boolean isAbsolutePath (String path) {
    return new File(path).isAbsolute();
  }

  static String removeSubPath (String path, String subPath) {
    return subPath.replace(path, "");
  }

  static public void main (String[] args) throws Exception {
    String input, output;
    if (args.length != 2) {
      System.out.println("Usage: INPUTDIR OUTPUTDIR");
      return;
    }
    input = args[0];
    output = args[1];
    Settings settings = new Settings();
    settings.alias = true;
    process(settings, input, output);
  }
}
TOP

Related Classes of com.badlogic.gdx.tools.imagepacker.TexturePacker$Settings

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.