*/
private MapLayer readLayer(Node t) throws Exception {
final int layerWidth = getAttribute(t, "width", map.getWidth());
final int layerHeight = getAttribute(t, "height", map.getHeight());
TileLayer ml = new TileLayer(layerWidth, layerHeight);
final int offsetX = getAttribute(t, "x", 0);
final int offsetY = getAttribute(t, "y", 0);
final int visible = getAttribute(t, "visible", 1);
String opacity = getAttributeValue(t, "opacity");
ml.setName(getAttributeValue(t, "name"));
if (opacity != null) {
ml.setOpacity(Float.parseFloat(opacity));
}
readProperties(t.getChildNodes(), ml.getProperties());
for (Node child = t.getFirstChild(); child != null;
child = child.getNextSibling())
{
String nodeName = child.getNodeName();
if ("data".equalsIgnoreCase(nodeName)) {
String encoding = getAttributeValue(child, "encoding");
String comp = getAttributeValue(child, "compression");
if ("base64".equalsIgnoreCase(encoding)) {
Node cdata = child.getFirstChild();
if (cdata != null) {
char[] enc = cdata.getNodeValue().trim().toCharArray();
byte[] dec = Base64.decode(enc);
ByteArrayInputStream bais = new ByteArrayInputStream(dec);
InputStream is;
if ("gzip".equalsIgnoreCase(comp)) {
final int len = layerWidth * layerHeight * 4;
is = new GZIPInputStream(bais, len);
} else if ("zlib".equalsIgnoreCase(comp)) {
is = new InflaterInputStream(bais);
} else if (comp != null && !comp.isEmpty()) {
throw new IOException("Unrecognized compression method \"" + comp + "\" for map layer " + ml.getName());
} else {
is = bais;
}
for (int y = 0; y < ml.getHeight(); y++) {
for (int x = 0; x < ml.getWidth(); x++) {
int tileId = 0;
tileId |= is.read();
tileId |= is.read() << 8;
tileId |= is.read() << 16;
tileId |= is.read() << 24;
setTileAtFromTileId(ml, y, x, tileId);
}
}
}
} else if ("csv".equalsIgnoreCase(encoding)) {
String csvText = child.getTextContent();
if (comp != null && !comp.isEmpty()) {
throw new IOException("Unrecognized compression method \"" + comp + "\" for map layer " + ml.getName() + " and encoding " + encoding);
}
String[] csvTileIds = csvText
.trim() // trim 'space', 'tab', 'newline'. pay attention to additional unicode chars like \u2028, \u2029, \u0085 if necessary
.split("[\\s]*,[\\s]*");
if (csvTileIds.length != ml.getHeight() * ml.getWidth()) {
throw new IOException("Number of tiles does not match the layer's width and height");
}
for (int y = 0; y < ml.getHeight(); y++) {
for (int x = 0; x < ml.getWidth(); x++) {
String sTileId = csvTileIds[x + y * ml.getHeight()];
int tileId = Integer.parseInt(sTileId);
setTileAtFromTileId(ml, y, x, tileId);
}
}
} else {
int x = 0, y = 0;
for (Node dataChild = child.getFirstChild();
dataChild != null;
dataChild = dataChild.getNextSibling())
{
if ("tile".equalsIgnoreCase(dataChild.getNodeName())) {
int tileId = getAttribute(dataChild, "gid", -1);
setTileAtFromTileId(ml, y, x, tileId);
x++;
if (x == ml.getWidth()) {
x = 0; y++;
}
if (y == ml.getHeight()) { break; }
}
}
}
} else if ("tileproperties".equalsIgnoreCase(nodeName)) {
for (Node tpn = child.getFirstChild();
tpn != null;
tpn = tpn.getNextSibling())
{
if ("tile".equalsIgnoreCase(tpn.getNodeName())) {
int x = getAttribute(tpn, "x", -1);
int y = getAttribute(tpn, "y", -1);
Properties tip = new Properties();
readProperties(tpn.getChildNodes(), tip);
ml.setTileInstancePropertiesAt(x, y, tip);
}
}
}
}
// This is done at the end, otherwise the offset is applied during
// the loading of the tiles.
ml.setOffset(offsetX, offsetY);
// Invisible layers are automatically locked, so it is important to
// set the layer to potentially invisible _after_ the layer data is
// loaded.
// todo: Shouldn't this be just a user interface feature, rather than
// todo: something to keep in mind at this level?
ml.setVisible(visible == 1);
return ml;
}