All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.mathiewz.slick.tiled.Layer Maven / Gradle / Ivy

Go to download

The main purpose of this libraryis to modernize and maintain the slick2D library.

The newest version!
package com.github.mathiewz.slick.tiled;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.Properties;
import java.util.zip.GZIPInputStream;

import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.github.mathiewz.slick.SlickException;
import com.github.mathiewz.slick.util.Log;

/**
 * A layer of tiles on the map
 *
 * @author kevin
 */
public class Layer {
    /** The code used to decode Base64 encoding */
    private static byte[] baseCodes = new byte[256];

    /**
     * Static initialiser for the codes created against Base64
     */
    static {
        for (int i = 0; i < 256; i++) {
            baseCodes[i] = -1;
        }
        for (int i = 'A'; i <= 'Z'; i++) {
            baseCodes[i] = (byte) (i - 'A');
        }
        for (int i = 'a'; i <= 'z'; i++) {
            baseCodes[i] = (byte) (26 + i - 'a');
        }
        for (int i = '0'; i <= '9'; i++) {
            baseCodes[i] = (byte) (52 + i - '0');
        }
        baseCodes['+'] = 62;
        baseCodes['/'] = 63;
    }

    /** The map this layer belongs to */
    private final TiledMap map;
    /** The index of this layer */
    public int index;
    /** The name of this layer - read from the XML */
    public String name;
    /**
     * The tile data representing this data, index 0 = tileset, index 1 = tile
     * id
     */
    public int[][][] data;
    /** The width of this layer */
    public int width;
    /** The height of this layer */
    public int height;

    /** the properties of this layer */
    public Properties props;

    /**
     * Create a new layer based on the XML definition
     *
     * @param element
     *            The XML element describing the layer
     * @param map
     *            The map this layer is part of
     */
    public Layer(TiledMap map, Element element) {
        this.map = map;
        name = element.getAttribute("name");
        width = Integer.parseInt(element.getAttribute("width"));
        height = Integer.parseInt(element.getAttribute("height"));
        data = new int[width][height][3];

        // now read the layer properties
        Element propsElement = (Element) element.getElementsByTagName("properties").item(0);
        if (propsElement != null) {
            NodeList properties = propsElement.getElementsByTagName("property");
            if (properties != null) {
                props = new Properties();
                for (int p = 0; p < properties.getLength(); p++) {
                    Element propElement = (Element) properties.item(p);

                    String name = propElement.getAttribute("name");
                    String value = propElement.getAttribute("value");
                    props.setProperty(name, value);
                }
            }
        }

        Element dataNode = (Element) element.getElementsByTagName("data").item(0);
        String encoding = dataNode.getAttribute("encoding");
        String compression = dataNode.getAttribute("compression");

        if (encoding.equals("base64") && compression.equals("gzip")) {
            try {
                Node cdata = dataNode.getFirstChild();
                char[] enc = cdata.getNodeValue().trim().toCharArray();
                byte[] dec = decodeBase64(enc);
                GZIPInputStream is = new GZIPInputStream(new ByteArrayInputStream(dec));

                for (int y = 0; y < height; y++) {
                    for (int x = 0; x < width; x++) {
                        int tileId = 0;
                        tileId |= is.read();
                        tileId |= is.read() << 8;
                        tileId |= is.read() << 16;
                        tileId |= is.read() << 24;

                        if (tileId == 0) {
                            data[x][y][0] = -1;
                            data[x][y][1] = 0;
                            data[x][y][2] = 0;
                        } else {
                            TileSet set = map.findTileSet(tileId);

                            if (set != null) {
                                data[x][y][0] = set.index;
                                data[x][y][1] = tileId - set.firstGID;
                            }
                            data[x][y][2] = tileId;
                        }
                    }
                }
            } catch (IOException e) {
                Log.error(e);
                throw new SlickException("Unable to decode base 64 block");
            }
        } else {
            throw new SlickException("Unsupport tiled map type: " + encoding + "," + compression + " (only gzip base64 supported)");
        }
    }

    /**
     * Get the gloal ID of the tile at the specified location in this layer
     *
     * @param x
     *            The x coorindate of the tile
     * @param y
     *            The y coorindate of the tile
     * @return The global ID of the tile
     */
    public int getTileID(int x, int y) {
        return data[x][y][2];
    }

    /**
     * Set the global tile ID at a specified location
     *
     * @param x
     *            The x location to set
     * @param y
     *            The y location to set
     * @param tile
     *            The tile value to set
     */
    public void setTileID(int x, int y, int tile) {
        if (tile == 0) {
            data[x][y][0] = -1;
            data[x][y][1] = 0;
            data[x][y][2] = 0;
        } else {
            TileSet set = map.findTileSet(tile);

            data[x][y][0] = set.index;
            data[x][y][1] = tile - set.firstGID;
            data[x][y][2] = tile;
        }
    }

    /**
     * Render a section of this layer
     *
     * @param x
     *            The x location to render at
     * @param y
     *            The y location to render at
     * @param sx
     *            The x tile location to start rendering
     * @param sy
     *            The y tile location to start rendering
     * @param width
     *            The number of tiles across to render
     * @param ty
     *            The line of tiles to render
     * @param lineByLine
     *            True if we should render line by line, i.e. giving us a chance
     *            to render something else between lines
     * @param mapTileWidth
     *            the tile width specified in the map file
     * @param mapTileHeight
     *            the tile height specified in the map file
     */
    public void render(int x, int y, int sx, int sy, int width, int ty, boolean lineByLine, int mapTileWidth, int mapTileHeight) {
        for (int tileset = 0; tileset < map.getTileSetCount(); tileset++) {
            TileSet set = null;

            for (int tx = 0; tx < width; tx++) {
                if (sx + tx < 0 || sy + ty < 0) {
                    continue;
                }
                if (sx + tx >= this.width || sy + ty >= height) {
                    continue;
                }

                if (data[sx + tx][sy + ty][0] == tileset) {
                    if (set == null) {
                        set = map.getTileSet(tileset);
                        set.tiles.startUse();
                    }

                    int sheetX = set.getTileX(data[sx + tx][sy + ty][1]);
                    int sheetY = set.getTileY(data[sx + tx][sy + ty][1]);

                    int tileOffsetY = set.tileHeight - mapTileHeight;

                    // set.tiles.renderInUse(x+(tx*set.tileWidth),
                    // y+(ty*set.tileHeight), sheetX, sheetY);
                    set.tiles.renderInUse(x + tx * mapTileWidth, y + ty * mapTileHeight - tileOffsetY, sheetX, sheetY);
                }
            }

            if (lineByLine) {
                if (set != null) {
                    set.tiles.endUse();
                    set = null;
                }
                map.renderedLine(ty, ty + sy, index);
            }

            if (set != null) {
                set.tiles.endUse();
            }
        }
    }

    /**
     * Decode a Base64 string as encoded by TilED
     *
     * @param data
     *            The string of character to decode
     * @return The byte array represented by character encoding
     */
    private byte[] decodeBase64(char[] data) {
        int temp = data.length;
        for (char element : data) {
            if (element > 255 || baseCodes[element] < 0) {
                --temp;
            }
        }

        int len = temp / 4 * 3;
        if (temp % 4 == 3) {
            len += 2;
        }
        if (temp % 4 == 2) {
            len += 1;
        }

        byte[] out = new byte[len];

        int shift = 0;
        int accum = 0;
        int index = 0;

        for (char element : data) {
            int value = element > 255 ? -1 : baseCodes[element];

            if (value >= 0) {
                accum <<= 6;
                shift += 6;
                accum |= value;
                if (shift >= 8) {
                    shift -= 8;
                    out[index++] = (byte) (accum >> shift & 0xff);
                }
            }
        }

        if (index != out.length) {
            throw new SlickException("Data length appears to be wrong (wrote " + index + " should be " + out.length + ")");
        }

        return out;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy