Methapolis  0.27
 All Classes Namespaces Files Functions Variables Enumerator
MakeTiles.java
Go to the documentation of this file.
1 package micropolisj.build_tool;
2 
3 import micropolisj.engine.TileSpec;
4 import java.awt.*;
5 import java.awt.image.BufferedImage;
6 import java.io.*;
7 import java.nio.charset.Charset;
8 import java.util.*;
9 import javax.imageio.*;
10 import javax.swing.ImageIcon;
11 
12 public class MakeTiles
13 {
14  static HashMap<String,String> tileData = new HashMap<String,String>();
15  static HashMap<String,SourceImage> loadedImages = new HashMap<String,SourceImage>();
16 
17  static final Charset UTF8 = Charset.forName("UTF-8");
18  static int SKIP_TILES = 0;
19  static int NTILES = 1005;
20  static int TILE_SIZE = 16;
21 
22  public static void main(String [] args)
23  throws Exception
24  {
25  if (args.length != 2) {
26  throw new Exception("Wrong number of arguments");
27  }
28 
29  if (System.getProperty("tile_size") != null) {
30  TILE_SIZE = Integer.parseInt(System.getProperty("tile_size"));
31  }
32  if (System.getProperty("skip_tiles") != null) {
33  SKIP_TILES = Integer.parseInt(System.getProperty("skip_tiles"));
34  }
35  if (System.getProperty("tile_count") != null) {
36  NTILES = Integer.parseInt(System.getProperty("tile_count"));
37  }
38 
39  File recipeFile = new File(args[0]);
40  File outputFile = new File(args[1]);
41 
42  Properties recipe = new Properties();
43  recipe.load(
44  new InputStreamReader(
45  new FileInputStream(recipeFile),
46  UTF8
47  ));
48 
49  // actually assemble the image
50  BufferedImage buf = new BufferedImage(TILE_SIZE,TILE_SIZE*NTILES,BufferedImage.TYPE_INT_RGB);
51  Graphics2D gr = buf.createGraphics();
52 
53  for (int i = 0; i < NTILES; i++) {
54  int tileNumber = SKIP_TILES + i;
55  String rawSpec = recipe.getProperty(Integer.toString(tileNumber));
56  if (rawSpec == null) {
57  continue;
58  }
59 
60  TileSpec tileSpec = TileSpec.parse(tileNumber, rawSpec, recipe);
61  FrameSpec ref = parseFrameSpec(tileSpec);
62  if (ref == null) {
63  // tile is defined, but it has no images
64  continue;
65  }
66 
67  drawTo(ref, gr, 0, TILE_SIZE*i);
68  }
69 
70  System.out.println("Generating tiles array: "+outputFile);
71  ImageIO.write(buf, "png", outputFile);
72  }
73 
74  static void drawTo(FrameSpec ref, Graphics2D gr, int destX, int destY)
75  throws IOException
76  {
77  if (ref.background != null) {
78  drawTo(ref.background, gr, destX, destY);
79  }
80 
81  if (!loadedImages.containsKey(ref.fileName)) {
82  loadedImages.put(ref.fileName,
83  loadImage(ref.fileName));
84  }
85 
86  SourceImage sourceImg = loadedImages.get(ref.fileName);
87 
88  gr.drawImage(
89  sourceImg.image,
90  destX, destY,
91  destX+TILE_SIZE, destY+TILE_SIZE,
92  ref.offsetX * sourceImg.basisSize / 16,
93  ref.offsetY * sourceImg.basisSize / 16,
94  (ref.offsetX + (ref.width != 0 ? ref.width : 16)) * sourceImg.basisSize / 16,
95  (ref.offsetY + (ref.height != 0 ? ref.height : 16)) * sourceImg.basisSize / 16,
96  null);
97  }
98 
99  static class SourceImage
100  {
101  Image image;
102  int basisSize;
103 
104  SourceImage(Image image, int basisSize) {
105  this.image = image;
106  this.basisSize = basisSize;
107  }
108  }
109 
110  static class FrameSpec
111  {
112  FrameSpec background;
113  String fileName;
114  int offsetX;
115  int offsetY;
116  int width;
117  int height;
118  }
119 
120  static FrameSpec parseFrameSpec(TileSpec spec)
121  {
122  FrameSpec result = null;
123 
124  for (String layerStr : spec.getImages()) {
125 
126  FrameSpec rv = new FrameSpec();
127  rv.background = result;
128  result = rv;
129 
130  String [] parts = layerStr.split("@", 2);
131  rv.fileName = parts[0];
132 
133  if (parts.length >= 2) {
134  String offsetInfo = parts[1];
135  parts = offsetInfo.split(",");
136  if (parts.length >= 1) {
137  rv.offsetX = Integer.parseInt(parts[0]);
138  }
139  if (parts.length >= 2) {
140  rv.offsetY = Integer.parseInt(parts[1]);
141  }
142  if (parts.length >= 3) {
143  rv.width = Integer.parseInt(parts[2]);
144  }
145  if (parts.length >= 4) {
146  rv.height = Integer.parseInt(parts[3]);
147  }
148  }//endif something given after '@' in image specifier
149 
150  }//end foreach layer in image specification
151 
152  return result;
153  }
154 
155  static File findInkscape()
156  {
157  String exeName = "inkscape";
158  if (System.getProperty("os.name").startsWith("Windows")) {
159  exeName += ".exe";
160  }
161 
162  File [] pathsToTry = {
163  new File("/usr/bin"),
164  new File("c:\\Program Files\\Inkscape"),
165  new File("c:\\Program Files (x86)\\Inkscape")
166  };
167  for (File p : pathsToTry) {
168  File f = new File(p, exeName);
169  if (f.exists()) {
170  return f;
171  }
172  }
173  throw new Error("INKSCAPE not installed (or not found)");
174  }
175 
176  static File stagingDir = new File("generated");
177  static File renderSvg(String fileName, File svgFile)
178  throws IOException
179  {
180  File pngFile = new File(stagingDir, fileName+"_"+TILE_SIZE+"x"+TILE_SIZE+".png");
181  if (pngFile.exists() &&
182  pngFile.lastModified() > svgFile.lastModified())
183  {
184  // looks like the PNG file is already up-to-date
185  return pngFile;
186  }
187 
188  File inkscapeBin = findInkscape();
189 
190  System.out.println("Generating raster image: "+pngFile);
191  if (pngFile.exists()) {
192  pngFile.delete();
193  }
194  else {
195  pngFile.getParentFile().mkdirs();
196  }
197 
198  String [] cmdline = {
199  inkscapeBin.toString(),
200  "--export-dpi="+(TILE_SIZE*90.0/16.0),
201  "--export-png="+pngFile.toString(),
202  svgFile.toString()
203  };
204  Process p = Runtime.getRuntime().exec(cmdline);
205  int exit_value;
206  try {
207  exit_value = p.waitFor();
208  }
209  catch (InterruptedException e) {
210  throw new RuntimeException(e);
211  }
212 
213  if (exit_value != 0) {
214  throw new RuntimeException("Helper exit status: "+exit_value);
215  }
216 
217  if (!pngFile.exists()) {
218  throw new RuntimeException("File not found: "+pngFile);
219  }
220 
221  return pngFile;
222  }
223 
224  static SourceImage loadImage(String fileName)
225  throws IOException
226  {
227  File svgFile, pngFile = null;
228 
229  svgFile = new File(fileName+"_"+TILE_SIZE+"x"+TILE_SIZE+".svg");
230 
231  if (svgFile.exists()) {
232  pngFile = renderSvg(fileName, svgFile);
233  }
234  else {
235  svgFile = new File(fileName+".svg");
236  if (svgFile.exists()) {
237  pngFile = renderSvg(fileName, svgFile);
238  }
239  }
240 
241  if (pngFile != null && pngFile.exists()) {
242  ImageIcon ii = new ImageIcon(pngFile.toString());
243  return new SourceImage(
244  ii.getImage(),
245  TILE_SIZE);
246  }
247 
248  pngFile = new File(fileName+"_"+TILE_SIZE+"x"+TILE_SIZE+".png");
249  if (pngFile.exists()) {
250  ImageIcon ii = new ImageIcon(pngFile.toString());
251  return new SourceImage(
252  ii.getImage(),
253  TILE_SIZE);
254  }
255 
256  pngFile = new File(fileName+".png");
257  if (pngFile.exists()) {
258  ImageIcon ii = new ImageIcon(pngFile.toString());
259  return new SourceImage(
260  ii.getImage(),
261  16);
262  }
263 
264  throw new IOException("File not found: "+fileName+".{svg,png}");
265  }
266 }