Home | History | Annotate | Download | only in texturepacker
      1 /*******************************************************************************
      2  * Copyright 2011 See AUTHORS file.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *   http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  ******************************************************************************/
     16 
     17 package com.badlogic.gdx.tools.texturepacker;
     18 
     19 import com.badlogic.gdx.tools.FileProcessor;
     20 import com.badlogic.gdx.tools.texturepacker.TexturePacker.Settings;
     21 import com.badlogic.gdx.utils.GdxRuntimeException;
     22 import com.badlogic.gdx.utils.Json;
     23 import com.badlogic.gdx.utils.JsonReader;
     24 import com.badlogic.gdx.utils.ObjectMap;
     25 import com.badlogic.gdx.utils.ObjectSet;
     26 
     27 import java.io.File;
     28 import java.io.FileReader;
     29 import java.util.ArrayList;
     30 import java.util.Collections;
     31 import java.util.Comparator;
     32 import java.util.regex.Matcher;
     33 import java.util.regex.Pattern;
     34 
     35 /** @author Nathan Sweet */
     36 public class TexturePackerFileProcessor extends FileProcessor {
     37 	private final Settings defaultSettings;
     38 	private ObjectMap<File, Settings> dirToSettings = new ObjectMap();
     39 	private Json json = new Json();
     40 	private String packFileName;
     41 	private File root;
     42 	ArrayList<File> ignoreDirs = new ArrayList();
     43 
     44 	public TexturePackerFileProcessor () {
     45 		this(new Settings(), "pack.atlas");
     46 	}
     47 
     48 	public TexturePackerFileProcessor (Settings defaultSettings, String packFileName) {
     49 		this.defaultSettings = defaultSettings;
     50 
     51 		if (packFileName.toLowerCase().endsWith(defaultSettings.atlasExtension.toLowerCase()))
     52 			packFileName = packFileName.substring(0, packFileName.length() - defaultSettings.atlasExtension.length());
     53 		this.packFileName = packFileName;
     54 
     55 		setFlattenOutput(true);
     56 		addInputSuffix(".png", ".jpg", ".jpeg");
     57 	}
     58 
     59 	public ArrayList<Entry> process (File inputFile, File outputRoot) throws Exception {
     60 		root = inputFile;
     61 
     62 		// Collect pack.json setting files.
     63 		final ArrayList<File> settingsFiles = new ArrayList();
     64 		FileProcessor settingsProcessor = new FileProcessor() {
     65 			protected void processFile (Entry inputFile) throws Exception {
     66 				settingsFiles.add(inputFile.inputFile);
     67 			}
     68 		};
     69 		settingsProcessor.addInputRegex("pack\\.json");
     70 		settingsProcessor.process(inputFile, null);
     71 		// Sort parent first.
     72 		Collections.sort(settingsFiles, new Comparator<File>() {
     73 			public int compare (File file1, File file2) {
     74 				return file1.toString().length() - file2.toString().length();
     75 			}
     76 		});
     77 		for (File settingsFile : settingsFiles) {
     78 			// Find first parent with settings, or use defaults.
     79 			Settings settings = null;
     80 			File parent = settingsFile.getParentFile();
     81 			while (true) {
     82 				if (parent.equals(root)) break;
     83 				parent = parent.getParentFile();
     84 				settings = dirToSettings.get(parent);
     85 				if (settings != null) {
     86 					settings = new Settings(settings);
     87 					break;
     88 				}
     89 			}
     90 			if (settings == null) settings = new Settings(defaultSettings);
     91 			// Merge settings from current directory.
     92 			merge(settings, settingsFile);
     93 			dirToSettings.put(settingsFile.getParentFile(), settings);
     94 		}
     95 
     96 		// Do actual processing.
     97 		return super.process(inputFile, outputRoot);
     98 	}
     99 
    100 	private void merge (Settings settings, File settingsFile) {
    101 		try {
    102 			json.readFields(settings, new JsonReader().parse(new FileReader(settingsFile)));
    103 		} catch (Exception ex) {
    104 			throw new GdxRuntimeException("Error reading settings file: " + settingsFile, ex);
    105 		}
    106 	}
    107 
    108 	public ArrayList<Entry> process (File[] files, File outputRoot) throws Exception {
    109 		// Delete pack file and images.
    110 		if (outputRoot.exists()) {
    111 			// Load root settings to get scale.
    112 			File settingsFile = new File(root, "pack.json");
    113 			Settings rootSettings = defaultSettings;
    114 			if (settingsFile.exists()) {
    115 				rootSettings = new Settings(rootSettings);
    116 				merge(rootSettings, settingsFile);
    117 			}
    118 
    119 			for (int i = 0, n = rootSettings.scale.length; i < n; i++) {
    120 				FileProcessor deleteProcessor = new FileProcessor() {
    121 					protected void processFile (Entry inputFile) throws Exception {
    122 						inputFile.inputFile.delete();
    123 					}
    124 				};
    125 				deleteProcessor.setRecursive(false);
    126 
    127 				String scaledPackFileName = rootSettings.getScaledPackFileName(packFileName, i);
    128 				File packFile = new File(scaledPackFileName);
    129 
    130 				String prefix = packFile.getName();
    131 				int dotIndex = prefix.lastIndexOf('.');
    132 				if (dotIndex != -1) prefix = prefix.substring(0, dotIndex);
    133 				deleteProcessor.addInputRegex("(?i)" + prefix + "\\d*\\.(png|jpg|jpeg)");
    134 				deleteProcessor.addInputRegex("(?i)" + prefix + "\\.atlas");
    135 
    136 				String dir = packFile.getParent();
    137 				if (dir == null)
    138 					deleteProcessor.process(outputRoot, null);
    139 				else if (new File(outputRoot + "/" + dir).exists()) //
    140 					deleteProcessor.process(outputRoot + "/" + dir, null);
    141 			}
    142 		}
    143 		return super.process(files, outputRoot);
    144 	}
    145 
    146 	protected void processDir (Entry inputDir, ArrayList<Entry> files) throws Exception {
    147 		if (ignoreDirs.contains(inputDir.inputFile)) return;
    148 
    149 		// Find first parent with settings, or use defaults.
    150 		Settings settings = null;
    151 		File parent = inputDir.inputFile;
    152 		while (true) {
    153 			settings = dirToSettings.get(parent);
    154 			if (settings != null) break;
    155 			if (parent.equals(root)) break;
    156 			parent = parent.getParentFile();
    157 		}
    158 		if (settings == null) settings = defaultSettings;
    159 
    160 		if (settings.combineSubdirectories) {
    161 			// Collect all files under subdirectories and ignore subdirectories so they won't be packed twice.
    162 			files = new FileProcessor(this) {
    163 				protected void processDir (Entry entryDir, ArrayList<Entry> files) {
    164 					ignoreDirs.add(entryDir.inputFile);
    165 				}
    166 
    167 				protected void processFile (Entry entry) {
    168 					addProcessedFile(entry);
    169 				}
    170 			}.process(inputDir.inputFile, null);
    171 		}
    172 
    173 		if (files.isEmpty()) return;
    174 
    175 		// Sort by name using numeric suffix, then alpha.
    176 		Collections.sort(files, new Comparator<Entry>() {
    177 			final Pattern digitSuffix = Pattern.compile("(.*?)(\\d+)$");
    178 
    179 			public int compare (Entry entry1, Entry entry2) {
    180 				String full1 = entry1.inputFile.getName();
    181 				int dotIndex = full1.lastIndexOf('.');
    182 				if (dotIndex != -1) full1 = full1.substring(0, dotIndex);
    183 
    184 				String full2 = entry2.inputFile.getName();
    185 				dotIndex = full2.lastIndexOf('.');
    186 				if (dotIndex != -1) full2 = full2.substring(0, dotIndex);
    187 
    188 				String name1 = full1, name2 = full2;
    189 				int num1 = 0, num2 = 0;
    190 
    191 				Matcher matcher = digitSuffix.matcher(full1);
    192 				if (matcher.matches()) {
    193 					try {
    194 						num1 = Integer.parseInt(matcher.group(2));
    195 						name1 = matcher.group(1);
    196 					} catch (Exception ignored) {
    197 					}
    198 				}
    199 				matcher = digitSuffix.matcher(full2);
    200 				if (matcher.matches()) {
    201 					try {
    202 						num2 = Integer.parseInt(matcher.group(2));
    203 						name2 = matcher.group(1);
    204 					} catch (Exception ignored) {
    205 					}
    206 				}
    207 				int compare = name1.compareTo(name2);
    208 				if (compare != 0 || num1 == num2) return compare;
    209 				return num1 - num2;
    210 			}
    211 		});
    212 
    213 		// Pack.
    214 		if (!settings.silent) System.out.println(inputDir.inputFile.getName());
    215 		TexturePacker packer = new TexturePacker(root, settings);
    216 		for (Entry file : files)
    217 			packer.addImage(file.inputFile);
    218 		packer.pack(inputDir.outputDir, packFileName);
    219 	}
    220 }
    221