mirror of
https://github.com/OMGeeky/ATCS.git
synced 2026-02-23 15:38:23 +01:00
Initial commit
This commit is contained in:
111
hacked-libtiled/build.xml
Normal file
111
hacked-libtiled/build.xml
Normal file
@@ -0,0 +1,111 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
|
||||
<!--
|
||||
|
||||
This is the Ant build script for rsyntaxtextarea.jar.
|
||||
Available targets include:
|
||||
|
||||
1. compile: Compiles all org.fife classes into ${class-dir}.
|
||||
2. make-jar: Creates the jar file.
|
||||
3. make-source-zip: Creates a source zip file.
|
||||
3. make-javadoc: Creates the javadoc for RSyntaxTextArea.
|
||||
|
||||
Author: Robert Futrell
|
||||
Version: 1.5
|
||||
Date: 11aug2013
|
||||
|
||||
-->
|
||||
|
||||
|
||||
<project name="RSyntaxTextArea" default="make-jar" basedir=".">
|
||||
|
||||
<description>RSyntaxTextArea build file</description>
|
||||
|
||||
|
||||
<!-- Set global properties for this build. -->
|
||||
<property name="version" value="2.5.3"/>
|
||||
<property name="source-dir" location="src"/>
|
||||
<property name="class-dir" location="ant-classes"/>
|
||||
<property name="dist-dir" location="dist"/>
|
||||
<property name="doc-dir" location="javadoc"/>
|
||||
<property name="debug" value="true"/>
|
||||
<property name="debuglevel" value="lines,vars,source"/>
|
||||
<property name="java-level" value="1.5"/>
|
||||
|
||||
|
||||
<!-- Compiles the classes. -->
|
||||
<target name="compile" description="Compile the source">
|
||||
<echo>Compiling with java: ${java.runtime.version}</echo>
|
||||
<delete includeEmptyDirs="true" quiet="true" dir="${class-dir}"/>
|
||||
<mkdir dir="${class-dir}"/>
|
||||
<javac srcdir="${source-dir}" destdir="${class-dir}"
|
||||
deprecation="yes" debug="${debug}" debuglevel="${debuglevel}"
|
||||
source="${java-level}" target="${java-level}" includeantruntime="false"/>
|
||||
</target>
|
||||
|
||||
|
||||
<!-- Creates the jar file. -->
|
||||
<target name="make-jar" depends="compile"
|
||||
description="Create RSyntaxTextArea jar">
|
||||
<delete includeEmptyDirs="true" quiet="true" dir="${dist-dir}"/>
|
||||
<mkdir dir="${dist-dir}"/>
|
||||
<jar destfile="${dist-dir}/rsyntaxtextarea.jar">
|
||||
<fileset dir="${class-dir}"/>
|
||||
<fileset dir="i18n"/>
|
||||
<fileset dir="${source-dir}">
|
||||
<include name="theme.dtd"/>
|
||||
</fileset>
|
||||
<manifest>
|
||||
<attribute name="Specification-Title" value="RSyntaxTextArea"/>
|
||||
<attribute name="Specification-Version" value="${version}"/>
|
||||
<attribute name="Implementation-Title" value="org.fife.ui"/>
|
||||
<attribute name="Implementation-Version" value="${version}"/>
|
||||
<section name="RTextArea">
|
||||
<attribute name="Specification-Title" value="RTextArea"/>
|
||||
<attribute name="Specification-Version" value="${version}"/>
|
||||
<attribute name="Implementation-Title" value="org.fife.ui.rtextarea"/>
|
||||
<attribute name="Implementation-Version" value="${version}"/>
|
||||
</section>
|
||||
<section name="RSyntaxTextArea">
|
||||
<attribute name="Specification-Title" value="RSyntaxTextArea-Core"/>
|
||||
<attribute name="Specification-Version" value="${version}"/>
|
||||
<attribute name="Implementation-Title" value="org.fife.ui.rsyntaxtextarea"/>
|
||||
<attribute name="Implementation-Version" value="${version}"/>
|
||||
</section>
|
||||
</manifest>
|
||||
</jar>
|
||||
<copy todir="${dist-dir}">
|
||||
<fileset dir="distfiles"/>
|
||||
</copy>
|
||||
</target>
|
||||
|
||||
|
||||
<!-- Builds the source zip file. -->
|
||||
<target name="make-source-zip" description="Builds the source zip file">
|
||||
<zip destfile="./rsyntaxtextarea_${version}_Source.zip">
|
||||
<fileset dir=".">
|
||||
<include name="distfiles/**"/>
|
||||
<include name="src/**"/>
|
||||
<include name="i18n/**"/>
|
||||
<include name="test/**"/>
|
||||
<include name="themes/**"/>
|
||||
<include name="build.xml"/>
|
||||
<include name=".project"/>
|
||||
<include name=".classpath"/>
|
||||
</fileset>
|
||||
</zip>
|
||||
</target>
|
||||
|
||||
|
||||
<!-- Builds the javadoc. -->
|
||||
<target name="make-javadoc" depends="compile">
|
||||
<javadoc destdir="${doc-dir}" author="true" version="true"
|
||||
breakiterator="yes">
|
||||
<packageset dir="${source-dir}" defaultexcludes="yes">
|
||||
<include name="org/**"/>
|
||||
</packageset>
|
||||
</javadoc>
|
||||
</target>
|
||||
|
||||
|
||||
</project>
|
||||
73
hacked-libtiled/tiled/core/AnimatedTile.java
Normal file
73
hacked-libtiled/tiled/core/AnimatedTile.java
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright 2004-2006, Thorbjørn Lindeijer <thorbjorn@lindeijer.nl>
|
||||
* Copyright 2004-2006, Adam Turk <aturk@biggeruniverse.com>
|
||||
*
|
||||
* This file is part of libtiled-java.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package tiled.core;
|
||||
|
||||
/**
|
||||
* Animated tiles take advantage of the Sprite class internally to handle
|
||||
* animation using an array of tiles.
|
||||
*
|
||||
* @see tiled.core.Sprite
|
||||
*/
|
||||
public class AnimatedTile extends Tile
|
||||
{
|
||||
private Sprite sprite;
|
||||
|
||||
public AnimatedTile() {
|
||||
}
|
||||
|
||||
public AnimatedTile(TileSet set) {
|
||||
super(set);
|
||||
}
|
||||
|
||||
public AnimatedTile(Tile[] frames) {
|
||||
this();
|
||||
sprite = new Sprite(frames);
|
||||
}
|
||||
|
||||
public AnimatedTile(Sprite s) {
|
||||
this();
|
||||
setSprite(s);
|
||||
}
|
||||
|
||||
public void setSprite(Sprite s) {
|
||||
sprite = s;
|
||||
}
|
||||
|
||||
public int countAnimationFrames() {
|
||||
return sprite.getTotalFrames();
|
||||
}
|
||||
|
||||
public int countKeys() {
|
||||
return sprite.getTotalKeys();
|
||||
}
|
||||
|
||||
public Sprite getSprite() {
|
||||
return sprite;
|
||||
}
|
||||
}
|
||||
469
hacked-libtiled/tiled/core/Map.java
Normal file
469
hacked-libtiled/tiled/core/Map.java
Normal file
@@ -0,0 +1,469 @@
|
||||
/*
|
||||
* Copyright 2004-2010, Thorbjørn Lindeijer <thorbjorn@lindeijer.nl>
|
||||
* Copyright 2004-2006, Adam Turk <aturk@biggeruniverse.com>
|
||||
*
|
||||
* This file is part of libtiled-java.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package tiled.core;
|
||||
|
||||
import java.awt.Rectangle;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* The Map class is the focal point of the <code>tiled.core</code> package.
|
||||
*/
|
||||
public class Map implements Iterable<MapLayer>
|
||||
{
|
||||
public static final int ORIENTATION_ORTHOGONAL = 1;
|
||||
public static final int ORIENTATION_ISOMETRIC = 2;
|
||||
public static final int ORIENTATION_HEXAGONAL = 4;
|
||||
/** Shifted (used for iso and hex). */
|
||||
public static final int ORIENTATION_SHIFTED = 5;
|
||||
|
||||
private Vector<MapLayer> layers;
|
||||
private Vector<ObjectGroup> objectGroups;
|
||||
private Vector<TileLayer> tileLayers;
|
||||
private Vector<TileSet> tileSets;
|
||||
|
||||
private int tileWidth, tileHeight;
|
||||
private int orientation = ORIENTATION_ORTHOGONAL;
|
||||
private Properties properties;
|
||||
private String filename;
|
||||
protected Rectangle bounds; // in tiles
|
||||
|
||||
/**
|
||||
* @param width the map width in tiles.
|
||||
* @param height the map height in tiles.
|
||||
*/
|
||||
public Map(int width, int height) {
|
||||
layers = new Vector<MapLayer>();
|
||||
bounds = new Rectangle(width, height);
|
||||
properties = new Properties();
|
||||
tileSets = new Vector<TileSet>();
|
||||
objectGroups = new Vector<ObjectGroup>();
|
||||
tileLayers = new Vector<TileLayer>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total number of layers.
|
||||
*
|
||||
* @return the size of the layer vector
|
||||
*/
|
||||
public int getLayerCount() {
|
||||
return layers.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the bounds of this plane to include all layers completely.
|
||||
*/
|
||||
public void fitBoundsToLayers() {
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
|
||||
Rectangle layerBounds = new Rectangle();
|
||||
|
||||
for (int i = 0; i < layers.size(); i++) {
|
||||
getLayer(i).getBounds(layerBounds);
|
||||
if (width < layerBounds.width) width = layerBounds.width;
|
||||
if (height < layerBounds.height) height = layerBounds.height;
|
||||
}
|
||||
|
||||
setBounds(width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a <code>Rectangle</code> representing the maximum bounds in
|
||||
* tiles.
|
||||
* @return a new rectangle containing the maximum bounds of this plane
|
||||
*/
|
||||
public Rectangle getBounds() {
|
||||
return new Rectangle(bounds);
|
||||
}
|
||||
|
||||
public void setBounds(int width, int height) {
|
||||
bounds.width = width;
|
||||
bounds.height = height;
|
||||
}
|
||||
|
||||
public MapLayer addLayer(MapLayer layer) {
|
||||
layer.setMap(this);
|
||||
layers.add(layer);
|
||||
if (layer instanceof TileLayer) {
|
||||
tileLayers.add((TileLayer) layer);
|
||||
} else if (layer instanceof ObjectGroup) {
|
||||
objectGroups.add((ObjectGroup) layer);
|
||||
}
|
||||
return layer;
|
||||
}
|
||||
|
||||
public void setLayer(int index, MapLayer layer) {
|
||||
layer.setMap(this);
|
||||
layers.set(index, layer);
|
||||
if (layer instanceof TileLayer) {
|
||||
tileLayers.set(index - objectGroups.size(), (TileLayer) layer);
|
||||
} else if (layer instanceof ObjectGroup) {
|
||||
objectGroups.set(index, (ObjectGroup) layer);
|
||||
}
|
||||
}
|
||||
|
||||
public void insertLayer(int index, MapLayer layer) {
|
||||
layer.setMap(this);
|
||||
layers.add(index, layer);
|
||||
if (layer instanceof TileLayer) {
|
||||
tileLayers.add(index - objectGroups.size(), (TileLayer) layer);
|
||||
} else if (layer instanceof ObjectGroup) {
|
||||
objectGroups.add(index, (ObjectGroup) layer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the layer at the specified index. Layers above this layer will
|
||||
* move down to fill the gap.
|
||||
*
|
||||
* @param index the index of the layer to be removed
|
||||
* @return the layer that was removed from the list
|
||||
*/
|
||||
public MapLayer removeLayer(int index) {
|
||||
if (index < objectGroups.size()) {
|
||||
objectGroups.remove(index);
|
||||
} else if (index - objectGroups.size() < tileLayers.size()){
|
||||
tileLayers.remove(index - objectGroups.size());
|
||||
}
|
||||
return layers.remove(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all layers from the plane.
|
||||
*/
|
||||
public void removeAllLayers() {
|
||||
layers.removeAllElements();
|
||||
objectGroups.removeAllElements();
|
||||
tileLayers.removeAllElements();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the layer vector.
|
||||
*
|
||||
* @return Vector the layer vector
|
||||
*/
|
||||
public Vector<MapLayer> getLayers() {
|
||||
return layers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tile layer vector.
|
||||
*
|
||||
* @return Vector the tile layer vector
|
||||
*/
|
||||
public Vector<TileLayer> getTileLayers() {
|
||||
return tileLayers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the object group vector.
|
||||
*
|
||||
* @return Vector the object group vector
|
||||
*/
|
||||
public Vector<ObjectGroup> getObjectGroup() {
|
||||
return objectGroups;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the layer vector to the given java.util.Vector.
|
||||
*
|
||||
* @param layers the new set of layers
|
||||
*/
|
||||
public void setLayers(Vector<MapLayer> layers) {
|
||||
this.layers = layers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the layer at the specified vector index.
|
||||
*
|
||||
* @param i the index of the layer to return
|
||||
* @return the layer at the specified index, or null if the index is out of
|
||||
* bounds
|
||||
*/
|
||||
public MapLayer getLayer(int i) {
|
||||
try {
|
||||
return layers.get(i);
|
||||
} catch (ArrayIndexOutOfBoundsException e) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resizes this plane. The (dx, dy) pair determines where the original
|
||||
* plane should be positioned on the new area. Only layers that exactly
|
||||
* match the bounds of the map are resized, any other layers are moved by
|
||||
* the given shift.
|
||||
*
|
||||
* @see tiled.core.MapLayer#resize
|
||||
*
|
||||
* @param width The new width of the map.
|
||||
* @param height The new height of the map.
|
||||
* @param dx The shift in x direction in tiles.
|
||||
* @param dy The shift in y direction in tiles.
|
||||
*/
|
||||
public void resize(int width, int height, int dx, int dy) {
|
||||
for (MapLayer layer : this) {
|
||||
if (layer.bounds.equals(bounds)) {
|
||||
layer.resize(width, height, dx, dy);
|
||||
} else {
|
||||
layer.setOffset(layer.bounds.x + dx, layer.bounds.y + dy);
|
||||
}
|
||||
}
|
||||
|
||||
bounds.width = width;
|
||||
bounds.height = height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether the point (x,y) falls within the plane.
|
||||
*
|
||||
* @param x
|
||||
* @param y
|
||||
* @return <code>true</code> if the point is within the plane,
|
||||
* <code>false</code> otherwise
|
||||
*/
|
||||
public boolean inBounds(int x, int y) {
|
||||
return x >= 0 && y >= 0 && x < bounds.width && y < bounds.height;
|
||||
}
|
||||
|
||||
public Iterator<MapLayer> iterator() {
|
||||
return layers.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a Tileset to this Map. If the set is already attached to this map,
|
||||
* <code>addTileset</code> simply returns.
|
||||
*
|
||||
* @param tileset a tileset to add
|
||||
*/
|
||||
public void addTileset(TileSet tileset) {
|
||||
if (tileset == null || tileSets.indexOf(tileset) > -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
Tile t = tileset.getTile(0);
|
||||
|
||||
if (t != null) {
|
||||
int tw = t.getWidth();
|
||||
int th = t.getHeight();
|
||||
if (tw != tileWidth) {
|
||||
if (tileWidth == 0) {
|
||||
tileWidth = tw;
|
||||
tileHeight = th;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tileSets.add(tileset);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a {@link TileSet} from the map, and removes any tiles in the set
|
||||
* from the map layers.
|
||||
*
|
||||
* @param tileset TileSet to remove
|
||||
*/
|
||||
public void removeTileset(TileSet tileset) {
|
||||
// Sanity check
|
||||
final int tilesetIndex = tileSets.indexOf(tileset);
|
||||
if (tilesetIndex == -1)
|
||||
return;
|
||||
|
||||
// Go through the map and remove any instances of the tiles in the set
|
||||
for (Tile tile : tileset) {
|
||||
for (MapLayer ml : this) {
|
||||
if (ml instanceof TileLayer) {
|
||||
((TileLayer) ml).removeTile(tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tileSets.remove(tileset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the map properties
|
||||
*/
|
||||
public Properties getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
public void setProperties(Properties prop) {
|
||||
properties = prop;
|
||||
}
|
||||
|
||||
public void setFilename(String filename) {
|
||||
this.filename = filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new tile width.
|
||||
*
|
||||
* @param width the new tile width
|
||||
*/
|
||||
public void setTileWidth(int width) {
|
||||
tileWidth = width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new tile height.
|
||||
*
|
||||
* @param height the new tile height
|
||||
*/
|
||||
public void setTileHeight(int height) {
|
||||
tileHeight = height;
|
||||
}
|
||||
|
||||
public void setOrientation(int orientation) {
|
||||
this.orientation = orientation;
|
||||
}
|
||||
|
||||
public String getFilename() {
|
||||
return filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a vector with the currently loaded tileSets.
|
||||
*
|
||||
* @return Vector
|
||||
*/
|
||||
public Vector<TileSet> getTileSets() {
|
||||
return tileSets;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns width of map in tiles.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public int getWidth() {
|
||||
return bounds.width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns height of map in tiles.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public int getHeight() {
|
||||
return bounds.height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns default tile width for this map.
|
||||
*
|
||||
* @return the default tile width
|
||||
*/
|
||||
public int getTileWidth() {
|
||||
return tileWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns default tile height for this map.
|
||||
*
|
||||
* @return the default tile height
|
||||
*/
|
||||
public int getTileHeight() {
|
||||
return tileHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns wether the given tile coordinates fall within the map
|
||||
* boundaries.
|
||||
*
|
||||
* @param x The tile-space x-coordinate
|
||||
* @param y The tile-space y-coordinate
|
||||
* @return <code>true</code> if the point is within the map boundaries,
|
||||
* <code>false</code> otherwise
|
||||
*/
|
||||
public boolean contains(int x, int y) {
|
||||
return x >= 0 && y >= 0 && x < bounds.width && y < bounds.height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum tile height. This is the height of the highest tile
|
||||
* in all tileSets or the tile height used by this map if it's smaller.
|
||||
*
|
||||
* @return int The maximum tile height
|
||||
*/
|
||||
public int getTileHeightMax() {
|
||||
int maxHeight = tileHeight;
|
||||
|
||||
for (TileSet tileset : tileSets) {
|
||||
int height = tileset.getTileHeight();
|
||||
if (height > maxHeight) {
|
||||
maxHeight = height;
|
||||
}
|
||||
}
|
||||
|
||||
return maxHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Swaps the tile sets at the given indices.
|
||||
*/
|
||||
public void swapTileSets(int index0, int index1) {
|
||||
if (index0 == index1) return;
|
||||
TileSet set = tileSets.get(index0);
|
||||
tileSets.set(index0, tileSets.get(index1));
|
||||
tileSets.set(index1, set);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the orientation of this map. Orientation will be one of
|
||||
* {@link Map#ORIENTATION_ISOMETRIC}, {@link Map#ORIENTATION_ORTHOGONAL},
|
||||
* {@link Map#ORIENTATION_HEXAGONAL} and {@link Map#ORIENTATION_SHIFTED}.
|
||||
*
|
||||
* @return The orientation from the enumerated set
|
||||
*/
|
||||
public int getOrientation() {
|
||||
return orientation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns string describing the map. The form is <code>Map[width x height
|
||||
* x layers][tileWidth x tileHeight]</code>, for example <code>
|
||||
* Map[64x64x2][24x24]</code>.
|
||||
*
|
||||
* @return string describing map
|
||||
*/
|
||||
public String toString() {
|
||||
return "Map[" + bounds.width + "x" + bounds.height + "x" +
|
||||
getLayerCount() + "][" + tileWidth + "x" +
|
||||
tileHeight + "]";
|
||||
}
|
||||
|
||||
public boolean containsLayer(MapLayer layer) {
|
||||
return layers.contains(layer);
|
||||
}
|
||||
|
||||
public int getLayerIndex(MapLayer layer) {
|
||||
return layers.indexOf(layer);
|
||||
}
|
||||
}
|
||||
310
hacked-libtiled/tiled/core/MapLayer.java
Normal file
310
hacked-libtiled/tiled/core/MapLayer.java
Normal file
@@ -0,0 +1,310 @@
|
||||
/*
|
||||
* Copyright 2004-2010, Thorbjørn Lindeijer <thorbjorn@lindeijer.nl>
|
||||
* Copyright 2004-2006, Adam Turk <aturk@biggeruniverse.com>
|
||||
*
|
||||
* This file is part of libtiled-java.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package tiled.core;
|
||||
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.geom.Area;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* A layer of a map.
|
||||
*
|
||||
* @see Map
|
||||
*/
|
||||
public abstract class MapLayer implements Cloneable
|
||||
{
|
||||
/** MIRROR_HORIZONTAL */
|
||||
public static final int MIRROR_HORIZONTAL = 1;
|
||||
/** MIRROR_VERTICAL */
|
||||
public static final int MIRROR_VERTICAL = 2;
|
||||
|
||||
/** ROTATE_90 */
|
||||
public static final int ROTATE_90 = 90;
|
||||
/** ROTATE_180 */
|
||||
public static final int ROTATE_180 = 180;
|
||||
/** ROTATE_270 */
|
||||
public static final int ROTATE_270 = 270;
|
||||
|
||||
protected String name;
|
||||
protected boolean isVisible = true;
|
||||
protected Map myMap;
|
||||
protected float opacity = 1.0f;
|
||||
protected Rectangle bounds;
|
||||
private Properties properties = new Properties();
|
||||
|
||||
public MapLayer() {
|
||||
bounds = new Rectangle();
|
||||
setMap(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param w width in tiles
|
||||
* @param h height in tiles
|
||||
*/
|
||||
public MapLayer(int w, int h) {
|
||||
this(new Rectangle(0, 0, w, h));
|
||||
}
|
||||
|
||||
public MapLayer(Rectangle r) {
|
||||
this();
|
||||
setBounds(r);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param map the map this layer is part of
|
||||
*/
|
||||
MapLayer(Map map) {
|
||||
this();
|
||||
setMap(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param map the map this layer is part of
|
||||
* @param w width in tiles
|
||||
* @param h height in tiles
|
||||
*/
|
||||
public MapLayer(Map map, int w, int h) {
|
||||
this(w, h);
|
||||
setMap(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a linear translation of this layer by (<i>dx, dy</i>).
|
||||
*
|
||||
* @param dx distance over x axis
|
||||
* @param dy distance over y axis
|
||||
*/
|
||||
public void translate(int dx, int dy) {
|
||||
bounds.x += dx;
|
||||
bounds.y += dy;
|
||||
}
|
||||
|
||||
public abstract void rotate(int angle);
|
||||
|
||||
public abstract void mirror(int dir);
|
||||
|
||||
/**
|
||||
* Sets the bounds (in tiles) to the specified Rectangle.
|
||||
*
|
||||
* @param bounds
|
||||
*/
|
||||
protected void setBounds(Rectangle bounds) {
|
||||
this.bounds = new Rectangle(bounds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of this layer.
|
||||
*
|
||||
* @param name the new name
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the map this layer is part of.
|
||||
*
|
||||
* @param map the Map object
|
||||
*/
|
||||
public void setMap(Map map) {
|
||||
myMap = map;
|
||||
}
|
||||
|
||||
public Map getMap() {
|
||||
return myMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets layer opacity. If it is different from the previous value and the
|
||||
* layer is visible, a MapChangedEvent is fired.
|
||||
*
|
||||
* @param opacity the new opacity for this layer
|
||||
*/
|
||||
public void setOpacity(float opacity) {
|
||||
this.opacity = opacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the visibility of this map layer. If it changes from its current
|
||||
* value, a MapChangedEvent is fired.
|
||||
*
|
||||
* @param visible <code>true</code> to make the layer visible;
|
||||
* <code>false</code> to make it invisible
|
||||
*/
|
||||
public void setVisible(boolean visible) {
|
||||
isVisible = visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the offset of this map layer. The offset is a distance by which to
|
||||
* shift this layer from the origin of the map.
|
||||
*
|
||||
* @param xOff x offset in tiles
|
||||
* @param yOff y offset in tiles
|
||||
*/
|
||||
public void setOffset(int xOff, int yOff) {
|
||||
bounds.x = xOff;
|
||||
bounds.y = yOff;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of this layer.
|
||||
* @return the name of the layer
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns layer width in tiles.
|
||||
* @return layer width in tiles.
|
||||
*/
|
||||
public int getWidth() {
|
||||
return bounds.width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns layer height in tiles.
|
||||
* @return layer height in tiles.
|
||||
*/
|
||||
public int getHeight() {
|
||||
return bounds.height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the layer bounds in tiles.
|
||||
* @return the layer bounds in tiles
|
||||
*/
|
||||
public Rectangle getBounds() {
|
||||
return new Rectangle(bounds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Assigns the layer bounds in tiles to the given rectangle.
|
||||
* @param rect the rectangle to which the layer bounds are assigned
|
||||
*/
|
||||
public void getBounds(Rectangle rect) {
|
||||
rect.setBounds(bounds);
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method to check if a point in tile-space is within
|
||||
* the layer boundaries.
|
||||
*
|
||||
* @param x the x-coordinate of the point
|
||||
* @param y the y-coordinate of the point
|
||||
* @return <code>true</code> if the point (x,y) is within the layer
|
||||
* boundaries, <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean contains(int x, int y) {
|
||||
return bounds.contains(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns layer opacity.
|
||||
*
|
||||
* @return layer opacity, ranging from 0.0 to 1.0
|
||||
*/
|
||||
public float getOpacity() {
|
||||
return opacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether this layer is visible.
|
||||
*
|
||||
* @return <code>true</code> if the layer is visible, <code>false</code>
|
||||
* otherwise.
|
||||
*/
|
||||
public boolean isVisible() {
|
||||
return isVisible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges the tile data of this layer with the specified layer. The calling
|
||||
* layer is considered the significant layer, and will overwrite the data
|
||||
* of the argument layer. At cells where the calling layer has no data, the
|
||||
* argument layer data is preserved.
|
||||
*
|
||||
* @param other the insignificant layer to merge with
|
||||
*/
|
||||
public abstract void mergeOnto(MapLayer other);
|
||||
|
||||
public abstract void maskedMergeOnto(MapLayer other, Area mask);
|
||||
|
||||
public abstract void copyFrom(MapLayer other);
|
||||
|
||||
public abstract void maskedCopyFrom(MapLayer other, Area mask);
|
||||
|
||||
public abstract MapLayer createDiff(MapLayer ml);
|
||||
|
||||
/**
|
||||
* Unlike mergeOnto, copyTo includes the null tile when merging
|
||||
*
|
||||
* @see MapLayer#copyFrom
|
||||
* @see MapLayer#mergeOnto
|
||||
* @param other the layer to copy this layer to
|
||||
*/
|
||||
public abstract void copyTo(MapLayer other);
|
||||
|
||||
public abstract boolean isEmpty();
|
||||
|
||||
/**
|
||||
* Creates a copy of this layer.
|
||||
*
|
||||
* @see Object#clone
|
||||
* @return a clone of this layer, as complete as possible
|
||||
* @exception CloneNotSupportedException
|
||||
*/
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
MapLayer clone = (MapLayer) super.clone();
|
||||
|
||||
// Create a new bounds object
|
||||
clone.bounds = new Rectangle(bounds);
|
||||
clone.properties = (Properties) properties.clone();
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param width the new width of the layer
|
||||
* @param height the new height of the layer
|
||||
* @param dx the shift in x direction
|
||||
* @param dy the shift in y direction
|
||||
*/
|
||||
public abstract void resize(int width, int height, int dx, int dy);
|
||||
|
||||
public void setProperties(Properties p) {
|
||||
properties.clear();
|
||||
properties.putAll(p);
|
||||
}
|
||||
|
||||
public Properties getProperties() {
|
||||
return properties;
|
||||
}
|
||||
}
|
||||
198
hacked-libtiled/tiled/core/MapObject.java
Normal file
198
hacked-libtiled/tiled/core/MapObject.java
Normal file
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright 2004-2008, Thorbjørn Lindeijer <thorbjorn@lindeijer.nl>
|
||||
* Copyright 2004-2008, Adam Turk <aturk@biggeruniverse.com>
|
||||
*
|
||||
* This file is part of libtiled-java.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package tiled.core;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.Properties;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
/**
|
||||
* An object occupying an {@link ObjectGroup}.
|
||||
*/
|
||||
public class MapObject implements Cloneable
|
||||
{
|
||||
private Properties properties = new Properties();
|
||||
private ObjectGroup objectGroup;
|
||||
private Rectangle bounds = new Rectangle();
|
||||
private String name = "Object";
|
||||
private String type = "";
|
||||
private String imageSource = "";
|
||||
private Image image;
|
||||
private Image scaledImage;
|
||||
|
||||
public MapObject(int x, int y, int width, int height) {
|
||||
bounds = new Rectangle(x, y, width, height);
|
||||
}
|
||||
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
MapObject clone = (MapObject) super.clone();
|
||||
clone.bounds = new Rectangle(bounds);
|
||||
clone.properties = (Properties) properties.clone();
|
||||
return clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the object group this object is part of
|
||||
*/
|
||||
public ObjectGroup getObjectGroup() {
|
||||
return objectGroup;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the object group this object is part of. Should only be called by
|
||||
* the object group.
|
||||
*
|
||||
* @param objectGroup the object group this object is part of
|
||||
*/
|
||||
public void setObjectGroup(ObjectGroup objectGroup) {
|
||||
this.objectGroup = objectGroup;
|
||||
}
|
||||
|
||||
public Rectangle getBounds() {
|
||||
return bounds;
|
||||
}
|
||||
|
||||
public void setBounds(Rectangle bounds) {
|
||||
this.bounds = bounds;
|
||||
}
|
||||
|
||||
public String getImageSource() {
|
||||
return imageSource;
|
||||
}
|
||||
|
||||
public void setImageSource(String source) {
|
||||
if (imageSource.equals(source))
|
||||
return;
|
||||
|
||||
imageSource = source;
|
||||
|
||||
// Attempt to read the image
|
||||
if (imageSource.length() > 0) {
|
||||
try {
|
||||
image = ImageIO.read(new File(imageSource));
|
||||
} catch (IOException e) {
|
||||
image = null;
|
||||
}
|
||||
} else {
|
||||
image = null;
|
||||
}
|
||||
|
||||
scaledImage = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the image to be used when drawing this object. This image is
|
||||
* scaled to the size of the object.
|
||||
*
|
||||
* @param zoom the requested zoom level of the image
|
||||
* @return the image to be used when drawing this object
|
||||
*/
|
||||
public Image getImage(double zoom) {
|
||||
if (image == null)
|
||||
return null;
|
||||
|
||||
final int zoomedWidth = (int) (getWidth() * zoom);
|
||||
final int zoomedHeight = (int) (getHeight() * zoom);
|
||||
|
||||
if (scaledImage == null || scaledImage.getWidth(null) != zoomedWidth
|
||||
|| scaledImage.getHeight(null) != zoomedHeight)
|
||||
{
|
||||
scaledImage = image.getScaledInstance(zoomedWidth, zoomedHeight,
|
||||
Image.SCALE_SMOOTH);
|
||||
}
|
||||
|
||||
return scaledImage;
|
||||
}
|
||||
|
||||
public int getX() {
|
||||
return bounds.x;
|
||||
}
|
||||
|
||||
public void setX(int x) {
|
||||
bounds.x = x;
|
||||
}
|
||||
|
||||
public int getY() {
|
||||
return bounds.y;
|
||||
}
|
||||
|
||||
public void setY(int y) {
|
||||
bounds.y = y;
|
||||
}
|
||||
|
||||
public void translate(int dx, int dy) {
|
||||
bounds.translate(dx, dy);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public void setType(String type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
return bounds.width;
|
||||
}
|
||||
|
||||
public void setWidth(int width) {
|
||||
bounds.width = width;
|
||||
}
|
||||
|
||||
public void setHeight(int height) {
|
||||
bounds.height = height;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
return bounds.height;
|
||||
}
|
||||
|
||||
public Properties getProperties() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
public void setProperties(Properties p) {
|
||||
properties = p;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return type + " (" + getX() + "," + getY() + ")";
|
||||
}
|
||||
}
|
||||
213
hacked-libtiled/tiled/core/ObjectGroup.java
Normal file
213
hacked-libtiled/tiled/core/ObjectGroup.java
Normal file
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* Copyright 2004-2006, Thorbjørn Lindeijer <thorbjorn@lindeijer.nl>
|
||||
* Copyright 2004-2006, Adam Turk <aturk@biggeruniverse.com>
|
||||
*
|
||||
* This file is part of libtiled-java.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package tiled.core;
|
||||
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Shape;
|
||||
import java.awt.geom.Area;
|
||||
import java.awt.geom.Ellipse2D;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A layer containing {@link MapObject map objects}.
|
||||
*/
|
||||
public class ObjectGroup extends MapLayer
|
||||
{
|
||||
private LinkedList<MapObject> objects = new LinkedList<MapObject>();
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public ObjectGroup() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param map the map this object group is part of
|
||||
*/
|
||||
public ObjectGroup(Map map) {
|
||||
super(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an object group that is part of the given map and has the given
|
||||
* origin.
|
||||
*
|
||||
* @param map the map this object group is part of
|
||||
* @param origX the x origin of this layer
|
||||
* @param origY the y origin of this layer
|
||||
*/
|
||||
public ObjectGroup(Map map, int origX, int origY) {
|
||||
super(map);
|
||||
setBounds(new Rectangle(origX, origY, 0, 0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an object group with a given area. The size of area is
|
||||
* irrelevant, just its origin.
|
||||
*
|
||||
* @param area the area of the object group
|
||||
*/
|
||||
public ObjectGroup(Rectangle area) {
|
||||
super(area);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see MapLayer#rotate(int)
|
||||
*/
|
||||
public void rotate(int angle) {
|
||||
// TODO: Implement rotating an object group
|
||||
}
|
||||
|
||||
/**
|
||||
* @see MapLayer#mirror(int)
|
||||
*/
|
||||
public void mirror(int dir) {
|
||||
// TODO: Implement mirroring an object group
|
||||
}
|
||||
|
||||
public void mergeOnto(MapLayer other) {
|
||||
// TODO: Implement merging with another object group
|
||||
}
|
||||
|
||||
public void maskedMergeOnto(MapLayer other, Area mask) {
|
||||
// TODO: Figure out what object group should do with this method
|
||||
}
|
||||
|
||||
public void copyFrom(MapLayer other) {
|
||||
// TODO: Implement copying from another object group (same as merging)
|
||||
}
|
||||
|
||||
public void maskedCopyFrom(MapLayer other, Area mask) {
|
||||
// TODO: Figure out what object group should do with this method
|
||||
}
|
||||
|
||||
public void copyTo(MapLayer other) {
|
||||
// TODO: Implement copying to another object group (same as merging)
|
||||
}
|
||||
|
||||
/**
|
||||
* @see MapLayer#resize(int,int,int,int)
|
||||
*/
|
||||
public void resize(int width, int height, int dx, int dy) {
|
||||
// TODO: Translate contained objects by the change of origin
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return objects.isEmpty();
|
||||
}
|
||||
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
ObjectGroup clone = (ObjectGroup) super.clone();
|
||||
clone.objects = new LinkedList<MapObject>();
|
||||
for (MapObject object : objects) {
|
||||
final MapObject objectClone = (MapObject) object.clone();
|
||||
clone.objects.add(objectClone);
|
||||
objectClone.setObjectGroup(clone);
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public MapLayer createDiff(MapLayer ml) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void addObject(MapObject o) {
|
||||
objects.add(o);
|
||||
o.setObjectGroup(this);
|
||||
}
|
||||
|
||||
public void removeObject(MapObject o) {
|
||||
objects.remove(o);
|
||||
o.setObjectGroup(null);
|
||||
}
|
||||
|
||||
public Iterator<MapObject> getObjects() {
|
||||
return objects.iterator();
|
||||
}
|
||||
|
||||
|
||||
public List<MapObject> getObjectsList() {
|
||||
return objects;
|
||||
}
|
||||
|
||||
public MapObject getObjectAt(int x, int y) {
|
||||
for (MapObject obj : objects) {
|
||||
// Attempt to get an object bordering the point that has no width
|
||||
if (obj.getWidth() == 0 && obj.getX() + bounds.x == x) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
// Attempt to get an object bordering the point that has no height
|
||||
if (obj.getHeight() == 0 && obj.getY() + bounds.y == y) {
|
||||
return obj;
|
||||
}
|
||||
|
||||
Rectangle rect = new Rectangle(obj.getX() + bounds.x * myMap.getTileWidth(),
|
||||
obj.getY() + bounds.y * myMap.getTileHeight(),
|
||||
obj.getWidth(), obj.getHeight());
|
||||
if (rect.contains(x, y)) {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// This method will work at any zoom level, provided you provide the correct zoom factor. It also adds a one pixel buffer (that doesn't change with zoom).
|
||||
public MapObject getObjectNear(int x, int y, double zoom) {
|
||||
Rectangle2D mouse = new Rectangle2D.Double(x - zoom - 1, y - zoom - 1, 2 * zoom + 1, 2 * zoom + 1);
|
||||
Shape shape;
|
||||
|
||||
for (MapObject obj : objects) {
|
||||
if (obj.getWidth() == 0 && obj.getHeight() == 0) {
|
||||
shape = new Ellipse2D.Double(obj.getX() * zoom, obj.getY() * zoom, 10 * zoom, 10 * zoom);
|
||||
} else {
|
||||
shape = new Rectangle2D.Double(obj.getX() + bounds.x * myMap.getTileWidth(),
|
||||
obj.getY() + bounds.y * myMap.getTileHeight(),
|
||||
obj.getWidth() > 0 ? obj.getWidth() : zoom,
|
||||
obj.getHeight() > 0 ? obj.getHeight() : zoom);
|
||||
}
|
||||
|
||||
if (shape.intersects(mouse)) {
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
objects.clear();
|
||||
}
|
||||
}
|
||||
378
hacked-libtiled/tiled/core/Sprite.java
Normal file
378
hacked-libtiled/tiled/core/Sprite.java
Normal file
@@ -0,0 +1,378 @@
|
||||
/*
|
||||
* Copyright 2004-2006, Thorbjørn Lindeijer <thorbjorn@lindeijer.nl>
|
||||
* Copyright 2004-2006, Adam Turk <aturk@biggeruniverse.com>
|
||||
*
|
||||
* This file is part of libtiled-java.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package tiled.core;
|
||||
|
||||
import java.awt.Image;
|
||||
import java.awt.Rectangle;
|
||||
import java.util.Iterator;
|
||||
import java.util.Vector;
|
||||
|
||||
public class Sprite
|
||||
{
|
||||
private Vector<KeyFrame> keys;
|
||||
private int borderWidth = 0;
|
||||
private int fpl = 0;
|
||||
private int totalKeys = -1;
|
||||
|
||||
private float currentFrame = 0;
|
||||
private Rectangle frameSize;
|
||||
private boolean bPlaying = true;
|
||||
|
||||
public class KeyFrame
|
||||
{
|
||||
public static final int MASK_ANIMATION = 0x0000000F;
|
||||
|
||||
public static final int KEY_LOOP = 0x01;
|
||||
public static final int KEY_STOP = 0x02;
|
||||
public static final int KEY_AUTO = 0x04;
|
||||
public static final int KEY_REVERSE = 0x08;
|
||||
|
||||
public static final int KEY_NAME_LENGTH_MAX = 32;
|
||||
|
||||
private String name = null;
|
||||
private int id = -1;
|
||||
private int flags = KEY_LOOP;
|
||||
private float frameRate = 1.0f; //one fps
|
||||
private Tile[] frames;
|
||||
|
||||
public KeyFrame() {
|
||||
flags = KEY_LOOP;
|
||||
}
|
||||
|
||||
public KeyFrame(String name) {
|
||||
this();
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public KeyFrame(String name, Tile[] tile) {
|
||||
this(name);
|
||||
frames = tile;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void setFrameRate(float r) {
|
||||
frameRate = r;
|
||||
}
|
||||
|
||||
public void setId(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public int getLastFrame() {
|
||||
return frames.length - 1;
|
||||
}
|
||||
|
||||
public boolean isFrameLast(int frame) {
|
||||
return frames.length - 1 == frame;
|
||||
}
|
||||
|
||||
public void setFlags(int f) {
|
||||
flags = f;
|
||||
}
|
||||
|
||||
public int getFlags() {
|
||||
return flags;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Tile getFrame(int f) {
|
||||
if (f > 0 && f < frames.length) {
|
||||
return frames[f];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public float getFrameRate() {
|
||||
return frameRate;
|
||||
}
|
||||
|
||||
public int getTotalFrames() {
|
||||
return frames.length;
|
||||
}
|
||||
|
||||
public boolean equalsIgnoreCase(String n) {
|
||||
return name != null && name.equalsIgnoreCase(n);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "(" + name + ")" + id + ": @ " + frameRate;
|
||||
}
|
||||
}
|
||||
|
||||
private KeyFrame currentKey = null;
|
||||
|
||||
public Sprite() {
|
||||
frameSize = new Rectangle();
|
||||
keys = new Vector<KeyFrame>();
|
||||
}
|
||||
|
||||
public Sprite(Tile[] frames) {
|
||||
setFrames(frames);
|
||||
}
|
||||
|
||||
public Sprite(Image image, int fpl, int border, int totalFrames) {
|
||||
Tile[] frames = null;
|
||||
this.fpl = fpl;
|
||||
borderWidth = border;
|
||||
|
||||
//TODO: break up the image into tiles
|
||||
|
||||
//given this information, extrapolate the rest...
|
||||
|
||||
frameSize.width = image.getWidth(null) / (fpl + borderWidth * fpl);
|
||||
frameSize.height = (int) (image.getHeight(null) / (Math.ceil(totalFrames / fpl) + Math.ceil(totalFrames / fpl) * borderWidth));
|
||||
createKey("", frames, KeyFrame.KEY_LOOP);
|
||||
}
|
||||
|
||||
public void setFrames(Tile[] frames) {
|
||||
frameSize = new Rectangle(0, 0, frames[0].getWidth(), frames[0].getHeight());
|
||||
|
||||
createKey("", frames, KeyFrame.KEY_LOOP);
|
||||
}
|
||||
|
||||
public void setFrameSize(int w, int h) {
|
||||
frameSize.width = w;
|
||||
frameSize.height = h;
|
||||
}
|
||||
|
||||
public void setBorderWidth(int b) {
|
||||
borderWidth = b;
|
||||
}
|
||||
|
||||
public void setFpl(int f) {
|
||||
fpl = f;
|
||||
}
|
||||
|
||||
public void setCurrentFrame(float c) {
|
||||
if (c < 0) {
|
||||
switch (currentKey.flags & KeyFrame.MASK_ANIMATION) {
|
||||
case KeyFrame.KEY_LOOP:
|
||||
currentFrame = currentKey.getLastFrame();
|
||||
break;
|
||||
case KeyFrame.KEY_AUTO:
|
||||
currentKey = getPreviousKey();
|
||||
currentFrame = currentKey.getLastFrame();
|
||||
break;
|
||||
case KeyFrame.KEY_REVERSE:
|
||||
currentKey.setFrameRate(-currentKey.getFrameRate());
|
||||
currentFrame = 0;
|
||||
break;
|
||||
case KeyFrame.KEY_STOP:
|
||||
bPlaying = false;
|
||||
currentFrame = 0;
|
||||
break;
|
||||
}
|
||||
} else if (c > currentKey.getLastFrame()) {
|
||||
switch (currentKey.flags & KeyFrame.MASK_ANIMATION) {
|
||||
case KeyFrame.KEY_LOOP:
|
||||
currentFrame = 0;
|
||||
break;
|
||||
case KeyFrame.KEY_AUTO:
|
||||
currentFrame = 0;
|
||||
currentKey = getNextKey();
|
||||
break;
|
||||
case KeyFrame.KEY_REVERSE:
|
||||
currentKey.setFrameRate(-currentKey.getFrameRate());
|
||||
currentFrame = currentKey.getLastFrame();
|
||||
break;
|
||||
case KeyFrame.KEY_STOP:
|
||||
bPlaying = false;
|
||||
currentFrame = currentKey.getLastFrame();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
currentFrame = c;
|
||||
}
|
||||
}
|
||||
|
||||
public void setTotalKeys(int t) {
|
||||
totalKeys = t;
|
||||
}
|
||||
|
||||
public Rectangle getFrameSize() {
|
||||
return frameSize;
|
||||
}
|
||||
|
||||
public int getTotalFrames() {
|
||||
int total = 0;
|
||||
for (KeyFrame key : keys) {
|
||||
total += key.getTotalFrames();
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
public int getBorderWidth() {
|
||||
return borderWidth;
|
||||
}
|
||||
|
||||
public Tile getCurrentFrame() {
|
||||
return currentKey.getFrame((int) currentFrame);
|
||||
}
|
||||
|
||||
public KeyFrame getNextKey() {
|
||||
Iterator<KeyFrame> itr = keys.iterator();
|
||||
while (itr.hasNext()) {
|
||||
KeyFrame k = itr.next();
|
||||
if (k == currentKey) {
|
||||
if (itr.hasNext())
|
||||
return itr.next();
|
||||
}
|
||||
}
|
||||
|
||||
return keys.get(0);
|
||||
}
|
||||
|
||||
public KeyFrame getPreviousKey() {
|
||||
//TODO: this
|
||||
return null;
|
||||
}
|
||||
|
||||
public KeyFrame getCurrentKey() {
|
||||
return currentKey;
|
||||
}
|
||||
|
||||
public int getFPL() {
|
||||
return fpl;
|
||||
}
|
||||
|
||||
public int getTotalKeys() {
|
||||
return keys.size();
|
||||
}
|
||||
|
||||
public void setKeyFrameTo(String name) {
|
||||
for (KeyFrame k : keys) {
|
||||
if (k.equalsIgnoreCase(name)) {
|
||||
currentKey = k;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void addKey(KeyFrame k) {
|
||||
keys.add(k);
|
||||
}
|
||||
|
||||
public void removeKey(String name) {
|
||||
keys.remove(getKey(name));
|
||||
}
|
||||
|
||||
public void createKey(String name, Tile[] frames, int flags) {
|
||||
KeyFrame kf = new KeyFrame(name, frames);
|
||||
kf.setName(name);
|
||||
kf.setFlags(flags);
|
||||
addKey(kf);
|
||||
}
|
||||
|
||||
public void iterateFrame() {
|
||||
|
||||
if (currentKey != null) {
|
||||
if (bPlaying) {
|
||||
setCurrentFrame(currentFrame + currentKey.getFrameRate());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current frame relative to the starting frame of the
|
||||
* current key.
|
||||
*
|
||||
* @param c
|
||||
*/
|
||||
public void keySetFrame(int c) {
|
||||
setCurrentFrame(c);
|
||||
}
|
||||
|
||||
public void play() {
|
||||
bPlaying = true;
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
bPlaying = false;
|
||||
}
|
||||
|
||||
public void keyStepBack(int amt) {
|
||||
setCurrentFrame(currentFrame - amt);
|
||||
}
|
||||
|
||||
public void keyStepForward(int amt) {
|
||||
setCurrentFrame(currentFrame + amt);
|
||||
}
|
||||
|
||||
public KeyFrame getKey(String keyName) {
|
||||
for (KeyFrame k : keys) {
|
||||
if (k != null && k.equalsIgnoreCase(keyName)) {
|
||||
return k;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public KeyFrame getKey(int i) {
|
||||
return keys.get(i);
|
||||
}
|
||||
|
||||
public Iterator<KeyFrame> getKeys() throws Exception {
|
||||
return keys.iterator();
|
||||
}
|
||||
|
||||
public Rectangle getCurrentFrameRect() {
|
||||
int x = 0, y = 0;
|
||||
|
||||
if (frameSize.height > 0 && frameSize.width > 0) {
|
||||
y = ((int) currentFrame / fpl) * (frameSize.height + borderWidth);
|
||||
x = ((int) currentFrame % fpl) * (frameSize.width + borderWidth);
|
||||
}
|
||||
|
||||
return new Rectangle(x, y, frameSize.width, frameSize.height);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see Object#toString()
|
||||
*/
|
||||
public String toString() {
|
||||
return "Frame: (" + frameSize.width + "x" + frameSize.height + ")\n" +
|
||||
"Border: " + borderWidth + "\n" +
|
||||
"FPL: " + fpl + "\n" +
|
||||
"Total Frames: " + getTotalFrames() + "\n" +
|
||||
"Total keys: " + totalKeys;
|
||||
}
|
||||
}
|
||||
|
||||
147
hacked-libtiled/tiled/core/Tile.java
Normal file
147
hacked-libtiled/tiled/core/Tile.java
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright 2004-2010, Thorbjørn Lindeijer <thorbjorn@lindeijer.nl>
|
||||
* Copyright 2004-2006, Adam Turk <aturk@biggeruniverse.com>
|
||||
*
|
||||
* This file is part of libtiled-java.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package tiled.core;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* The core class for our tiles.
|
||||
*/
|
||||
public class Tile
|
||||
{
|
||||
private Image image;
|
||||
private int id = -1;
|
||||
private Properties properties;
|
||||
private TileSet tileset;
|
||||
|
||||
public Tile() {
|
||||
// properties = new Properties();
|
||||
}
|
||||
|
||||
public Tile(TileSet set) {
|
||||
this();
|
||||
setTileSet(set);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy constructor
|
||||
*
|
||||
* @param t tile to copy
|
||||
*/
|
||||
public Tile(Tile t) {
|
||||
if (t.properties != null) properties = (Properties) t.properties.clone();
|
||||
tileset = t.tileset;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the id of the tile as long as it is at least 0.
|
||||
*
|
||||
* @param i The id of the tile
|
||||
*/
|
||||
public void setId(int i) {
|
||||
if (i >= 0) {
|
||||
id = i;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the image of the tile.
|
||||
*
|
||||
* @param i the new image of the tile
|
||||
*/
|
||||
public void setImage(Image i) {
|
||||
image = i;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the parent tileset for a tile.
|
||||
*
|
||||
* @param set
|
||||
*/
|
||||
public void setTileSet(TileSet set) {
|
||||
tileset = set;
|
||||
}
|
||||
|
||||
public void setProperties(Properties p) {
|
||||
properties = p;
|
||||
}
|
||||
|
||||
public Properties getProperties() {
|
||||
if (properties == null) return new Properties();
|
||||
return properties;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tile id of this tile, relative to tileset.
|
||||
*
|
||||
* @return id
|
||||
*/
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the {@link tiled.core.TileSet} that this tile is part of.
|
||||
*
|
||||
* @return TileSet
|
||||
*/
|
||||
public TileSet getTileSet() {
|
||||
return tileset;
|
||||
}
|
||||
|
||||
public int getWidth() {
|
||||
if (image != null)
|
||||
return image.getWidth(null);
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int getHeight() {
|
||||
if (image != null)
|
||||
return image.getHeight(null);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tile image for this Tile.
|
||||
*
|
||||
* @return Image
|
||||
*/
|
||||
public Image getImage() {
|
||||
if (tileset != null && tileset.sheet != null) return tileset.sheet.getImage(getId());
|
||||
return image;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.lang.Object#toString()
|
||||
*/
|
||||
public String toString() {
|
||||
return "Tile " + id + " (" + getWidth() + "x" + getHeight() + ")";
|
||||
}
|
||||
}
|
||||
482
hacked-libtiled/tiled/core/TileLayer.java
Normal file
482
hacked-libtiled/tiled/core/TileLayer.java
Normal file
@@ -0,0 +1,482 @@
|
||||
/*
|
||||
* Copyright 2004-2010, Thorbjørn Lindeijer <thorbjorn@lindeijer.nl>
|
||||
* Copyright 2004-2006, Adam Turk <aturk@biggeruniverse.com>
|
||||
*
|
||||
* This file is part of libtiled-java.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package tiled.core;
|
||||
|
||||
import java.awt.Point;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.geom.Area;
|
||||
import java.util.HashMap;
|
||||
import java.util.Properties;
|
||||
|
||||
/**
|
||||
* A TileLayer is a specialized MapLayer, used for tracking two dimensional
|
||||
* tile data.
|
||||
*/
|
||||
public class TileLayer extends MapLayer
|
||||
{
|
||||
protected Tile[][] map;
|
||||
protected HashMap<Object, Properties> tileInstanceProperties = new HashMap<Object, Properties>();
|
||||
|
||||
public Properties getTileInstancePropertiesAt(int x, int y) {
|
||||
if (!bounds.contains(x, y)) {
|
||||
return null;
|
||||
}
|
||||
Object key = new Point(x, y);
|
||||
return tileInstanceProperties.get(key);
|
||||
}
|
||||
|
||||
public void setTileInstancePropertiesAt(int x, int y, Properties tip) {
|
||||
if (bounds.contains(x, y)) {
|
||||
Object key = new Point(x, y);
|
||||
tileInstanceProperties.put(key, tip);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Default constructor.
|
||||
*/
|
||||
public TileLayer() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a TileLayer from the given width and height.
|
||||
*
|
||||
* @param w width in tiles
|
||||
* @param h height in tiles
|
||||
*/
|
||||
public TileLayer(int w, int h) {
|
||||
super(w, h);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a tile layer using the given bounds.
|
||||
*
|
||||
* @param r the bounds of the tile layer.
|
||||
*/
|
||||
public TileLayer(Rectangle r) {
|
||||
super(r);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param m the map this layer is part of
|
||||
*/
|
||||
TileLayer(Map m) {
|
||||
super(m);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param m the map this layer is part of
|
||||
* @param w width in tiles
|
||||
* @param h height in tiles
|
||||
*/
|
||||
public TileLayer(Map m, int w, int h) {
|
||||
super(w, h);
|
||||
setMap(m);
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates the layer by the given Euler angle.
|
||||
*
|
||||
* @param angle The Euler angle (0-360) to rotate the layer array data by.
|
||||
* @see MapLayer#rotate(int)
|
||||
*/
|
||||
public void rotate(int angle) {
|
||||
Tile[][] trans;
|
||||
int xtrans = 0, ytrans = 0;
|
||||
|
||||
switch (angle) {
|
||||
case ROTATE_90:
|
||||
trans = new Tile[bounds.width][bounds.height];
|
||||
xtrans = bounds.height - 1;
|
||||
break;
|
||||
case ROTATE_180:
|
||||
trans = new Tile[bounds.height][bounds.width];
|
||||
xtrans = bounds.width - 1;
|
||||
ytrans = bounds.height - 1;
|
||||
break;
|
||||
case ROTATE_270:
|
||||
trans = new Tile[bounds.width][bounds.height];
|
||||
ytrans = bounds.width - 1;
|
||||
break;
|
||||
default:
|
||||
// System.out.println("Unsupported rotation (" + angle + ")");
|
||||
return;
|
||||
}
|
||||
|
||||
double ra = Math.toRadians(angle);
|
||||
int cos_angle = (int)Math.round(Math.cos(ra));
|
||||
int sin_angle = (int)Math.round(Math.sin(ra));
|
||||
|
||||
for (int y = 0; y < bounds.height; y++) {
|
||||
for (int x = 0; x < bounds.width; x++) {
|
||||
int xrot = x * cos_angle - y * sin_angle;
|
||||
int yrot = x * sin_angle + y * cos_angle;
|
||||
trans[yrot + ytrans][xrot + xtrans] = getTileAt(x+bounds.x, y+bounds.y);
|
||||
}
|
||||
}
|
||||
|
||||
bounds.width = trans[0].length;
|
||||
bounds.height = trans.length;
|
||||
map = trans;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a mirroring function on the layer data. Two orientations are
|
||||
* allowed: vertical and horizontal.
|
||||
*
|
||||
* Example: <code>layer.mirror(MapLayer.MIRROR_VERTICAL);</code> will
|
||||
* mirror the layer data around a horizontal axis.
|
||||
*
|
||||
* @param dir the axial orientation to mirror around
|
||||
*/
|
||||
public void mirror(int dir) {
|
||||
Tile[][] mirror = new Tile[bounds.height][bounds.width];
|
||||
for (int y = 0; y < bounds.height; y++) {
|
||||
for (int x = 0; x < bounds.width; x++) {
|
||||
if (dir == MIRROR_VERTICAL) {
|
||||
mirror[y][x] = map[bounds.height - 1 - y][x];
|
||||
} else {
|
||||
mirror[y][x] = map[y][bounds.width - 1 - x];
|
||||
}
|
||||
}
|
||||
}
|
||||
map = mirror;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks to see if the given Tile is used anywhere in the layer.
|
||||
*
|
||||
* @param t a Tile object to check for
|
||||
* @return <code>true</code> if the Tile is used at least once,
|
||||
* <code>false</code> otherwise.
|
||||
*/
|
||||
public boolean isUsed(Tile t) {
|
||||
for (int y = 0; y < bounds.height; y++) {
|
||||
for (int x = 0; x < bounds.width; x++) {
|
||||
if (map[y][x] == t) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
for (int p = 0; p < 2; p++) {
|
||||
for (int y = 0; y < bounds.height; y++) {
|
||||
for (int x = p; x < bounds.width; x += 2) {
|
||||
if (map[y][x] != null)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the bounds (in tiles) to the specified Rectangle. <b>Caution:</b>
|
||||
* this causes a reallocation of the data array, and all previous data is
|
||||
* lost.
|
||||
*
|
||||
* @param bounds new new bounds of this tile layer (in tiles)
|
||||
* @see MapLayer#setBounds
|
||||
*/
|
||||
protected void setBounds(Rectangle bounds) {
|
||||
super.setBounds(bounds);
|
||||
map = new Tile[bounds.height][bounds.width];
|
||||
|
||||
// Tile instance properties is null when this method is called from
|
||||
// the constructor of MapLayer
|
||||
if (tileInstanceProperties != null) {
|
||||
tileInstanceProperties.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a diff of the two layers, <code>ml</code> is considered the
|
||||
* significant difference.
|
||||
*
|
||||
* @param ml
|
||||
* @return A new MapLayer that represents the difference between this
|
||||
* layer, and the argument, or <b>null</b> if no difference exists.
|
||||
*/
|
||||
public MapLayer createDiff(MapLayer ml) {
|
||||
if (ml == null) { return null; }
|
||||
|
||||
if (ml instanceof TileLayer) {
|
||||
Rectangle r = null;
|
||||
|
||||
for (int y = bounds.y; y < bounds.height + bounds.y; y++) {
|
||||
for (int x = bounds.x; x < bounds.width + bounds.x; x++) {
|
||||
if (((TileLayer)ml).getTileAt(x, y) != getTileAt(x, y)) {
|
||||
if (r != null) {
|
||||
r.add(x, y);
|
||||
} else {
|
||||
r = new Rectangle(new Point(x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (r != null) {
|
||||
MapLayer diff = new TileLayer(
|
||||
new Rectangle(r.x, r.y, r.width + 1, r.height + 1));
|
||||
diff.copyFrom(ml);
|
||||
return diff;
|
||||
} else {
|
||||
return new TileLayer();
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes any occurences of the given tile from this map layer. If layer
|
||||
* is locked, an exception is thrown.
|
||||
*
|
||||
* @param tile the Tile to be removed
|
||||
*/
|
||||
public void removeTile(Tile tile) {
|
||||
for (int y = 0; y < bounds.height; y++) {
|
||||
for (int x = 0; x < bounds.width; x++) {
|
||||
if (map[y][x] == tile) {
|
||||
setTileAt(x + bounds.x, y + bounds.y, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the tile at the specified position. Does nothing if (tx, ty) falls
|
||||
* outside of this layer.
|
||||
*
|
||||
* @param tx x position of tile
|
||||
* @param ty y position of tile
|
||||
* @param ti the tile object to place
|
||||
*/
|
||||
public void setTileAt(int tx, int ty, Tile ti) {
|
||||
if (bounds.contains(tx, ty)) {
|
||||
map[ty - bounds.y][tx - bounds.x] = ti;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tile at the specified position.
|
||||
*
|
||||
* @param tx Tile-space x coordinate
|
||||
* @param ty Tile-space y coordinate
|
||||
* @return tile at position (tx, ty) or <code>null</code> when (tx, ty) is
|
||||
* outside this layer
|
||||
*/
|
||||
public Tile getTileAt(int tx, int ty) {
|
||||
return (bounds.contains(tx, ty)) ?
|
||||
map[ty - bounds.y][tx - bounds.x] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first occurrence (using top down, left to right search) of
|
||||
* the given tile.
|
||||
*
|
||||
* @param t the {@link Tile} to look for
|
||||
* @return A java.awt.Point instance of the first instance of t, or
|
||||
* <code>null</code> if it is not found
|
||||
*/
|
||||
public Point locationOf(Tile t) {
|
||||
for (int y = bounds.y; y < bounds.height + bounds.y; y++) {
|
||||
for (int x = bounds.x; x < bounds.width + bounds.x; x++) {
|
||||
if (getTileAt(x, y) == t) {
|
||||
return new Point(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces all occurrences of the Tile <code>find</code> with the Tile
|
||||
* <code>replace</code> in the entire layer
|
||||
*
|
||||
* @param find the tile to replace
|
||||
* @param replace the replacement tile
|
||||
*/
|
||||
public void replaceTile(Tile find, Tile replace) {
|
||||
for (int y = bounds.y; y < bounds.y + bounds.height; y++) {
|
||||
for (int x = bounds.x; x < bounds.x + bounds.width; x++) {
|
||||
if(getTileAt(x,y) == find) {
|
||||
setTileAt(x, y, replace);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritDoc MapLayer#mergeOnto(MapLayer)
|
||||
*/
|
||||
public void mergeOnto(MapLayer other) {
|
||||
for (int y = bounds.y; y < bounds.y + bounds.height; y++) {
|
||||
for (int x = bounds.x; x < bounds.x + bounds.width; x++) {
|
||||
Tile tile = getTileAt(x, y);
|
||||
if (tile != null) {
|
||||
((TileLayer) other).setTileAt(x, y, tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Like mergeOnto, but will only copy the area specified.
|
||||
*
|
||||
* @see TileLayer#mergeOnto(MapLayer)
|
||||
* @param other
|
||||
* @param mask
|
||||
*/
|
||||
public void maskedMergeOnto(MapLayer other, Area mask) {
|
||||
Rectangle boundBox = mask.getBounds();
|
||||
|
||||
for (int y = boundBox.y; y < boundBox.y + boundBox.height; y++) {
|
||||
for (int x = boundBox.x; x < boundBox.x + boundBox.width; x++) {
|
||||
Tile tile = ((TileLayer) other).getTileAt(x, y);
|
||||
if (mask.contains(x, y) && tile != null) {
|
||||
setTileAt(x, y, tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy data from another layer onto this layer. Unlike mergeOnto,
|
||||
* copyFrom() copies the empty cells as well.
|
||||
*
|
||||
* @see MapLayer#mergeOnto
|
||||
* @param other
|
||||
*/
|
||||
public void copyFrom(MapLayer other) {
|
||||
for (int y = bounds.y; y < bounds.y + bounds.height; y++) {
|
||||
for (int x = bounds.x; x < bounds.x + bounds.width; x++) {
|
||||
setTileAt(x, y, ((TileLayer) other).getTileAt(x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Like copyFrom, but will only copy the area specified.
|
||||
*
|
||||
* @see TileLayer#copyFrom(MapLayer)
|
||||
* @param other
|
||||
* @param mask
|
||||
*/
|
||||
public void maskedCopyFrom(MapLayer other, Area mask) {
|
||||
Rectangle boundBox = mask.getBounds();
|
||||
|
||||
for (int y = boundBox.y; y < boundBox.y + boundBox.height; y++) {
|
||||
for (int x = boundBox.x; x < boundBox.x + boundBox.width; x++) {
|
||||
if (mask.contains(x,y)) {
|
||||
setTileAt(x, y, ((TileLayer) other).getTileAt(x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlike mergeOnto, copyTo includes the null tile when merging.
|
||||
*
|
||||
* @see MapLayer#copyFrom
|
||||
* @see MapLayer#mergeOnto
|
||||
* @param other the layer to copy this layer to
|
||||
*/
|
||||
public void copyTo(MapLayer other) {
|
||||
for (int y = bounds.y; y < bounds.y + bounds.height; y++) {
|
||||
for (int x = bounds.x; x < bounds.x + bounds.width; x++) {
|
||||
((TileLayer) other).setTileAt(x, y, getTileAt(x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a copy of this layer.
|
||||
*
|
||||
* @see Object#clone
|
||||
* @return a clone of this layer, as complete as possible
|
||||
* @exception CloneNotSupportedException
|
||||
*/
|
||||
public Object clone() throws CloneNotSupportedException {
|
||||
TileLayer clone = (TileLayer) super.clone();
|
||||
|
||||
// Clone the layer data
|
||||
clone.map = new Tile[map.length][];
|
||||
clone.tileInstanceProperties = new HashMap<Object, Properties>();
|
||||
|
||||
for (int i = 0; i < map.length; i++) {
|
||||
clone.map[i] = new Tile[map[i].length];
|
||||
System.arraycopy(map[i], 0, clone.map[i], 0, map[i].length);
|
||||
|
||||
for (int j = 0; j < map[i].length; j++) {
|
||||
Properties p = getTileInstancePropertiesAt(i, j);
|
||||
|
||||
if (p != null) {
|
||||
Integer key = i + j * bounds.width;
|
||||
clone.tileInstanceProperties.put(key, (Properties) p.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param width the new width of the layer
|
||||
* @param height the new height of the layer
|
||||
* @param dx the shift in x direction
|
||||
* @param dy the shift in y direction
|
||||
*/
|
||||
public void resize(int width, int height, int dx, int dy) {
|
||||
Tile[][] newMap = new Tile[height][width];
|
||||
HashMap<Object, Properties> newTileInstanceProperties = new HashMap<Object, Properties>();
|
||||
|
||||
int maxX = Math.min(width, bounds.width + dx);
|
||||
int maxY = Math.min(height, bounds.height + dy);
|
||||
|
||||
for (int x = Math.max(0, dx); x < maxX; x++) {
|
||||
for (int y = Math.max(0, dy); y < maxY; y++) {
|
||||
newMap[y][x] = getTileAt(x - dx, y - dy);
|
||||
|
||||
Properties tip = getTileInstancePropertiesAt(x - dx, y - dy);
|
||||
if (tip != null) {
|
||||
newTileInstanceProperties.put(new Point(x, y), tip);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
map = newMap;
|
||||
tileInstanceProperties = newTileInstanceProperties;
|
||||
bounds.width = width;
|
||||
bounds.height = height;
|
||||
}
|
||||
}
|
||||
523
hacked-libtiled/tiled/core/TileSet.java
Normal file
523
hacked-libtiled/tiled/core/TileSet.java
Normal file
@@ -0,0 +1,523 @@
|
||||
/*
|
||||
* Copyright 2004-2010, Thorbjørn Lindeijer <thorbjorn@lindeijer.nl>
|
||||
* Copyright 2004-2006, Adam Turk <aturk@biggeruniverse.com>
|
||||
*
|
||||
* This file is part of libtiled-java.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package tiled.core;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Image;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.Toolkit;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.awt.image.FilteredImageSource;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.Vector;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
import tiled.util.BasicTileCutter;
|
||||
import tiled.util.TileCutter;
|
||||
import tiled.util.TransparentImageFilter;
|
||||
|
||||
import com.gpl.rpg.atcontentstudio.model.maps.TMXMap;
|
||||
import com.gpl.rpg.atcontentstudio.model.sprites.Spritesheet;
|
||||
|
||||
/**
|
||||
* todo: Update documentation
|
||||
* <p>TileSet handles operations on tiles as a set, or group. It has several
|
||||
* advanced internal functions aimed at reducing unnecessary data replication.
|
||||
* A 'tile' is represented internally as two distinct pieces of data. The
|
||||
* first and most important is a {@link Tile} object, and these are held in
|
||||
* a {@link Vector}.</p>
|
||||
*
|
||||
* <p>The other is the tile image.</p>
|
||||
*/
|
||||
public class TileSet implements Iterable<Tile>
|
||||
{
|
||||
private String base;
|
||||
final private Vector<Tile> tiles = new Vector<Tile>();
|
||||
private long tilebmpFileLastModified;
|
||||
private TileCutter tileCutter;
|
||||
private Rectangle tileDimensions;
|
||||
private int tileSpacing;
|
||||
private int tileMargin;
|
||||
private int tilesPerRow;
|
||||
private String externalSource;
|
||||
private File tilebmpFile;
|
||||
private String name;
|
||||
private Color transparentColor;
|
||||
private Image tileSetImage;
|
||||
public Spritesheet sheet = null;
|
||||
|
||||
/**
|
||||
* Default constructor
|
||||
*/
|
||||
public TileSet() {
|
||||
tileDimensions = new Rectangle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a tileset from a tileset image file.
|
||||
*
|
||||
* @param imgFilename
|
||||
* @param cutter
|
||||
* @throws IOException
|
||||
* @see TileSet#importTileBitmap(BufferedImage, TileCutter)
|
||||
*/
|
||||
public void importTileBitmap(String imgFilename, TileCutter cutter)
|
||||
throws IOException
|
||||
{
|
||||
setTilesetImageFilename(imgFilename);
|
||||
|
||||
File f = new File(imgFilename);
|
||||
|
||||
Image image = ImageIO.read(f.getCanonicalFile());
|
||||
if (image == null) {
|
||||
throw new IOException("Failed to load " + tilebmpFile);
|
||||
}
|
||||
|
||||
Toolkit tk = Toolkit.getDefaultToolkit();
|
||||
|
||||
if (transparentColor != null) {
|
||||
int rgb = transparentColor.getRGB();
|
||||
image = tk.createImage(
|
||||
new FilteredImageSource(image.getSource(),
|
||||
new TransparentImageFilter(rgb)));
|
||||
}
|
||||
|
||||
BufferedImage buffered = new BufferedImage(
|
||||
image.getWidth(null),
|
||||
image.getHeight(null),
|
||||
BufferedImage.TYPE_INT_ARGB);
|
||||
buffered.getGraphics().drawImage(image, 0, 0, null);
|
||||
|
||||
importTileBitmap(buffered, cutter);
|
||||
}
|
||||
|
||||
public void loadFromProject(String name, TMXMap tmxMap) {
|
||||
sheet = tmxMap.getProject().getSpritesheet(name);
|
||||
int i = 0;
|
||||
Image tileImage = sheet.getImage(i);
|
||||
while (tileImage != null) {
|
||||
Tile tile = new Tile();
|
||||
// tile.setImage(tileImage);
|
||||
addNewTile(tile);
|
||||
i++;
|
||||
tileImage = sheet.getImage(i);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a tileset from a buffered image. Tiles are cut by the passed
|
||||
* cutter.
|
||||
*
|
||||
* @param tileBitmap the image to be used, must not be null
|
||||
* @param cutter the tile cutter, must not be null
|
||||
*/
|
||||
private void importTileBitmap(BufferedImage tileBitmap, TileCutter cutter)
|
||||
{
|
||||
assert tileBitmap != null;
|
||||
assert cutter != null;
|
||||
|
||||
tileCutter = cutter;
|
||||
tileSetImage = tileBitmap;
|
||||
|
||||
cutter.setImage(tileBitmap);
|
||||
|
||||
tileDimensions = new Rectangle(cutter.getTileDimensions());
|
||||
if (cutter instanceof BasicTileCutter) {
|
||||
BasicTileCutter basicTileCutter = (BasicTileCutter) cutter;
|
||||
tileSpacing = basicTileCutter.getTileSpacing();
|
||||
tileMargin = basicTileCutter.getTileMargin();
|
||||
tilesPerRow = basicTileCutter.getTilesPerRow();
|
||||
}
|
||||
|
||||
Image tileImage = cutter.getNextTile();
|
||||
while (tileImage != null) {
|
||||
Tile tile = new Tile();
|
||||
tile.setImage(tileImage);
|
||||
addNewTile(tile);
|
||||
tileImage = cutter.getNextTile();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes a tileset from a tileset image file.
|
||||
*
|
||||
* @throws IOException
|
||||
* @see TileSet#importTileBitmap(BufferedImage,TileCutter)
|
||||
*/
|
||||
private void refreshImportedTileBitmap()
|
||||
throws IOException
|
||||
{
|
||||
String imgFilename = tilebmpFile.getPath();
|
||||
|
||||
Image image = ImageIO.read(new File(imgFilename));
|
||||
if (image == null) {
|
||||
throw new IOException("Failed to load " + tilebmpFile);
|
||||
}
|
||||
|
||||
Toolkit tk = Toolkit.getDefaultToolkit();
|
||||
|
||||
if (transparentColor != null) {
|
||||
int rgb = transparentColor.getRGB();
|
||||
image = tk.createImage(
|
||||
new FilteredImageSource(image.getSource(),
|
||||
new TransparentImageFilter(rgb)));
|
||||
}
|
||||
|
||||
BufferedImage buffered = new BufferedImage(
|
||||
image.getWidth(null),
|
||||
image.getHeight(null),
|
||||
BufferedImage.TYPE_INT_ARGB);
|
||||
buffered.getGraphics().drawImage(image, 0, 0, null);
|
||||
|
||||
refreshImportedTileBitmap(buffered);
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes a tileset from a buffered image. Tiles are cut by the passed
|
||||
* cutter.
|
||||
*
|
||||
* @param tileBitmap the image to be used, must not be null
|
||||
*/
|
||||
private void refreshImportedTileBitmap(BufferedImage tileBitmap) {
|
||||
assert tileBitmap != null;
|
||||
|
||||
tileCutter.reset();
|
||||
tileCutter.setImage(tileBitmap);
|
||||
|
||||
tileSetImage = tileBitmap;
|
||||
tileDimensions = new Rectangle(tileCutter.getTileDimensions());
|
||||
|
||||
int id = 0;
|
||||
Image tileImage = tileCutter.getNextTile();
|
||||
while (tileImage != null) {
|
||||
Tile tile = getTile(id);
|
||||
tile.setImage(tileImage);
|
||||
tileImage = tileCutter.getNextTile();
|
||||
id++;
|
||||
}
|
||||
}
|
||||
|
||||
public void checkUpdate() throws IOException {
|
||||
if (tilebmpFile != null &&
|
||||
tilebmpFile.lastModified() > tilebmpFileLastModified)
|
||||
{
|
||||
refreshImportedTileBitmap();
|
||||
tilebmpFileLastModified = tilebmpFile.lastModified();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the URI path of the external source of this tile set. By setting
|
||||
* this, the set is implied to be external in all other operations.
|
||||
*
|
||||
* @param source a URI of the tileset image file
|
||||
*/
|
||||
public void setSource(String source) {
|
||||
externalSource = source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the base directory for the tileset
|
||||
*
|
||||
* @param base a String containing the native format directory
|
||||
*/
|
||||
public void setBaseDir(String base) {
|
||||
this.base = base;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the filename of the tileset image. Doesn't change the tileset in
|
||||
* any other way.
|
||||
*
|
||||
* @param name
|
||||
*/
|
||||
public void setTilesetImageFilename(String name) {
|
||||
if (name != null) {
|
||||
tilebmpFile = new File(name);
|
||||
tilebmpFileLastModified = tilebmpFile.lastModified();
|
||||
}
|
||||
else {
|
||||
tilebmpFile = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the name of this tileset.
|
||||
*
|
||||
* @param name the new name for this tileset
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the transparent color in the tileset image.
|
||||
*
|
||||
* @param color
|
||||
*/
|
||||
public void setTransparentColor(Color color) {
|
||||
transparentColor = color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the tile to the set, setting the id of the tile only if the current
|
||||
* value of id is -1.
|
||||
*
|
||||
* @param t the tile to add
|
||||
* @return int The <b>local</b> id of the tile
|
||||
*/
|
||||
public int addTile(Tile t) {
|
||||
if (t.getId() < 0)
|
||||
t.setId(tiles.size());
|
||||
|
||||
if (tileDimensions.width < t.getWidth())
|
||||
tileDimensions.width = t.getWidth();
|
||||
|
||||
if (tileDimensions.height < t.getHeight())
|
||||
tileDimensions.height = t.getHeight();
|
||||
|
||||
tiles.add(t);
|
||||
t.setTileSet(this);
|
||||
|
||||
return t.getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method takes a new Tile object as argument, and in addition to
|
||||
* the functionality of <code>addTile()</code>, sets the id of the tile
|
||||
* to -1.
|
||||
*
|
||||
* @see TileSet#addTile(Tile)
|
||||
* @param t the new tile to add.
|
||||
*/
|
||||
public void addNewTile(Tile t) {
|
||||
t.setId(-1);
|
||||
addTile(t);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a tile from this tileset. Does not invalidate other tile
|
||||
* indices. Removal is simply setting the reference at the specified
|
||||
* index to <b>null</b>.
|
||||
*
|
||||
* @param i the index to remove
|
||||
*/
|
||||
public void removeTile(int i) {
|
||||
tiles.set(i, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the amount of tiles in this tileset.
|
||||
*
|
||||
* @return the amount of tiles in this tileset
|
||||
*/
|
||||
public int size() {
|
||||
return tiles.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum tile id.
|
||||
*
|
||||
* @return the maximum tile id, or -1 when there are no tiles
|
||||
*/
|
||||
public int getMaxTileId() {
|
||||
return tiles.size() - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an iterator over the tiles in this tileset.
|
||||
*
|
||||
* @return an iterator over the tiles in this tileset.
|
||||
*/
|
||||
public Iterator<Tile> iterator() {
|
||||
return tiles.iterator();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the width of tiles in this tileset. All tiles in a tileset
|
||||
* should be the same width, and the same as the tile width of the map the
|
||||
* tileset is used with.
|
||||
*
|
||||
* @return int - The maximum tile width
|
||||
*/
|
||||
public int getTileWidth() {
|
||||
return tileDimensions.width;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the tile height of tiles in this tileset. Not all tiles in a
|
||||
* tileset are required to have the same height, but the height should be
|
||||
* at least the tile height of the map the tileset is used with.
|
||||
*
|
||||
* If there are tiles with varying heights in this tileset, the returned
|
||||
* height will be the maximum.
|
||||
*
|
||||
* @return the max height of the tiles in the set
|
||||
*/
|
||||
public int getTileHeight() {
|
||||
return tileDimensions.height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the spacing between the tiles on the tileset image.
|
||||
* @return the spacing in pixels between the tiles on the tileset image
|
||||
*/
|
||||
public int getTileSpacing() {
|
||||
return tileSpacing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the margin around the tiles on the tileset image.
|
||||
* @return the margin in pixels around the tiles on the tileset image
|
||||
*/
|
||||
public int getTileMargin() {
|
||||
return tileMargin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of tiles per row in the original tileset image.
|
||||
* @return the number of tiles per row in the original tileset image.
|
||||
*/
|
||||
public int getTilesPerRow() {
|
||||
return tilesPerRow;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the tile with <b>local</b> id <code>i</code>.
|
||||
*
|
||||
* @param i local id of tile
|
||||
* @return A tile with local id <code>i</code> or <code>null</code> if no
|
||||
* tile exists with that id
|
||||
*/
|
||||
public Tile getTile(int i) {
|
||||
try {
|
||||
return tiles.get(i);
|
||||
} catch (ArrayIndexOutOfBoundsException a) {}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the first non-null tile in the set.
|
||||
*
|
||||
* @return The first tile in this tileset, or <code>null</code> if none
|
||||
* exists.
|
||||
*/
|
||||
public Tile getFirstTile() {
|
||||
Tile ret = null;
|
||||
int i = 0;
|
||||
while (ret == null && i <= getMaxTileId()) {
|
||||
ret = getTile(i);
|
||||
i++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the source of this tileset.
|
||||
*
|
||||
* @return a filename if tileset is external or <code>null</code> if
|
||||
* tileset is internal.
|
||||
*/
|
||||
public String getSource() {
|
||||
return externalSource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the base directory for the tileset
|
||||
*
|
||||
* @return a directory in native format as given in the tileset file or tag
|
||||
*/
|
||||
public String getBaseDir() {
|
||||
return base;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the filename of the tileset image.
|
||||
*
|
||||
* @return the filename of the tileset image, or <code>null</code> if this
|
||||
* tileset doesn't reference a tileset image
|
||||
*/
|
||||
public String getTilebmpFile() {
|
||||
if (tilebmpFile != null) {
|
||||
try {
|
||||
return tilebmpFile.getCanonicalPath();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
} else if (sheet != null) {
|
||||
try {
|
||||
return sheet.spritesheetFile.getCanonicalPath();
|
||||
} catch (IOException e) {}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the name of this tileset.
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the transparent color of the tileset image, or <code>null</code>
|
||||
* if none is set.
|
||||
*
|
||||
* @return Color - The transparent color of the set
|
||||
*/
|
||||
public Color getTransparentColor() {
|
||||
return transparentColor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the name of the tileset, and the total tiles
|
||||
*/
|
||||
public String toString() {
|
||||
return getName() + " [" + size() + "]";
|
||||
}
|
||||
|
||||
|
||||
// TILE IMAGE CODE
|
||||
|
||||
/**
|
||||
* Returns whether the tileset is derived from a tileset image.
|
||||
*
|
||||
* @return tileSetImage != null
|
||||
*/
|
||||
public boolean isSetFromImage() {
|
||||
return tileSetImage != null;
|
||||
}
|
||||
|
||||
public Spritesheet getSpritesheet() {
|
||||
return this.sheet;
|
||||
}
|
||||
}
|
||||
963
hacked-libtiled/tiled/io/TMXMapReader.java
Normal file
963
hacked-libtiled/tiled/io/TMXMapReader.java
Normal file
@@ -0,0 +1,963 @@
|
||||
/*
|
||||
* Copyright 2004-2010, Thorbjørn Lindeijer <thorbjorn@lindeijer.nl>
|
||||
* Copyright 2004-2006, Adam Turk <aturk@biggeruniverse.com>
|
||||
*
|
||||
* This file is part of libtiled-java.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package tiled.io;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Image;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.io.StringReader;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.Properties;
|
||||
import java.util.TreeMap;
|
||||
import java.util.zip.GZIPInputStream;
|
||||
import java.util.zip.InflaterInputStream;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.NamedNodeMap;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.EntityResolver;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import tiled.core.AnimatedTile;
|
||||
import tiled.core.Map;
|
||||
import tiled.core.MapLayer;
|
||||
import tiled.core.MapObject;
|
||||
import tiled.core.ObjectGroup;
|
||||
import tiled.core.Tile;
|
||||
import tiled.core.TileLayer;
|
||||
import tiled.core.TileSet;
|
||||
import tiled.util.Base64;
|
||||
import tiled.util.BasicTileCutter;
|
||||
import tiled.util.ImageHelper;
|
||||
|
||||
import com.gpl.rpg.atcontentstudio.model.maps.TMXMap;
|
||||
|
||||
/**
|
||||
* The standard map reader for TMX files. Supports reading .tmx, .tmx.gz and *.tsx files.
|
||||
*/
|
||||
public class TMXMapReader
|
||||
{
|
||||
private Map map;
|
||||
private String xmlPath;
|
||||
private String error;
|
||||
private final EntityResolver entityResolver = new MapEntityResolver();
|
||||
private TreeMap<Integer, TileSet> tilesetPerFirstGid;
|
||||
public final TMXMapReaderSettings settings = new TMXMapReaderSettings();
|
||||
private final HashMap<String, TileSet> cachedTilesets = new HashMap<String, TileSet>();
|
||||
|
||||
public static final class TMXMapReaderSettings {
|
||||
public boolean reuseCachedTilesets = false;
|
||||
}
|
||||
|
||||
public TMXMapReader() {
|
||||
}
|
||||
|
||||
String getError() {
|
||||
return error;
|
||||
}
|
||||
|
||||
private static String makeUrl(String filename) throws MalformedURLException {
|
||||
final String url;
|
||||
if (filename.indexOf("://") > 0 || filename.startsWith("file:")) {
|
||||
url = filename;
|
||||
} else {
|
||||
url = new File(filename).toURI().toString();
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
private static int reflectFindMethodByName(Class c, String methodName) {
|
||||
Method[] methods = c.getMethods();
|
||||
for (int i = 0; i < methods.length; i++) {
|
||||
if (methods[i].getName().equalsIgnoreCase(methodName)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private void reflectInvokeMethod(Object invokeVictim, Method method,
|
||||
String[] args) throws Exception
|
||||
{
|
||||
Class[] parameterTypes = method.getParameterTypes();
|
||||
Object[] conformingArguments = new Object[parameterTypes.length];
|
||||
|
||||
if (args.length < parameterTypes.length) {
|
||||
throw new Exception("Insufficient arguments were supplied");
|
||||
}
|
||||
|
||||
for (int i = 0; i < parameterTypes.length; i++) {
|
||||
if ("int".equalsIgnoreCase(parameterTypes[i].getName())) {
|
||||
conformingArguments[i] = new Integer(args[i]);
|
||||
} else if ("float".equalsIgnoreCase(parameterTypes[i].getName())) {
|
||||
conformingArguments[i] = new Float(args[i]);
|
||||
} else if (parameterTypes[i].getName().endsWith("String")) {
|
||||
conformingArguments[i] = args[i];
|
||||
} else if ("boolean".equalsIgnoreCase(parameterTypes[i].getName())) {
|
||||
conformingArguments[i] = Boolean.valueOf(args[i]);
|
||||
} else {
|
||||
// Unsupported argument type, defaulting to String
|
||||
conformingArguments[i] = args[i];
|
||||
}
|
||||
}
|
||||
|
||||
method.invoke(invokeVictim,conformingArguments);
|
||||
}
|
||||
|
||||
private void setOrientation(String o) {
|
||||
if ("isometric".equalsIgnoreCase(o)) {
|
||||
map.setOrientation(Map.ORIENTATION_ISOMETRIC);
|
||||
} else if ("orthogonal".equalsIgnoreCase(o)) {
|
||||
map.setOrientation(Map.ORIENTATION_ORTHOGONAL);
|
||||
} else if ("hexagonal".equalsIgnoreCase(o)) {
|
||||
map.setOrientation(Map.ORIENTATION_HEXAGONAL);
|
||||
} else if ("shifted".equalsIgnoreCase(o)) {
|
||||
map.setOrientation(Map.ORIENTATION_SHIFTED);
|
||||
} else {
|
||||
// System.out.println("Unknown orientation '" + o + "'");
|
||||
}
|
||||
}
|
||||
|
||||
private static String getAttributeValue(Node node, String attribname) {
|
||||
final NamedNodeMap attributes = node.getAttributes();
|
||||
String value = null;
|
||||
if (attributes != null) {
|
||||
Node attribute = attributes.getNamedItem(attribname);
|
||||
if (attribute != null) {
|
||||
value = attribute.getNodeValue();
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private static int getAttribute(Node node, String attribname, int def) {
|
||||
final String attr = getAttributeValue(node, attribname);
|
||||
if (attr != null) {
|
||||
return Integer.parseInt(attr);
|
||||
} else {
|
||||
return def;
|
||||
}
|
||||
}
|
||||
|
||||
private Object unmarshalClass(Class reflector, Node node)
|
||||
throws InstantiationException, IllegalAccessException,
|
||||
InvocationTargetException {
|
||||
Constructor cons = null;
|
||||
try {
|
||||
cons = reflector.getConstructor((Class[]) null);
|
||||
} catch (SecurityException e1) {
|
||||
e1.printStackTrace();
|
||||
} catch (NoSuchMethodException e1) {
|
||||
e1.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
Object o = cons.newInstance((Object[]) null);
|
||||
Node n;
|
||||
|
||||
Method[] methods = reflector.getMethods();
|
||||
NamedNodeMap nnm = node.getAttributes();
|
||||
|
||||
if (nnm != null) {
|
||||
for (int i = 0; i < nnm.getLength(); i++) {
|
||||
n = nnm.item(i);
|
||||
|
||||
try {
|
||||
int j = reflectFindMethodByName(reflector,
|
||||
"set" + n.getNodeName());
|
||||
if (j >= 0) {
|
||||
reflectInvokeMethod(o,methods[j],
|
||||
new String [] {n.getNodeValue()});
|
||||
} else {
|
||||
// System.out.println("Unsupported attribute '" +
|
||||
// n.getNodeName() + "' on <" +
|
||||
// node.getNodeName() + "> tag");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
private Image unmarshalImage(Node t, String baseDir) throws IOException
|
||||
{
|
||||
Image img = null;
|
||||
|
||||
String source = getAttributeValue(t, "source");
|
||||
|
||||
if (source != null) {
|
||||
if (checkRoot(source)) {
|
||||
source = makeUrl(source);
|
||||
} else {
|
||||
source = makeUrl(baseDir + source);
|
||||
}
|
||||
img = ImageIO.read(new URL(source));
|
||||
} else {
|
||||
NodeList nl = t.getChildNodes();
|
||||
|
||||
for (int i = 0; i < nl.getLength(); i++) {
|
||||
Node node = nl.item(i);
|
||||
if ("data".equals(node.getNodeName())) {
|
||||
Node cdata = node.getFirstChild();
|
||||
if (cdata != null) {
|
||||
String sdata = cdata.getNodeValue();
|
||||
char[] charArray = sdata.trim().toCharArray();
|
||||
byte[] imageData = Base64.decode(charArray);
|
||||
img = ImageHelper.bytesToImage(imageData);
|
||||
|
||||
// Deriving a scaled instance, even if it has the same
|
||||
// size, somehow makes drawing of the tiles a lot
|
||||
// faster on various systems (seen on Linux, Windows
|
||||
// and MacOS X).
|
||||
img = img.getScaledInstance(
|
||||
img.getWidth(null), img.getHeight(null),
|
||||
Image.SCALE_FAST);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return img;
|
||||
}
|
||||
|
||||
private TileSet unmarshalTilesetFile(InputStream in, String filename, TMXMap tmxMap)
|
||||
throws Exception
|
||||
{
|
||||
TileSet set = null;
|
||||
Node tsNode;
|
||||
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
try {
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
//builder.setErrorHandler(new XMLErrorHandler());
|
||||
Document tsDoc = builder.parse(in, ".");
|
||||
|
||||
String xmlPathSave = xmlPath;
|
||||
if (filename.indexOf(File.separatorChar) >= 0) {
|
||||
xmlPath = filename.substring(0,
|
||||
filename.lastIndexOf(File.separatorChar) + 1);
|
||||
}
|
||||
|
||||
NodeList tsNodeList = tsDoc.getElementsByTagName("tileset");
|
||||
|
||||
// There can be only one tileset in a .tsx file.
|
||||
tsNode = tsNodeList.item(0);
|
||||
if (tsNode != null) {
|
||||
set = unmarshalTileset(tsNode, tmxMap);
|
||||
if (set.getSource() != null) {
|
||||
// System.out.println("Recursive external tilesets are not supported.");
|
||||
}
|
||||
set.setSource(filename);
|
||||
}
|
||||
|
||||
xmlPath = xmlPathSave;
|
||||
} catch (SAXException e) {
|
||||
error = "Failed while loading " + filename + ": " +
|
||||
e.getLocalizedMessage();
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
private TileSet unmarshalTileset(Node t, TMXMap tmxMap) throws Exception {
|
||||
String source = getAttributeValue(t, "source");
|
||||
String basedir = getAttributeValue(t, "basedir");
|
||||
int firstGid = getAttribute(t, "firstgid", 1);
|
||||
|
||||
String tilesetBaseDir = xmlPath;
|
||||
|
||||
if (basedir != null) {
|
||||
tilesetBaseDir = basedir; //makeUrl(basedir);
|
||||
}
|
||||
|
||||
if (source != null) {
|
||||
String filename = tilesetBaseDir + source;
|
||||
//if (checkRoot(source)) {
|
||||
// filename = makeUrl(source);
|
||||
//}
|
||||
|
||||
TileSet ext = null;
|
||||
|
||||
try {
|
||||
InputStream in = new URL(makeUrl(filename)).openStream();
|
||||
ext = unmarshalTilesetFile(in, filename, tmxMap);
|
||||
setFirstGidForTileset(ext, firstGid);
|
||||
} catch (FileNotFoundException fnf) {
|
||||
error = "Could not find external tileset file " + filename;
|
||||
}
|
||||
|
||||
if (ext == null) {
|
||||
error = "Tileset " + source + " was not loaded correctly!";
|
||||
}
|
||||
|
||||
return ext;
|
||||
}
|
||||
else {
|
||||
final int tileWidth = getAttribute(t, "tilewidth", map != null ? map.getTileWidth() : 0);
|
||||
final int tileHeight = getAttribute(t, "tileheight", map != null ? map.getTileHeight() : 0);
|
||||
final int tileSpacing = getAttribute(t, "spacing", 0);
|
||||
final int tileMargin = getAttribute(t, "margin", 0);
|
||||
|
||||
final String name = getAttributeValue(t, "name");
|
||||
|
||||
TileSet set;
|
||||
if (settings.reuseCachedTilesets) {
|
||||
set = cachedTilesets.get(name);
|
||||
if (set != null) {
|
||||
setFirstGidForTileset(set, firstGid);
|
||||
return set;
|
||||
}
|
||||
set = new TileSet();
|
||||
cachedTilesets.put(name, set);
|
||||
} else {
|
||||
set = new TileSet();
|
||||
}
|
||||
|
||||
set.setName(name);
|
||||
set.setBaseDir(basedir);
|
||||
setFirstGidForTileset(set, firstGid);
|
||||
|
||||
boolean hasTilesetImage = false;
|
||||
NodeList children = t.getChildNodes();
|
||||
|
||||
for (int i = 0; i < children.getLength(); i++) {
|
||||
Node child = children.item(i);
|
||||
|
||||
if (child.getNodeName().equalsIgnoreCase("image")) {
|
||||
if (hasTilesetImage) {
|
||||
// System.out.println("Ignoring illegal image element after tileset image.");
|
||||
continue;
|
||||
}
|
||||
|
||||
String imgSource = getAttributeValue(child, "source");
|
||||
String transStr = getAttributeValue(child, "trans");
|
||||
|
||||
if (imgSource != null) {
|
||||
// Not a shared image, but an entire set in one image
|
||||
// file. There should be only one image element in this
|
||||
// case.
|
||||
|
||||
if (tmxMap.getProject().getSpritesheet(name) != null) {
|
||||
set.loadFromProject(name, tmxMap);
|
||||
} else {
|
||||
|
||||
|
||||
hasTilesetImage = true;
|
||||
|
||||
// FIXME: importTileBitmap does not fully support URLs
|
||||
String sourcePath = imgSource;
|
||||
if (! new File(imgSource).isAbsolute()) {
|
||||
sourcePath = tilesetBaseDir + imgSource;
|
||||
}
|
||||
|
||||
if (transStr != null) {
|
||||
if (transStr.startsWith("#"))
|
||||
transStr = transStr.substring(1);
|
||||
|
||||
int colorInt = Integer.parseInt(transStr, 16);
|
||||
Color color = new Color(colorInt);
|
||||
set.setTransparentColor(color);
|
||||
}
|
||||
|
||||
set.importTileBitmap(sourcePath, new BasicTileCutter(
|
||||
tileWidth, tileHeight, tileSpacing, tileMargin));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (child.getNodeName().equalsIgnoreCase("tile")) {
|
||||
Tile tile = unmarshalTile(set, child, tilesetBaseDir);
|
||||
if (!hasTilesetImage || tile.getId() > set.getMaxTileId()) {
|
||||
set.addTile(tile);
|
||||
} else {
|
||||
Tile myTile = set.getTile(tile.getId());
|
||||
myTile.setProperties(tile.getProperties());
|
||||
//TODO: there is the possibility here of overlaying images,
|
||||
// which some people may want
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
}
|
||||
|
||||
private MapObject readMapObject(Node t) throws Exception {
|
||||
final String name = getAttributeValue(t, "name");
|
||||
final String type = getAttributeValue(t, "type");
|
||||
final int x = getAttribute(t, "x", 0);
|
||||
final int y = getAttribute(t, "y", 0);
|
||||
final int width = getAttribute(t, "width", 0);
|
||||
final int height = getAttribute(t, "height", 0);
|
||||
|
||||
MapObject obj = new MapObject(x, y, width, height);
|
||||
if (name != null)
|
||||
obj.setName(name);
|
||||
if (type != null)
|
||||
obj.setType(type);
|
||||
|
||||
NodeList children = t.getChildNodes();
|
||||
for (int i = 0; i < children.getLength(); i++) {
|
||||
Node child = children.item(i);
|
||||
if ("image".equalsIgnoreCase(child.getNodeName())) {
|
||||
String source = getAttributeValue(child, "source");
|
||||
if (source != null) {
|
||||
if (! new File(source).isAbsolute()) {
|
||||
source = xmlPath + source;
|
||||
}
|
||||
obj.setImageSource(source);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Properties props = new Properties();
|
||||
readProperties(children, props);
|
||||
|
||||
obj.setProperties(props);
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads properties from amongst the given children. When a "properties"
|
||||
* element is encountered, it recursively calls itself with the children
|
||||
* of this node. This function ensures backward compatibility with tmx
|
||||
* version 0.99a.
|
||||
*
|
||||
* Support for reading property values stored as character data was added
|
||||
* in Tiled 0.7.0 (tmx version 0.99c).
|
||||
*
|
||||
* @param children the children amongst which to find properties
|
||||
* @param props the properties object to set the properties of
|
||||
*/
|
||||
private static void readProperties(NodeList children, Properties props) {
|
||||
for (int i = 0; i < children.getLength(); i++) {
|
||||
Node child = children.item(i);
|
||||
if ("property".equalsIgnoreCase(child.getNodeName())) {
|
||||
final String key = getAttributeValue(child, "name");
|
||||
String value = getAttributeValue(child, "value");
|
||||
if (value == null) {
|
||||
Node grandChild = child.getFirstChild();
|
||||
if (grandChild != null) {
|
||||
value = grandChild.getNodeValue();
|
||||
if (value != null)
|
||||
value = value.trim();
|
||||
}
|
||||
}
|
||||
if (value != null)
|
||||
props.setProperty(key, value);
|
||||
}
|
||||
else if ("properties".equals(child.getNodeName())) {
|
||||
readProperties(child.getChildNodes(), props);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Tile unmarshalTile(TileSet set, Node t, String baseDir)
|
||||
throws Exception
|
||||
{
|
||||
Tile tile = null;
|
||||
NodeList children = t.getChildNodes();
|
||||
boolean isAnimated = false;
|
||||
|
||||
for (int i = 0; i < children.getLength(); i++) {
|
||||
Node child = children.item(i);
|
||||
if ("animation".equalsIgnoreCase(child.getNodeName())) {
|
||||
isAnimated = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (isAnimated) {
|
||||
tile = (Tile) unmarshalClass(AnimatedTile.class, t);
|
||||
} else {
|
||||
tile = (Tile) unmarshalClass(Tile.class, t);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
error = "Failed creating tile: " + e.getLocalizedMessage();
|
||||
return tile;
|
||||
}
|
||||
|
||||
tile.setTileSet(set);
|
||||
|
||||
readProperties(children, tile.getProperties());
|
||||
|
||||
for (int i = 0; i < children.getLength(); i++) {
|
||||
Node child = children.item(i);
|
||||
if ("image".equalsIgnoreCase(child.getNodeName())) {
|
||||
int id = getAttribute(child, "id", -1);
|
||||
Image img = unmarshalImage(child, baseDir);
|
||||
tile.setImage(img);
|
||||
} else if ("animation".equalsIgnoreCase(child.getNodeName())) {
|
||||
// TODO: fill this in once TMXMapWriter is complete
|
||||
}
|
||||
}
|
||||
|
||||
return tile;
|
||||
}
|
||||
|
||||
private MapLayer unmarshalObjectGroup(Node t) throws Exception {
|
||||
ObjectGroup og = null;
|
||||
try {
|
||||
og = (ObjectGroup)unmarshalClass(ObjectGroup.class, t);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return og;
|
||||
}
|
||||
|
||||
final int offsetX = getAttribute(t, "x", 0);
|
||||
final int offsetY = getAttribute(t, "y", 0);
|
||||
og.setOffset(offsetX, offsetY);
|
||||
|
||||
// Add all objects from the objects group
|
||||
NodeList children = t.getChildNodes();
|
||||
|
||||
for (int i = 0; i < children.getLength(); i++) {
|
||||
Node child = children.item(i);
|
||||
if ("object".equalsIgnoreCase(child.getNodeName())) {
|
||||
og.addObject(readMapObject(child));
|
||||
}
|
||||
}
|
||||
|
||||
Properties props = new Properties();
|
||||
readProperties(children, props);
|
||||
og.setProperties(props);
|
||||
|
||||
return og;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a map layer from a layer node.
|
||||
* @param t the node representing the "layer" element
|
||||
* @return the loaded map layer
|
||||
* @throws Exception
|
||||
*/
|
||||
private MapLayer readLayer(Node t, TMXMap tmxMap) 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");
|
||||
|
||||
if (encoding != null && "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;
|
||||
|
||||
String comp = getAttributeValue(child, "compression");
|
||||
|
||||
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;
|
||||
|
||||
java.util.Map.Entry<Integer, TileSet> ts = findTileSetForTileGID(tileId);
|
||||
if (ts != null) {
|
||||
ml.setTileAt(x, y,
|
||||
ts.getValue().getTile(tileId - ts.getKey()));
|
||||
if (ts.getValue().getSpritesheet() != null) {
|
||||
tmxMap.usedSpritesheets.add(ts.getValue().getSpritesheet());
|
||||
ts.getValue().getSpritesheet().addBacklink(tmxMap);
|
||||
}
|
||||
} else {
|
||||
ml.setTileAt(x, y, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} 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);
|
||||
java.util.Map.Entry<Integer, TileSet> ts = findTileSetForTileGID(tileId);
|
||||
if (ts != null) {
|
||||
ml.setTileAt(x, y,
|
||||
ts.getValue().getTile(tileId - ts.getKey()));
|
||||
if (ts.getValue().getSpritesheet() != null) {
|
||||
tmxMap.usedSpritesheets.add(ts.getValue().getSpritesheet());
|
||||
ts.getValue().getSpritesheet().addBacklink(tmxMap);
|
||||
}
|
||||
} else {
|
||||
ml.setTileAt(x, y, null);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
private void buildMap(Document doc, TMXMap tmxMap) throws Exception {
|
||||
Node item, mapNode;
|
||||
|
||||
mapNode = doc.getDocumentElement();
|
||||
|
||||
if (!"map".equals(mapNode.getNodeName())) {
|
||||
throw new Exception("Not a valid tmx map file.");
|
||||
}
|
||||
|
||||
// Get the map dimensions and create the map
|
||||
int mapWidth = getAttribute(mapNode, "width", 0);
|
||||
int mapHeight = getAttribute(mapNode, "height", 0);
|
||||
|
||||
if (mapWidth > 0 && mapHeight > 0) {
|
||||
map = new Map(mapWidth, mapHeight);
|
||||
} else {
|
||||
// Maybe this map is still using the dimensions element
|
||||
NodeList l = doc.getElementsByTagName("dimensions");
|
||||
for (int i = 0; (item = l.item(i)) != null; i++) {
|
||||
if (item.getParentNode() == mapNode) {
|
||||
mapWidth = getAttribute(item, "width", 0);
|
||||
mapHeight = getAttribute(item, "height", 0);
|
||||
|
||||
if (mapWidth > 0 && mapHeight > 0) {
|
||||
map = new Map(mapWidth, mapHeight);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (map == null) {
|
||||
throw new Exception("Couldn't locate map dimensions.");
|
||||
}
|
||||
|
||||
// Load other map attributes
|
||||
String orientation = getAttributeValue(mapNode, "orientation");
|
||||
int tileWidth = getAttribute(mapNode, "tilewidth", 0);
|
||||
int tileHeight = getAttribute(mapNode, "tileheight", 0);
|
||||
|
||||
if (tileWidth > 0) {
|
||||
map.setTileWidth(tileWidth);
|
||||
}
|
||||
if (tileHeight > 0) {
|
||||
map.setTileHeight(tileHeight);
|
||||
}
|
||||
|
||||
if (orientation != null) {
|
||||
setOrientation(orientation);
|
||||
} else {
|
||||
setOrientation("orthogonal");
|
||||
}
|
||||
|
||||
// Load properties
|
||||
readProperties(mapNode.getChildNodes(), map.getProperties());
|
||||
|
||||
// Load tilesets first, in case order is munged
|
||||
tilesetPerFirstGid = new TreeMap<Integer, TileSet>();
|
||||
NodeList l = doc.getElementsByTagName("tileset");
|
||||
for (int i = 0; (item = l.item(i)) != null; i++) {
|
||||
map.addTileset(unmarshalTileset(item, tmxMap));
|
||||
}
|
||||
|
||||
// Load the layers and objectgroups
|
||||
for (Node sibs = mapNode.getFirstChild(); sibs != null;
|
||||
sibs = sibs.getNextSibling())
|
||||
{
|
||||
if ("layer".equals(sibs.getNodeName())) {
|
||||
MapLayer layer = readLayer(sibs, tmxMap);
|
||||
if (layer != null) {
|
||||
map.addLayer(layer);
|
||||
}
|
||||
}
|
||||
else if ("objectgroup".equals(sibs.getNodeName())) {
|
||||
MapLayer layer = unmarshalObjectGroup(sibs);
|
||||
if (layer != null) {
|
||||
map.addLayer(layer);
|
||||
}
|
||||
}
|
||||
}
|
||||
tilesetPerFirstGid = null;
|
||||
}
|
||||
|
||||
private Map unmarshal(Reader reader, TMXMap tmxMap) throws Exception {
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
Document doc;
|
||||
try {
|
||||
factory.setIgnoringComments(true);
|
||||
factory.setIgnoringElementContentWhitespace(true);
|
||||
factory.setExpandEntityReferences(false);
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
builder.setEntityResolver(entityResolver);
|
||||
InputSource insrc = new InputSource(reader);
|
||||
insrc.setSystemId(xmlPath);
|
||||
insrc.setEncoding("UTF-8");
|
||||
doc = builder.parse(insrc);
|
||||
} catch (SAXException e) {
|
||||
e.printStackTrace();
|
||||
throw new Exception("Error while parsing map file: " +
|
||||
e.toString());
|
||||
}
|
||||
|
||||
buildMap(doc, tmxMap);
|
||||
|
||||
return map;
|
||||
}
|
||||
private Map unmarshal(InputStream in, TMXMap tmxMap) throws Exception {
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
Document doc;
|
||||
try {
|
||||
factory.setIgnoringComments(true);
|
||||
factory.setIgnoringElementContentWhitespace(true);
|
||||
factory.setExpandEntityReferences(false);
|
||||
DocumentBuilder builder = factory.newDocumentBuilder();
|
||||
builder.setEntityResolver(entityResolver);
|
||||
InputSource insrc = new InputSource(in);
|
||||
insrc.setSystemId(xmlPath);
|
||||
insrc.setEncoding("UTF-8");
|
||||
doc = builder.parse(insrc);
|
||||
} catch (SAXException e) {
|
||||
e.printStackTrace();
|
||||
throw new Exception("Error while parsing map file: " +
|
||||
e.toString());
|
||||
}
|
||||
|
||||
buildMap(doc, tmxMap);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
public Map readMap(String filename, TMXMap tmxMap) throws Exception {
|
||||
xmlPath = filename.substring(0,
|
||||
filename.lastIndexOf(File.separatorChar) + 1);
|
||||
|
||||
String xmlFile = makeUrl(filename);
|
||||
//xmlPath = makeUrl(xmlPath);
|
||||
|
||||
URL url = new URL(xmlFile);
|
||||
InputStream is = url.openStream();
|
||||
|
||||
// Wrap with GZIP decoder for .tmx.gz files
|
||||
if (filename.endsWith(".gz")) {
|
||||
is = new GZIPInputStream(is);
|
||||
}
|
||||
|
||||
Map unmarshalledMap = unmarshal(is, tmxMap);
|
||||
unmarshalledMap.setFilename(filename);
|
||||
|
||||
map = null;
|
||||
|
||||
return unmarshalledMap;
|
||||
}
|
||||
|
||||
public Map readMap(InputStream in, TMXMap tmxMap) throws Exception {
|
||||
xmlPath = makeUrl(".");
|
||||
|
||||
Map unmarshalledMap = unmarshal(in, tmxMap);
|
||||
|
||||
//unmarshalledMap.setFilename(xmlFile)
|
||||
//
|
||||
return unmarshalledMap;
|
||||
}
|
||||
|
||||
public Map readMap(Reader reader, TMXMap tmxMap) throws Exception {
|
||||
xmlPath = makeUrl(".");
|
||||
|
||||
Map unmarshalledMap = unmarshal(reader, tmxMap);
|
||||
|
||||
//unmarshalledMap.setFilename(xmlFile)
|
||||
//
|
||||
return unmarshalledMap;
|
||||
}
|
||||
|
||||
public TileSet readTileset(String filename, TMXMap tmxMap) throws Exception {
|
||||
String xmlFile = filename;
|
||||
|
||||
xmlPath = filename.substring(0,
|
||||
filename.lastIndexOf(File.separatorChar) + 1);
|
||||
|
||||
xmlFile = makeUrl(xmlFile);
|
||||
xmlPath = makeUrl(xmlPath);
|
||||
|
||||
URL url = new URL(xmlFile);
|
||||
return unmarshalTilesetFile(url.openStream(), filename, tmxMap);
|
||||
}
|
||||
|
||||
public TileSet readTileset(InputStream in, TMXMap tmxMap) throws Exception {
|
||||
return unmarshalTilesetFile(in, ".", tmxMap);
|
||||
}
|
||||
|
||||
public boolean accept(File pathName) {
|
||||
try {
|
||||
String path = pathName.getCanonicalPath();
|
||||
if (path.endsWith(".tmx") || path.endsWith(".tsx") ||
|
||||
path.endsWith(".tmx.gz")) {
|
||||
return true;
|
||||
}
|
||||
} catch (IOException e) {}
|
||||
return false;
|
||||
}
|
||||
|
||||
private class MapEntityResolver implements EntityResolver
|
||||
{
|
||||
public InputSource resolveEntity(String publicId, String systemId) {
|
||||
if (systemId.equals("http://mapeditor.org/dtd/1.0/map.dtd")) {
|
||||
return new InputSource(TMXMapReader.class.getResourceAsStream(
|
||||
"resources/map.dtd"));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This utility function will check the specified string to see if it
|
||||
* starts with one of the OS root designations. (Ex.: '/' on Unix, 'C:' on
|
||||
* Windows)
|
||||
*
|
||||
* @param filename a filename to check for absolute or relative path
|
||||
* @return <code>true</code> if the specified filename starts with a
|
||||
* filesystem root, <code>false</code> otherwise.
|
||||
*/
|
||||
public static boolean checkRoot(String filename) {
|
||||
File[] roots = File.listRoots();
|
||||
|
||||
for (File root : roots) {
|
||||
try {
|
||||
String canonicalRoot = root.getCanonicalPath().toLowerCase();
|
||||
if (filename.toLowerCase().startsWith(canonicalRoot)) {
|
||||
return true;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
// Do we care?
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the tile set and its corresponding firstgid that matches the given
|
||||
* global tile id.
|
||||
*
|
||||
*
|
||||
* @param gid a global tile id
|
||||
* @return the tileset containing the tile with the given global tile id,
|
||||
* or <code>null</code> when no such tileset exists
|
||||
*/
|
||||
private java.util.Map.Entry<Integer, TileSet> findTileSetForTileGID(int gid) {
|
||||
return tilesetPerFirstGid.floorEntry(gid);
|
||||
}
|
||||
|
||||
private void setFirstGidForTileset(TileSet tileset, int firstGid) {
|
||||
tilesetPerFirstGid.put(firstGid, tileset);
|
||||
}
|
||||
|
||||
}
|
||||
613
hacked-libtiled/tiled/io/TMXMapWriter.java
Normal file
613
hacked-libtiled/tiled/io/TMXMapWriter.java
Normal file
@@ -0,0 +1,613 @@
|
||||
/*
|
||||
* Copyright 2004-2010, Thorbjørn Lindeijer <thorbjorn@lindeijer.nl>
|
||||
* Copyright 2004-2008, Adam Turk <aturk@biggeruniverse.com>
|
||||
*
|
||||
* This file is part of libtiled-java.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package tiled.io;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Rectangle;
|
||||
import java.io.*;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.*;
|
||||
import java.util.zip.DeflaterOutputStream;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
import tiled.core.*;
|
||||
import tiled.core.Map;
|
||||
import tiled.io.xml.XMLWriter;
|
||||
import tiled.util.Base64;
|
||||
|
||||
/**
|
||||
* A writer for Tiled's TMX map format.
|
||||
*/
|
||||
public class TMXMapWriter
|
||||
{
|
||||
private static final int LAST_BYTE = 0x000000FF;
|
||||
|
||||
private static final boolean encodeLayerData = true;
|
||||
private static final boolean compressLayerData = encodeLayerData;
|
||||
|
||||
private HashMap<String, Integer> firstGidPerTileset;
|
||||
|
||||
public static class Settings {
|
||||
public static final String LAYER_COMPRESSION_METHOD_GZIP = "gzip";
|
||||
public static final String LAYER_COMPRESSION_METHOD_ZLIB = "zlib";
|
||||
|
||||
public String layerCompressionMethod = LAYER_COMPRESSION_METHOD_GZIP;
|
||||
}
|
||||
public Settings settings = new Settings();
|
||||
|
||||
/**
|
||||
* Saves a map to an XML file.
|
||||
*
|
||||
* @param filename the filename of the map file
|
||||
*/
|
||||
public void writeMap(Map map, String filename) throws Exception {
|
||||
OutputStream os = new FileOutputStream(filename);
|
||||
|
||||
if (filename.endsWith(".tmx.gz")) {
|
||||
os = new GZIPOutputStream(os);
|
||||
}
|
||||
|
||||
Writer writer = new OutputStreamWriter(os, Charset.forName("UTF-8"));
|
||||
XMLWriter xmlWriter = new XMLWriter(writer);
|
||||
|
||||
xmlWriter.startDocument();
|
||||
writeMap(map, xmlWriter, filename);
|
||||
xmlWriter.endDocument();
|
||||
|
||||
writer.flush();
|
||||
|
||||
if (os instanceof GZIPOutputStream) {
|
||||
((GZIPOutputStream)os).finish();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves a tileset to an XML file.
|
||||
*
|
||||
* @param filename the filename of the tileset file
|
||||
*/
|
||||
public void writeTileset(TileSet set, String filename) throws Exception {
|
||||
OutputStream os = new FileOutputStream(filename);
|
||||
Writer writer = new OutputStreamWriter(os, Charset.forName("UTF-8"));
|
||||
XMLWriter xmlWriter = new XMLWriter(writer);
|
||||
|
||||
xmlWriter.startDocument();
|
||||
writeTileset(set, xmlWriter, filename);
|
||||
xmlWriter.endDocument();
|
||||
|
||||
writer.flush();
|
||||
}
|
||||
|
||||
|
||||
public void writeMap(Map map, OutputStream out) throws Exception {
|
||||
writeMap(map, out, "/.");
|
||||
}
|
||||
|
||||
public void writeMap(Map map, OutputStream out, String workingDirectory) throws Exception {
|
||||
Writer writer = new OutputStreamWriter(out,Charset.forName("UTF-8"));
|
||||
XMLWriter xmlWriter = new XMLWriter(writer);
|
||||
|
||||
xmlWriter.startDocument();
|
||||
writeMap(map, xmlWriter, workingDirectory);
|
||||
xmlWriter.endDocument();
|
||||
|
||||
writer.flush();
|
||||
}
|
||||
|
||||
public void writeTileset(TileSet set, OutputStream out) throws Exception {
|
||||
Writer writer = new OutputStreamWriter(out, Charset.forName("UTF-8"));
|
||||
XMLWriter xmlWriter = new XMLWriter(writer);
|
||||
|
||||
xmlWriter.startDocument();
|
||||
writeTileset(set, xmlWriter, "/.");
|
||||
xmlWriter.endDocument();
|
||||
|
||||
writer.flush();
|
||||
}
|
||||
|
||||
private void writeMap(Map map, XMLWriter w, String wp) throws IOException {
|
||||
w.writeDocType("map", null, "http://mapeditor.org/dtd/1.0/map.dtd");
|
||||
w.startElement("map");
|
||||
|
||||
w.writeAttribute("version", "1.0");
|
||||
|
||||
switch (map.getOrientation()) {
|
||||
case Map.ORIENTATION_ORTHOGONAL:
|
||||
w.writeAttribute("orientation", "orthogonal"); break;
|
||||
case Map.ORIENTATION_ISOMETRIC:
|
||||
w.writeAttribute("orientation", "isometric"); break;
|
||||
case Map.ORIENTATION_HEXAGONAL:
|
||||
w.writeAttribute("orientation", "hexagonal"); break;
|
||||
case Map.ORIENTATION_SHIFTED:
|
||||
w.writeAttribute("orientation", "shifted"); break;
|
||||
}
|
||||
|
||||
w.writeAttribute("width", map.getWidth());
|
||||
w.writeAttribute("height", map.getHeight());
|
||||
w.writeAttribute("tilewidth", map.getTileWidth());
|
||||
w.writeAttribute("tileheight", map.getTileHeight());
|
||||
|
||||
writeProperties(map.getProperties(), w);
|
||||
|
||||
firstGidPerTileset = new HashMap<String, Integer>();
|
||||
int firstgid = 1;
|
||||
for (TileSet tileset : map.getTileSets()) {
|
||||
setFirstGidForTileset(tileset, firstgid);
|
||||
writeTilesetReference(tileset, w, wp);
|
||||
firstgid += tileset.getMaxTileId() + 1;
|
||||
}
|
||||
|
||||
for (MapLayer layer : map) {
|
||||
writeMapLayer(layer, w, wp);
|
||||
}
|
||||
firstGidPerTileset = null;
|
||||
|
||||
w.endElement();
|
||||
}
|
||||
|
||||
private static void writeProperties(Properties props, XMLWriter w) throws
|
||||
IOException
|
||||
{
|
||||
if (!props.isEmpty()) {
|
||||
final SortedSet<Object> propertyKeys = new TreeSet<Object>();
|
||||
propertyKeys.addAll(props.keySet());
|
||||
w.startElement("properties");
|
||||
for (Object propertyKey : propertyKeys) {
|
||||
final String key = (String) propertyKey;
|
||||
final String property = props.getProperty(key);
|
||||
w.startElement("property");
|
||||
w.writeAttribute("name", key);
|
||||
if (property.indexOf('\n') == -1) {
|
||||
w.writeAttribute("value", property);
|
||||
} else {
|
||||
// Save multiline values as character data
|
||||
w.writeCDATA(property);
|
||||
}
|
||||
w.endElement();
|
||||
}
|
||||
w.endElement();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a reference to an external tileset into a XML document. In the
|
||||
* case where the tileset is not stored in an external file, writes the
|
||||
* contents of the tileset instead.
|
||||
*
|
||||
* @param set the tileset to write a reference to
|
||||
* @param w the XML writer to write to
|
||||
* @param wp the working directory of the map
|
||||
* @throws java.io.IOException
|
||||
*/
|
||||
private void writeTilesetReference(TileSet set, XMLWriter w, String wp)
|
||||
throws IOException {
|
||||
|
||||
String source = set.getSource();
|
||||
|
||||
if (source == null) {
|
||||
writeTileset(set, w, wp);
|
||||
} else {
|
||||
w.startElement("tileset");
|
||||
w.writeAttribute("firstgid", getFirstGidForTileset(set));
|
||||
w.writeAttribute("source", getRelativePath(wp, source));
|
||||
if (set.getBaseDir() != null) {
|
||||
w.writeAttribute("basedir", set.getBaseDir());
|
||||
}
|
||||
w.endElement();
|
||||
}
|
||||
}
|
||||
|
||||
private void writeTileset(TileSet set, XMLWriter w, String wp)
|
||||
throws IOException {
|
||||
|
||||
String tileBitmapFile = set.getTilebmpFile();
|
||||
String name = set.getName();
|
||||
|
||||
w.startElement("tileset");
|
||||
w.writeAttribute("firstgid", getFirstGidForTileset(set));
|
||||
|
||||
if (name != null) {
|
||||
w.writeAttribute("name", name);
|
||||
}
|
||||
|
||||
if (tileBitmapFile != null) {
|
||||
w.writeAttribute("tilewidth", set.getTileWidth());
|
||||
w.writeAttribute("tileheight", set.getTileHeight());
|
||||
|
||||
final int tileSpacing = set.getTileSpacing();
|
||||
final int tileMargin = set.getTileMargin();
|
||||
if (tileSpacing != 0) {
|
||||
w.writeAttribute("spacing", tileSpacing);
|
||||
}
|
||||
if (tileMargin != 0) {
|
||||
w.writeAttribute("margin", tileMargin);
|
||||
}
|
||||
}
|
||||
|
||||
if (set.getBaseDir() != null) {
|
||||
w.writeAttribute("basedir", set.getBaseDir());
|
||||
}
|
||||
|
||||
if (tileBitmapFile != null) {
|
||||
w.startElement("image");
|
||||
w.writeAttribute("source", getRelativePath(wp, tileBitmapFile));
|
||||
|
||||
Color trans = set.getTransparentColor();
|
||||
if (trans != null) {
|
||||
w.writeAttribute("trans", Integer.toHexString(
|
||||
trans.getRGB()).substring(2));
|
||||
}
|
||||
w.endElement();
|
||||
|
||||
// Write tile properties when necessary.
|
||||
for (Tile tile : set) {
|
||||
// todo: move the null check back into the iterator?
|
||||
if (tile != null && !tile.getProperties().isEmpty()) {
|
||||
w.startElement("tile");
|
||||
w.writeAttribute("id", tile.getId());
|
||||
writeProperties(tile.getProperties(), w);
|
||||
w.endElement();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Check to see if there is a need to write tile elements
|
||||
boolean needWrite = false;
|
||||
|
||||
// As long as one has properties, they all need to be written.
|
||||
// TODO: This shouldn't be necessary
|
||||
for (Tile tile : set) {
|
||||
if (!tile.getProperties().isEmpty()) {
|
||||
needWrite = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (needWrite) {
|
||||
for (Tile tile : set) {
|
||||
// todo: move this check back into the iterator?
|
||||
if (tile != null) {
|
||||
writeTile(tile, w);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
w.endElement();
|
||||
}
|
||||
|
||||
private static void writeObjectGroup(ObjectGroup o, XMLWriter w, String wp)
|
||||
throws IOException
|
||||
{
|
||||
Iterator<MapObject> itr = o.getObjects();
|
||||
while (itr.hasNext()) {
|
||||
writeMapObject(itr.next(), w, wp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes this layer to an XMLWriter. This should be done <b>after</b> the
|
||||
* first global ids for the tilesets are determined, in order for the right
|
||||
* gids to be written to the layer data.
|
||||
*/
|
||||
private void writeMapLayer(MapLayer l, XMLWriter w, String wp) throws IOException {
|
||||
Rectangle bounds = l.getBounds();
|
||||
|
||||
if (l instanceof ObjectGroup) {
|
||||
w.startElement("objectgroup");
|
||||
} else {
|
||||
w.startElement("layer");
|
||||
}
|
||||
|
||||
w.writeAttribute("name", l.getName());
|
||||
if (bounds.width != 0) {
|
||||
w.writeAttribute("width", bounds.width);
|
||||
}
|
||||
if (bounds.height != 0) {
|
||||
w.writeAttribute("height", bounds.height);
|
||||
}
|
||||
if (bounds.x != 0) {
|
||||
w.writeAttribute("x", bounds.x);
|
||||
}
|
||||
if (bounds.y != 0) {
|
||||
w.writeAttribute("y", bounds.y);
|
||||
}
|
||||
|
||||
if (!l.isVisible()) {
|
||||
w.writeAttribute("visible", "0");
|
||||
}
|
||||
if (l.getOpacity() < 1.0f) {
|
||||
w.writeAttribute("opacity", l.getOpacity());
|
||||
}
|
||||
|
||||
writeProperties(l.getProperties(), w);
|
||||
|
||||
if (l instanceof ObjectGroup){
|
||||
writeObjectGroup((ObjectGroup) l, w, wp);
|
||||
} else if (l instanceof TileLayer) {
|
||||
final TileLayer tl = (TileLayer) l;
|
||||
w.startElement("data");
|
||||
if (encodeLayerData) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
OutputStream out;
|
||||
|
||||
w.writeAttribute("encoding", "base64");
|
||||
|
||||
DeflaterOutputStream dos;
|
||||
if (compressLayerData) {
|
||||
if (Settings.LAYER_COMPRESSION_METHOD_ZLIB.equalsIgnoreCase(settings.layerCompressionMethod)) {
|
||||
dos = new DeflaterOutputStream(baos);
|
||||
} else if (Settings.LAYER_COMPRESSION_METHOD_GZIP.equalsIgnoreCase(settings.layerCompressionMethod)) {
|
||||
dos = new GZIPOutputStream(baos);
|
||||
} else {
|
||||
throw new IOException("Unrecognized compression method \"" + settings.layerCompressionMethod + "\" for map layer " + l.getName());
|
||||
}
|
||||
out = dos;
|
||||
w.writeAttribute("compression", settings.layerCompressionMethod);
|
||||
} else {
|
||||
out = baos;
|
||||
}
|
||||
|
||||
for (int y = 0; y < l.getHeight(); y++) {
|
||||
for (int x = 0; x < l.getWidth(); x++) {
|
||||
Tile tile = tl.getTileAt(x + bounds.x,
|
||||
y + bounds.y);
|
||||
int gid = 0;
|
||||
|
||||
if (tile != null) {
|
||||
gid = getGid(tile);
|
||||
}
|
||||
|
||||
out.write(gid & LAST_BYTE);
|
||||
out.write(gid >> 8 & LAST_BYTE);
|
||||
out.write(gid >> 16 & LAST_BYTE);
|
||||
out.write(gid >> 24 & LAST_BYTE);
|
||||
}
|
||||
}
|
||||
|
||||
if (compressLayerData && dos != null) {
|
||||
dos.finish();
|
||||
}
|
||||
|
||||
w.writeCDATA(Base64.encodeToString(baos.toByteArray(), false));
|
||||
} else {
|
||||
for (int y = 0; y < l.getHeight(); y++) {
|
||||
for (int x = 0; x < l.getWidth(); x++) {
|
||||
Tile tile = tl.getTileAt(x + bounds.x, y + bounds.y);
|
||||
int gid = 0;
|
||||
|
||||
if (tile != null) {
|
||||
gid = getGid(tile);
|
||||
}
|
||||
|
||||
w.startElement("tile");
|
||||
w.writeAttribute("gid", gid);
|
||||
w.endElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
w.endElement();
|
||||
|
||||
boolean tilePropertiesElementStarted = false;
|
||||
|
||||
for (int y = 0; y < l.getHeight(); y++) {
|
||||
for (int x = 0; x < l.getWidth(); x++) {
|
||||
Properties tip = tl.getTileInstancePropertiesAt(x, y);
|
||||
|
||||
if (tip != null && !tip.isEmpty()) {
|
||||
if (!tilePropertiesElementStarted) {
|
||||
w.startElement("tileproperties");
|
||||
tilePropertiesElementStarted = true;
|
||||
}
|
||||
w.startElement("tile");
|
||||
|
||||
w.writeAttribute("x", x);
|
||||
w.writeAttribute("y", y);
|
||||
|
||||
writeProperties(tip, w);
|
||||
|
||||
w.endElement();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (tilePropertiesElementStarted)
|
||||
w.endElement();
|
||||
}
|
||||
w.endElement();
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to write tile elements for tilesets not based on a tileset image.
|
||||
*
|
||||
* @param tile the tile instance that should be written
|
||||
* @param w the writer to write to
|
||||
* @throws IOException when an io error occurs
|
||||
*/
|
||||
private void writeTile(Tile tile, XMLWriter w) throws IOException {
|
||||
w.startElement("tile");
|
||||
w.writeAttribute("id", tile.getId());
|
||||
|
||||
writeProperties(tile.getProperties(), w);
|
||||
|
||||
if (tile instanceof AnimatedTile)
|
||||
writeAnimation(((AnimatedTile)tile).getSprite(), w);
|
||||
|
||||
w.endElement();
|
||||
}
|
||||
|
||||
private void writeAnimation(Sprite s, XMLWriter w) throws IOException {
|
||||
w.startElement("animation");
|
||||
for (int k = 0; k < s.getTotalKeys(); k++) {
|
||||
Sprite.KeyFrame key = s.getKey(k);
|
||||
w.startElement("keyframe");
|
||||
w.writeAttribute("name", key.getName());
|
||||
for (int it = 0; it < key.getTotalFrames(); it++) {
|
||||
Tile stile = key.getFrame(it);
|
||||
w.startElement("tile");
|
||||
w.writeAttribute("gid", getGid(stile));
|
||||
w.endElement();
|
||||
}
|
||||
w.endElement();
|
||||
}
|
||||
w.endElement();
|
||||
}
|
||||
|
||||
private static void writeMapObject(MapObject mapObject, XMLWriter w, String wp)
|
||||
throws IOException
|
||||
{
|
||||
w.startElement("object");
|
||||
w.writeAttribute("name", mapObject.getName());
|
||||
|
||||
if (mapObject.getType().length() != 0)
|
||||
w.writeAttribute("type", mapObject.getType());
|
||||
|
||||
w.writeAttribute("x", mapObject.getX());
|
||||
w.writeAttribute("y", mapObject.getY());
|
||||
|
||||
if (mapObject.getWidth() != 0)
|
||||
w.writeAttribute("width", mapObject.getWidth());
|
||||
if (mapObject.getHeight() != 0)
|
||||
w.writeAttribute("height", mapObject.getHeight());
|
||||
|
||||
writeProperties(mapObject.getProperties(), w);
|
||||
|
||||
if (mapObject.getImageSource().length() > 0) {
|
||||
w.startElement("image");
|
||||
w.writeAttribute("source",
|
||||
getRelativePath(wp, mapObject.getImageSource()));
|
||||
w.endElement();
|
||||
}
|
||||
|
||||
w.endElement();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the relative path from one file to the other. The function
|
||||
* expects absolute paths, relative paths will be converted to absolute
|
||||
* using the working directory.
|
||||
*
|
||||
* @param from the path of the origin file
|
||||
* @param to the path of the destination file
|
||||
* @return the relative path from origin to destination
|
||||
*/
|
||||
public static String getRelativePath(String from, String to) {
|
||||
if(!(new File(to)).isAbsolute())
|
||||
return to;
|
||||
|
||||
// Make the two paths absolute and unique
|
||||
try {
|
||||
from = new File(from).getCanonicalPath();
|
||||
to = new File(to).getCanonicalPath();
|
||||
} catch (IOException e) {
|
||||
}
|
||||
|
||||
File fromFile = new File(from);
|
||||
File toFile = new File(to);
|
||||
Vector<String> fromParents = new Vector<String>();
|
||||
Vector<String> toParents = new Vector<String>();
|
||||
|
||||
// Iterate to find both parent lists
|
||||
while (fromFile != null) {
|
||||
fromParents.add(0, fromFile.getName());
|
||||
fromFile = fromFile.getParentFile();
|
||||
}
|
||||
while (toFile != null) {
|
||||
toParents.add(0, toFile.getName());
|
||||
toFile = toFile.getParentFile();
|
||||
}
|
||||
|
||||
// Iterate while parents are the same
|
||||
int shared = 0;
|
||||
int maxShared = Math.min(fromParents.size(), toParents.size());
|
||||
for (shared = 0; shared < maxShared; shared++) {
|
||||
String fromParent = fromParents.get(shared);
|
||||
String toParent = toParents.get(shared);
|
||||
if (!fromParent.equals(toParent)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Append .. for each remaining parent in fromParents
|
||||
StringBuffer relPathBuf = new StringBuffer();
|
||||
for (int i = shared; i < fromParents.size() - 1; i++) {
|
||||
relPathBuf.append(".." + File.separator);
|
||||
}
|
||||
|
||||
// Add the remaining part in toParents
|
||||
for (int i = shared; i < toParents.size() - 1; i++) {
|
||||
relPathBuf.append(toParents.get(i) + File.separator);
|
||||
}
|
||||
relPathBuf.append(new File(to).getName());
|
||||
String relPath = relPathBuf.toString();
|
||||
|
||||
// Turn around the slashes when path is relative
|
||||
try {
|
||||
String absPath = new File(relPath).getCanonicalPath();
|
||||
|
||||
if (!absPath.equals(relPath)) {
|
||||
// Path is not absolute, turn slashes around
|
||||
// Assumes: \ does not occur in file names
|
||||
relPath = relPath.replace('\\', '/');
|
||||
}
|
||||
} catch (IOException e) {
|
||||
}
|
||||
|
||||
return relPath;
|
||||
}
|
||||
|
||||
public boolean accept(File pathName) {
|
||||
try {
|
||||
String path = pathName.getCanonicalPath();
|
||||
if (path.endsWith(".tmx") || path.endsWith(".tsx") || path.endsWith(".tmx.gz")) {
|
||||
return true;
|
||||
}
|
||||
} catch (IOException e) {}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the global tile id of the given tile.
|
||||
*
|
||||
* @return global tile id of the given tile
|
||||
*/
|
||||
private int getGid(Tile tile) {
|
||||
TileSet tileset = tile.getTileSet();
|
||||
if (tileset != null) {
|
||||
return tile.getId() + getFirstGidForTileset(tileset);
|
||||
}
|
||||
return tile.getId();
|
||||
}
|
||||
|
||||
private void setFirstGidForTileset(TileSet tileset, int firstGid) {
|
||||
firstGidPerTileset.put(tileset.getName(), firstGid);
|
||||
}
|
||||
|
||||
private int getFirstGidForTileset(TileSet tileset) {
|
||||
return firstGidPerTileset.get(tileset.getName());
|
||||
}
|
||||
}
|
||||
121
hacked-libtiled/tiled/io/resources/map.dtd
Normal file
121
hacked-libtiled/tiled/io/resources/map.dtd
Normal file
@@ -0,0 +1,121 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<!--
|
||||
format 0.7.1 by
|
||||
Tiled Developers (mapeditor.org)
|
||||
documented in dtd form originally by
|
||||
Olivier.Beaton@quadir.net
|
||||
Creative Commons Attribution 3.0
|
||||
http://creativecommons.org/licenses/by/3.0/
|
||||
last updated on
|
||||
2008-08-05
|
||||
-->
|
||||
|
||||
<!ELEMENT map (properties?, tileset*, (layer | objectgroup)*)>
|
||||
<!ATTLIST map
|
||||
xmlns CDATA #IMPLIED
|
||||
xmlns:xsi CDATA #IMPLIED
|
||||
xsi:schemaLocation CDATA #IMPLIED
|
||||
version CDATA #REQUIRED
|
||||
orientation (orthogonal | isometric | hexagonal | shifted) #REQUIRED
|
||||
width CDATA #REQUIRED
|
||||
height CDATA #REQUIRED
|
||||
tilewidth CDATA #REQUIRED
|
||||
tileheight CDATA #REQUIRED
|
||||
>
|
||||
|
||||
<!ELEMENT properties (property*)>
|
||||
|
||||
<!ELEMENT property EMPTY>
|
||||
<!ATTLIST property
|
||||
name CDATA #REQUIRED
|
||||
value CDATA #REQUIRED
|
||||
>
|
||||
|
||||
<!--
|
||||
data is required when a child of tilset
|
||||
data is not valid when a child of tile
|
||||
-->
|
||||
<!ELEMENT image (data?)>
|
||||
<!--
|
||||
format is required when a child of tileset
|
||||
format is not valid when a child of tile
|
||||
source here is required when tileset tileheight/tilewidth -> image is used and you are referencing an outside image
|
||||
-->
|
||||
<!ATTLIST image
|
||||
format CDATA #IMPLIED
|
||||
id CDATA #IMPLIED
|
||||
source CDATA #IMPLIED
|
||||
trans CDATA #IMPLIED
|
||||
>
|
||||
|
||||
<!--
|
||||
#PCDATA when data is child of image
|
||||
tile* when data is child of layer without compression
|
||||
-->
|
||||
<!ELEMENT data (#PCDATA | tile)*>
|
||||
<!ATTLIST data
|
||||
encoding CDATA #IMPLIED
|
||||
compression CDATA #IMPLIED
|
||||
>
|
||||
|
||||
<!ELEMENT tileset (image*, tile*)>
|
||||
<!--
|
||||
name REQUIRED only if source tsx not present
|
||||
source here refers to a TSX
|
||||
-->
|
||||
<!ATTLIST tileset
|
||||
name CDATA #IMPLIED
|
||||
firstgid CDATA #REQUIRED
|
||||
source CDATA #IMPLIED
|
||||
tilewidth CDATA #IMPLIED
|
||||
tileheight CDATA #IMPLIED
|
||||
spacing CDATA #IMPLIED
|
||||
margin CDATA #IMPLIED
|
||||
>
|
||||
|
||||
<!--
|
||||
image required when child of all but layer -> data
|
||||
image not valid when child of layer -> data
|
||||
-->
|
||||
<!ELEMENT tile (properties?, image?)>
|
||||
<!--
|
||||
id required when child of all but layer -> data
|
||||
id not valid when child of layer -> data
|
||||
gid required when child of layer -> data
|
||||
gid not valid when not child of layer -> data
|
||||
-->
|
||||
<!ATTLIST tile
|
||||
id CDATA #IMPLIED
|
||||
gid CDATA #IMPLIED
|
||||
>
|
||||
|
||||
<!ELEMENT layer (properties?, data)>
|
||||
<!ATTLIST layer
|
||||
name CDATA #REQUIRED
|
||||
width CDATA #REQUIRED
|
||||
height CDATA #REQUIRED
|
||||
x CDATA #IMPLIED
|
||||
y CDATA #IMPLIED
|
||||
opacity CDATA #IMPLIED
|
||||
visible (0 | 1) #IMPLIED
|
||||
>
|
||||
|
||||
<!ELEMENT objectgroup (object*)>
|
||||
<!ATTLIST objectgroup
|
||||
name CDATA #REQUIRED
|
||||
width CDATA #IMPLIED
|
||||
height CDATA #IMPLIED
|
||||
x CDATA #IMPLIED
|
||||
y CDATA #IMPLIED
|
||||
>
|
||||
|
||||
<!ELEMENT object (properties?, image?)>
|
||||
<!ATTLIST object
|
||||
name CDATA #REQUIRED
|
||||
type CDATA #REQUIRED
|
||||
x CDATA #REQUIRED
|
||||
y CDATA #REQUIRED
|
||||
width CDATA #IMPLIED
|
||||
height CDATA #IMPLIED
|
||||
>
|
||||
206
hacked-libtiled/tiled/io/xml/XMLWriter.java
Normal file
206
hacked-libtiled/tiled/io/xml/XMLWriter.java
Normal file
@@ -0,0 +1,206 @@
|
||||
/*
|
||||
* Copyright 2004-2006, Thorbjørn Lindeijer <thorbjorn@lindeijer.nl>
|
||||
* Copyright 2004-2006, Adam Turk <aturk@biggeruniverse.com>
|
||||
*
|
||||
* This file is part of libtiled-java.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package tiled.io.xml;
|
||||
|
||||
import java.lang.String;
|
||||
import java.io.Writer;
|
||||
import java.io.IOException;
|
||||
import java.util.Stack;
|
||||
|
||||
/**
|
||||
* A simple helper class to write an XML file, based on
|
||||
* http://www.xmlsoft.org/html/libxml-xmlwriter.html
|
||||
*/
|
||||
public class XMLWriter
|
||||
{
|
||||
private boolean bIndent = true;
|
||||
private String indentString = " ";
|
||||
private String newLine = "\n";
|
||||
private final Writer w;
|
||||
|
||||
private final Stack<String> openElements;
|
||||
private boolean bStartTagOpen;
|
||||
private boolean bDocumentOpen;
|
||||
|
||||
|
||||
public XMLWriter(Writer writer) {
|
||||
openElements = new Stack<String>();
|
||||
w = writer;
|
||||
}
|
||||
|
||||
|
||||
public void setIndent(boolean bIndent) {
|
||||
this.bIndent = bIndent;
|
||||
newLine = bIndent ? "\n" : "";
|
||||
}
|
||||
|
||||
public void setIndentString(String indentString) {
|
||||
this.indentString = indentString;
|
||||
}
|
||||
|
||||
|
||||
public void startDocument() throws IOException {
|
||||
startDocument("1.0");
|
||||
}
|
||||
|
||||
public void startDocument(String version) throws IOException {
|
||||
w.write("<?xml version=\"" + version + "\" encoding=\"UTF-8\"?>"
|
||||
+ newLine);
|
||||
bDocumentOpen = true;
|
||||
}
|
||||
|
||||
public void writeDocType(String name, String pubId, String sysId)
|
||||
throws IOException, XMLWriterException {
|
||||
if (!bDocumentOpen) {
|
||||
throw new XMLWriterException(
|
||||
"Can't write DocType, no open document.");
|
||||
} else if (!openElements.isEmpty()) {
|
||||
throw new XMLWriterException(
|
||||
"Can't write DocType, open elements exist.");
|
||||
}
|
||||
|
||||
w.write("<!DOCTYPE " + name + " ");
|
||||
|
||||
if (pubId != null) {
|
||||
w.write("PUBLIC \"" + pubId + "\"");
|
||||
if (sysId != null) {
|
||||
w.write(" \"" + sysId + "\"");
|
||||
}
|
||||
} else if (sysId != null) {
|
||||
w.write("SYSTEM \"" + sysId + "\"");
|
||||
}
|
||||
|
||||
w.write(">" + newLine);
|
||||
}
|
||||
|
||||
public void startElement(String name)
|
||||
throws IOException, XMLWriterException {
|
||||
if (!bDocumentOpen) {
|
||||
throw new XMLWriterException(
|
||||
"Can't start new element, no open document.");
|
||||
}
|
||||
|
||||
if (bStartTagOpen) {
|
||||
w.write(">" + newLine);
|
||||
}
|
||||
|
||||
writeIndent();
|
||||
w.write("<" + name);
|
||||
|
||||
openElements.push(name);
|
||||
bStartTagOpen = true;
|
||||
}
|
||||
|
||||
|
||||
public void endDocument() throws IOException {
|
||||
// End all open elements.
|
||||
while (!openElements.isEmpty()) {
|
||||
endElement();
|
||||
}
|
||||
|
||||
w.flush(); //writers do not always flush automatically...
|
||||
}
|
||||
|
||||
public void endElement() throws IOException {
|
||||
String name = openElements.pop();
|
||||
|
||||
// If start tag still open, end with />, else with </name>.
|
||||
if (bStartTagOpen) {
|
||||
w.write("/>" + newLine);
|
||||
bStartTagOpen = false;
|
||||
} else {
|
||||
writeIndent();
|
||||
w.write("</" + name + ">" + newLine);
|
||||
}
|
||||
|
||||
// Set document closed when last element is closed
|
||||
if (openElements.isEmpty()) {
|
||||
bDocumentOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void writeAttribute(String name, String content)
|
||||
throws IOException, XMLWriterException {
|
||||
if (bStartTagOpen) {
|
||||
String escapedContent = (content != null) ?
|
||||
content.replaceAll("\"", """) : "";
|
||||
w.write(" " + name + "=\"" + escapedContent + "\"");
|
||||
} else {
|
||||
throw new XMLWriterException(
|
||||
"Can't write attribute without open start tag.");
|
||||
}
|
||||
}
|
||||
|
||||
public void writeAttribute(String name, int content)
|
||||
throws IOException, XMLWriterException {
|
||||
writeAttribute(name, String.valueOf(content));
|
||||
}
|
||||
|
||||
public void writeAttribute(String name, float content)
|
||||
throws IOException, XMLWriterException {
|
||||
writeAttribute(name, String.valueOf(content));
|
||||
}
|
||||
|
||||
public void writeCDATA(String content) throws IOException {
|
||||
if (bStartTagOpen) {
|
||||
w.write(">" + newLine);
|
||||
bStartTagOpen = false;
|
||||
}
|
||||
|
||||
writeIndent();
|
||||
w.write(content + newLine);
|
||||
}
|
||||
|
||||
public void writeComment(String content) throws IOException {
|
||||
if (bStartTagOpen) {
|
||||
w.write(">" + newLine);
|
||||
bStartTagOpen = false;
|
||||
}
|
||||
|
||||
writeIndent();
|
||||
w.write("<!-- " + content + " -->" + newLine);
|
||||
}
|
||||
|
||||
public void writeElement(String name, String content)
|
||||
throws IOException, XMLWriterException {
|
||||
startElement(name);
|
||||
writeCDATA(content);
|
||||
endElement();
|
||||
}
|
||||
|
||||
|
||||
private void writeIndent() throws IOException {
|
||||
if (bIndent) {
|
||||
for (int i = 0; i < openElements.size(); i++) {
|
||||
w.write(indentString);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
36
hacked-libtiled/tiled/io/xml/XMLWriterException.java
Normal file
36
hacked-libtiled/tiled/io/xml/XMLWriterException.java
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2004-2006, Thorbjørn Lindeijer <thorbjorn@lindeijer.nl>
|
||||
* Copyright 2004-2006, Adam Turk <aturk@biggeruniverse.com>
|
||||
*
|
||||
* This file is part of libtiled-java.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package tiled.io.xml;
|
||||
|
||||
public class XMLWriterException extends RuntimeException
|
||||
{
|
||||
public XMLWriterException(String error) {
|
||||
super(error);
|
||||
}
|
||||
}
|
||||
575
hacked-libtiled/tiled/util/Base64.java
Normal file
575
hacked-libtiled/tiled/util/Base64.java
Normal file
@@ -0,0 +1,575 @@
|
||||
package tiled.util;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/** A very fast and memory efficient class to encode and decode to and from BASE64 in full accordance
|
||||
* with RFC 2045.<br><br>
|
||||
* On Windows XP sp1 with 1.4.2_04 and later ;), this encoder and decoder is about 10 times faster
|
||||
* on small arrays (10 - 1000 bytes) and 2-3 times as fast on larger arrays (10000 - 1000000 bytes)
|
||||
* compared to <code>sun.misc.Encoder()/Decoder()</code>.<br><br>
|
||||
*
|
||||
* On byte arrays the encoder is about 20% faster than Jakarta Commons Base64 Codec for encode and
|
||||
* about 50% faster for decoding large arrays. This implementation is about twice as fast on very small
|
||||
* arrays (< 30 bytes). If source/destination is a <code>String</code> this
|
||||
* version is about three times as fast due to the fact that the Commons Codec result has to be recoded
|
||||
* to a <code>String</code> from <code>byte[]</code>, which is very expensive.<br><br>
|
||||
*
|
||||
* This encode/decode algorithm doesn't create any temporary arrays as many other codecs do, it only
|
||||
* allocates the resulting array. This produces less garbage and it is possible to handle arrays twice
|
||||
* as large as algorithms that create a temporary array. (E.g. Jakarta Commons Codec). It is unknown
|
||||
* whether Sun's <code>sun.misc.Encoder()/Decoder()</code> produce temporary arrays but since performance
|
||||
* is quite low it probably does.<br><br>
|
||||
*
|
||||
* The encoder produces the same output as the Sun one except that the Sun's encoder appends
|
||||
* a trailing line separator if the last character isn't a pad. Unclear why but it only adds to the
|
||||
* length and is probably a side effect. Both are in conformance with RFC 2045 though.<br>
|
||||
* Commons codec seem to always att a trailing line separator.<br><br>
|
||||
*
|
||||
* <b>Note!</b>
|
||||
* The encode/decode method pairs (types) come in three versions with the <b>exact</b> same algorithm and
|
||||
* thus a lot of code redundancy. This is to not create any temporary arrays for transcoding to/from different
|
||||
* format types. The methods not used can simply be commented out.<br><br>
|
||||
*
|
||||
* There is also a "fast" version of all decode methods that works the same way as the normal ones, but
|
||||
* har a few demands on the decoded input. Normally though, these fast verions should be used if the source if
|
||||
* the input is known and it hasn't bee tampered with.<br><br>
|
||||
*
|
||||
* If you find the code useful or you find a bug, please send me a note at base64 @ miginfocom . com.
|
||||
*
|
||||
* Licence (BSD):
|
||||
* ==============
|
||||
*
|
||||
* Copyright (c) 2004, Mikael Grev, MiG InfoCom AB. (base64 @ miginfocom . com)
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright notice, this list
|
||||
* of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice, this
|
||||
* list of conditions and the following disclaimer in the documentation and/or other
|
||||
* materials provided with the distribution.
|
||||
* Neither the name of the MiG InfoCom AB nor the names of its contributors may be
|
||||
* used to endorse or promote products derived from this software without specific
|
||||
* prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
|
||||
* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
||||
* OF SUCH DAMAGE.
|
||||
*
|
||||
* @version 2.2
|
||||
* @author Mikael Grev
|
||||
* Date: 2004-aug-02
|
||||
* Time: 11:31:11
|
||||
*/
|
||||
|
||||
public class Base64
|
||||
{
|
||||
private static final char[] CA = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
|
||||
private static final int[] IA = new int[256];
|
||||
static {
|
||||
Arrays.fill(IA, -1);
|
||||
for (int i = 0, iS = CA.length; i < iS; i++)
|
||||
IA[CA[i]] = i;
|
||||
IA['='] = 0;
|
||||
}
|
||||
|
||||
// ****************************************************************************************
|
||||
// * char[] version
|
||||
// ****************************************************************************************
|
||||
|
||||
/** Encodes a raw byte array into a BASE64 <code>char[]</code> representation i accordance with RFC 2045.
|
||||
* @param sArr The bytes to convert. If <code>null</code> or length 0 an empty array will be returned.
|
||||
* @param lineSep Optional "\r\n" after 76 characters, unless end of file.<br>
|
||||
* No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a
|
||||
* little faster.
|
||||
* @return A BASE64 encoded array. Never <code>null</code>.
|
||||
*/
|
||||
public final static char[] encodeToChar(byte[] sArr, boolean lineSep)
|
||||
{
|
||||
// Check special case
|
||||
int sLen = sArr != null ? sArr.length : 0;
|
||||
if (sLen == 0)
|
||||
return new char[0];
|
||||
|
||||
int eLen = (sLen / 3) * 3; // Length of even 24-bits.
|
||||
int cCnt = ((sLen - 1) / 3 + 1) << 2; // Returned character count
|
||||
int dLen = cCnt + (lineSep ? (cCnt - 1) / 76 << 1 : 0); // Length of returned array
|
||||
char[] dArr = new char[dLen];
|
||||
|
||||
// Encode even 24-bits
|
||||
for (int s = 0, d = 0, cc = 0; s < eLen;) {
|
||||
// Copy next three bytes into lower 24 bits of int, paying attension to sign.
|
||||
int i = (sArr[s++] & 0xff) << 16 | (sArr[s++] & 0xff) << 8 | (sArr[s++] & 0xff);
|
||||
|
||||
// Encode the int into four chars
|
||||
dArr[d++] = CA[(i >>> 18) & 0x3f];
|
||||
dArr[d++] = CA[(i >>> 12) & 0x3f];
|
||||
dArr[d++] = CA[(i >>> 6) & 0x3f];
|
||||
dArr[d++] = CA[i & 0x3f];
|
||||
|
||||
// Add optional line separator
|
||||
if (lineSep && ++cc == 19 && d < dLen - 2) {
|
||||
dArr[d++] = '\r';
|
||||
dArr[d++] = '\n';
|
||||
cc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Pad and encode last bits if source isn't even 24 bits.
|
||||
int left = sLen - eLen; // 0 - 2.
|
||||
if (left > 0) {
|
||||
// Prepare the int
|
||||
int i = ((sArr[eLen] & 0xff) << 10) | (left == 2 ? ((sArr[sLen - 1] & 0xff) << 2) : 0);
|
||||
|
||||
// Set last four chars
|
||||
dArr[dLen - 4] = CA[i >> 12];
|
||||
dArr[dLen - 3] = CA[(i >>> 6) & 0x3f];
|
||||
dArr[dLen - 2] = left == 2 ? CA[i & 0x3f] : '=';
|
||||
dArr[dLen - 1] = '=';
|
||||
}
|
||||
return dArr;
|
||||
}
|
||||
|
||||
/** Decodes a BASE64 encoded char array. All illegal characters will be ignored and can handle both arrays with
|
||||
* and without line separators.
|
||||
* @param sArr The source array. <code>null</code> or length 0 will return an empty array.
|
||||
* @return The decoded array of bytes. May be of length 0. Will be <code>null</code> if the legal characters
|
||||
* (including '=') isn't divideable by 4. (I.e. definitely corrupted).
|
||||
*/
|
||||
public final static byte[] decode(char[] sArr)
|
||||
{
|
||||
// Check special case
|
||||
int sLen = sArr != null ? sArr.length : 0;
|
||||
if (sLen == 0)
|
||||
return new byte[0];
|
||||
|
||||
// Count illegal characters (including '\r', '\n') to know what size the returned array will be,
|
||||
// so we don't have to reallocate & copy it later.
|
||||
int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...)
|
||||
for (int i = 0; i < sLen; i++) // If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out.
|
||||
if (IA[sArr[i]] < 0)
|
||||
sepCnt++;
|
||||
|
||||
// Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045.
|
||||
if ((sLen - sepCnt) % 4 != 0)
|
||||
return null;
|
||||
|
||||
int pad = 0;
|
||||
for (int i = sLen; i > 1 && IA[sArr[--i]] <= 0;)
|
||||
if (sArr[i] == '=')
|
||||
pad++;
|
||||
|
||||
int len = ((sLen - sepCnt) * 6 >> 3) - pad;
|
||||
|
||||
byte[] dArr = new byte[len]; // Preallocate byte[] of exact length
|
||||
|
||||
for (int s = 0, d = 0; d < len;) {
|
||||
// Assemble three bytes into an int from four "valid" characters.
|
||||
int i = 0;
|
||||
for (int j = 0; j < 4; j++) { // j only increased if a valid char was found.
|
||||
int c = IA[sArr[s++]];
|
||||
if (c >= 0)
|
||||
i |= c << (18 - j * 6);
|
||||
else
|
||||
j--;
|
||||
}
|
||||
// Add the bytes
|
||||
dArr[d++] = (byte) (i >> 16);
|
||||
if (d < len) {
|
||||
dArr[d++]= (byte) (i >> 8);
|
||||
if (d < len)
|
||||
dArr[d++] = (byte) i;
|
||||
}
|
||||
}
|
||||
return dArr;
|
||||
}
|
||||
|
||||
/** Decodes a BASE64 encoded char array that is known to be resonably well formatted. The method is about twice as
|
||||
* fast as {@link #decode(char[])}. The preconditions are:<br>
|
||||
* + The array must have a line length of 76 chars OR no line separators at all (one line).<br>
|
||||
* + Line separator must be "\r\n", as specified in RFC 2045
|
||||
* + The array must not contain illegal characters within the encoded string<br>
|
||||
* + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.<br>
|
||||
* @param sArr The source array. Length 0 will return an empty array. <code>null</code> will throw an exception.
|
||||
* @return The decoded array of bytes. May be of length 0.
|
||||
*/
|
||||
public final static byte[] decodeFast(char[] sArr)
|
||||
{
|
||||
// Check special case
|
||||
int sLen = sArr.length;
|
||||
if (sLen == 0)
|
||||
return new byte[0];
|
||||
|
||||
int sIx = 0, eIx = sLen - 1; // Start and end index after trimming.
|
||||
|
||||
// Trim illegal chars from start
|
||||
while (sIx < eIx && IA[sArr[sIx]] < 0)
|
||||
sIx++;
|
||||
|
||||
// Trim illegal chars from end
|
||||
while (eIx > 0 && IA[sArr[eIx]] < 0)
|
||||
eIx--;
|
||||
|
||||
// get the padding count (=) (0, 1 or 2)
|
||||
int pad = sArr[eIx] == '=' ? (sArr[eIx - 1] == '=' ? 2 : 1) : 0; // Count '=' at end.
|
||||
int cCnt = eIx - sIx + 1; // Content count including possible separators
|
||||
int sepCnt = sLen > 76 ? (sArr[76] == '\r' ? cCnt / 78 : 0) << 1 : 0;
|
||||
|
||||
int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes
|
||||
byte[] dArr = new byte[len]; // Preallocate byte[] of exact length
|
||||
|
||||
// Decode all but the last 0 - 2 bytes.
|
||||
int d = 0;
|
||||
for (int cc = 0, eLen = (len / 3) * 3; d < eLen;) {
|
||||
// Assemble three bytes into an int from four "valid" characters.
|
||||
int i = IA[sArr[sIx++]] << 18 | IA[sArr[sIx++]] << 12 | IA[sArr[sIx++]] << 6 | IA[sArr[sIx++]];
|
||||
|
||||
// Add the bytes
|
||||
dArr[d++] = (byte) (i >> 16);
|
||||
dArr[d++] = (byte) (i >> 8);
|
||||
dArr[d++] = (byte) i;
|
||||
|
||||
// If line separator, jump over it.
|
||||
if (sepCnt > 0 && ++cc == 19) {
|
||||
sIx += 2;
|
||||
cc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (d < len) {
|
||||
// Decode last 1-3 bytes (incl '=') into 1-3 bytes
|
||||
int i = 0;
|
||||
for (int j = 0; sIx <= eIx - pad; j++)
|
||||
i |= IA[sArr[sIx++]] << (18 - j * 6);
|
||||
|
||||
for (int r = 16; d < len; r -= 8)
|
||||
dArr[d++] = (byte) (i >> r);
|
||||
}
|
||||
|
||||
return dArr;
|
||||
}
|
||||
|
||||
// ****************************************************************************************
|
||||
// * byte[] version
|
||||
// ****************************************************************************************
|
||||
|
||||
/** Encodes a raw byte array into a BASE64 <code>byte[]</code> representation i accordance with RFC 2045.
|
||||
* @param sArr The bytes to convert. If <code>null</code> or length 0 an empty array will be returned.
|
||||
* @param lineSep Optional "\r\n" after 76 characters, unless end of file.<br>
|
||||
* No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a
|
||||
* little faster.
|
||||
* @return A BASE64 encoded array. Never <code>null</code>.
|
||||
*/
|
||||
public final static byte[] encodeToByte(byte[] sArr, boolean lineSep)
|
||||
{
|
||||
// Check special case
|
||||
int sLen = sArr != null ? sArr.length : 0;
|
||||
if (sLen == 0)
|
||||
return new byte[0];
|
||||
|
||||
int eLen = (sLen / 3) * 3; // Length of even 24-bits.
|
||||
int cCnt = ((sLen - 1) / 3 + 1) << 2; // Returned character count
|
||||
int dLen = cCnt + (lineSep ? (cCnt - 1) / 76 << 1 : 0); // Length of returned array
|
||||
byte[] dArr = new byte[dLen];
|
||||
|
||||
// Encode even 24-bits
|
||||
for (int s = 0, d = 0, cc = 0; s < eLen;) {
|
||||
// Copy next three bytes into lower 24 bits of int, paying attension to sign.
|
||||
int i = (sArr[s++] & 0xff) << 16 | (sArr[s++] & 0xff) << 8 | (sArr[s++] & 0xff);
|
||||
|
||||
// Encode the int into four chars
|
||||
dArr[d++] = (byte) CA[(i >>> 18) & 0x3f];
|
||||
dArr[d++] = (byte) CA[(i >>> 12) & 0x3f];
|
||||
dArr[d++] = (byte) CA[(i >>> 6) & 0x3f];
|
||||
dArr[d++] = (byte) CA[i & 0x3f];
|
||||
|
||||
// Add optional line separator
|
||||
if (lineSep && ++cc == 19 && d < dLen - 2) {
|
||||
dArr[d++] = '\r';
|
||||
dArr[d++] = '\n';
|
||||
cc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Pad and encode last bits if source isn't an even 24 bits.
|
||||
int left = sLen - eLen; // 0 - 2.
|
||||
if (left > 0) {
|
||||
// Prepare the int
|
||||
int i = ((sArr[eLen] & 0xff) << 10) | (left == 2 ? ((sArr[sLen - 1] & 0xff) << 2) : 0);
|
||||
|
||||
// Set last four chars
|
||||
dArr[dLen - 4] = (byte) CA[i >> 12];
|
||||
dArr[dLen - 3] = (byte) CA[(i >>> 6) & 0x3f];
|
||||
dArr[dLen - 2] = left == 2 ? (byte) CA[i & 0x3f] : (byte) '=';
|
||||
dArr[dLen - 1] = '=';
|
||||
}
|
||||
return dArr;
|
||||
}
|
||||
|
||||
/** Decodes a BASE64 encoded byte array. All illegal characters will be ignored and can handle both arrays with
|
||||
* and without line separators.
|
||||
* @param sArr The source array. Length 0 will return an empty array. <code>null</code> will throw an exception.
|
||||
* @return The decoded array of bytes. May be of length 0. Will be <code>null</code> if the legal characters
|
||||
* (including '=') isn't divideable by 4. (I.e. definitely corrupted).
|
||||
*/
|
||||
public final static byte[] decode(byte[] sArr)
|
||||
{
|
||||
// Check special case
|
||||
int sLen = sArr.length;
|
||||
|
||||
// Count illegal characters (including '\r', '\n') to know what size the returned array will be,
|
||||
// so we don't have to reallocate & copy it later.
|
||||
int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...)
|
||||
for (int i = 0; i < sLen; i++) // If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out.
|
||||
if (IA[sArr[i] & 0xff] < 0)
|
||||
sepCnt++;
|
||||
|
||||
// Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045.
|
||||
if ((sLen - sepCnt) % 4 != 0)
|
||||
return null;
|
||||
|
||||
int pad = 0;
|
||||
for (int i = sLen; i > 1 && IA[sArr[--i] & 0xff] <= 0;)
|
||||
if (sArr[i] == '=')
|
||||
pad++;
|
||||
|
||||
int len = ((sLen - sepCnt) * 6 >> 3) - pad;
|
||||
|
||||
byte[] dArr = new byte[len]; // Preallocate byte[] of exact length
|
||||
|
||||
for (int s = 0, d = 0; d < len;) {
|
||||
// Assemble three bytes into an int from four "valid" characters.
|
||||
int i = 0;
|
||||
for (int j = 0; j < 4; j++) { // j only increased if a valid char was found.
|
||||
int c = IA[sArr[s++] & 0xff];
|
||||
if (c >= 0)
|
||||
i |= c << (18 - j * 6);
|
||||
else
|
||||
j--;
|
||||
}
|
||||
|
||||
// Add the bytes
|
||||
dArr[d++] = (byte) (i >> 16);
|
||||
if (d < len) {
|
||||
dArr[d++]= (byte) (i >> 8);
|
||||
if (d < len)
|
||||
dArr[d++] = (byte) i;
|
||||
}
|
||||
}
|
||||
|
||||
return dArr;
|
||||
}
|
||||
|
||||
|
||||
/** Decodes a BASE64 encoded byte array that is known to be resonably well formatted. The method is about twice as
|
||||
* fast as {@link #decode(byte[])}. The preconditions are:<br>
|
||||
* + The array must have a line length of 76 chars OR no line separators at all (one line).<br>
|
||||
* + Line separator must be "\r\n", as specified in RFC 2045
|
||||
* + The array must not contain illegal characters within the encoded string<br>
|
||||
* + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.<br>
|
||||
* @param sArr The source array. Length 0 will return an empty array. <code>null</code> will throw an exception.
|
||||
* @return The decoded array of bytes. May be of length 0.
|
||||
*/
|
||||
public final static byte[] decodeFast(byte[] sArr)
|
||||
{
|
||||
// Check special case
|
||||
int sLen = sArr.length;
|
||||
if (sLen == 0)
|
||||
return new byte[0];
|
||||
|
||||
int sIx = 0, eIx = sLen - 1; // Start and end index after trimming.
|
||||
|
||||
// Trim illegal chars from start
|
||||
while (sIx < eIx && IA[sArr[sIx] & 0xff] < 0)
|
||||
sIx++;
|
||||
|
||||
// Trim illegal chars from end
|
||||
while (eIx > 0 && IA[sArr[eIx] & 0xff] < 0)
|
||||
eIx--;
|
||||
|
||||
// get the padding count (=) (0, 1 or 2)
|
||||
int pad = sArr[eIx] == '=' ? (sArr[eIx - 1] == '=' ? 2 : 1) : 0; // Count '=' at end.
|
||||
int cCnt = eIx - sIx + 1; // Content count including possible separators
|
||||
int sepCnt = sLen > 76 ? (sArr[76] == '\r' ? cCnt / 78 : 0) << 1 : 0;
|
||||
|
||||
int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes
|
||||
byte[] dArr = new byte[len]; // Preallocate byte[] of exact length
|
||||
|
||||
// Decode all but the last 0 - 2 bytes.
|
||||
int d = 0;
|
||||
for (int cc = 0, eLen = (len / 3) * 3; d < eLen;) {
|
||||
// Assemble three bytes into an int from four "valid" characters.
|
||||
int i = IA[sArr[sIx++]] << 18 | IA[sArr[sIx++]] << 12 | IA[sArr[sIx++]] << 6 | IA[sArr[sIx++]];
|
||||
|
||||
// Add the bytes
|
||||
dArr[d++] = (byte) (i >> 16);
|
||||
dArr[d++] = (byte) (i >> 8);
|
||||
dArr[d++] = (byte) i;
|
||||
|
||||
// If line separator, jump over it.
|
||||
if (sepCnt > 0 && ++cc == 19) {
|
||||
sIx += 2;
|
||||
cc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (d < len) {
|
||||
// Decode last 1-3 bytes (incl '=') into 1-3 bytes
|
||||
int i = 0;
|
||||
for (int j = 0; sIx <= eIx - pad; j++)
|
||||
i |= IA[sArr[sIx++]] << (18 - j * 6);
|
||||
|
||||
for (int r = 16; d < len; r -= 8)
|
||||
dArr[d++] = (byte) (i >> r);
|
||||
}
|
||||
|
||||
return dArr;
|
||||
}
|
||||
|
||||
// ****************************************************************************************
|
||||
// * String version
|
||||
// ****************************************************************************************
|
||||
|
||||
/** Encodes a raw byte array into a BASE64 <code>String</code> representation i accordance with RFC 2045.
|
||||
* @param sArr The bytes to convert. If <code>null</code> or length 0 an empty array will be returned.
|
||||
* @param lineSep Optional "\r\n" after 76 characters, unless end of file.<br>
|
||||
* No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a
|
||||
* little faster.
|
||||
* @return A BASE64 encoded array. Never <code>null</code>.
|
||||
*/
|
||||
public final static String encodeToString(byte[] sArr, boolean lineSep)
|
||||
{
|
||||
// Reuse char[] since we can't create a String incrementally anyway and StringBuffer/Builder would be slower.
|
||||
return new String(encodeToChar(sArr, lineSep));
|
||||
}
|
||||
|
||||
/** Decodes a BASE64 encoded <code>String</code>. All illegal characters will be ignored and can handle both strings with
|
||||
* and without line separators.<br>
|
||||
* <b>Note!</b> It can be up to about 2x the speed to call <code>decode(str.toCharArray())</code> instead. That
|
||||
* will create a temporary array though. This version will use <code>str.charAt(i)</code> to iterate the string.
|
||||
* @param str The source string. <code>null</code> or length 0 will return an empty array.
|
||||
* @return The decoded array of bytes. May be of length 0. Will be <code>null</code> if the legal characters
|
||||
* (including '=') isn't divideable by 4. (I.e. definitely corrupted).
|
||||
*/
|
||||
public final static byte[] decode(String str)
|
||||
{
|
||||
// Check special case
|
||||
int sLen = str != null ? str.length() : 0;
|
||||
if (sLen == 0)
|
||||
return new byte[0];
|
||||
|
||||
// Count illegal characters (including '\r', '\n') to know what size the returned array will be,
|
||||
// so we don't have to reallocate & copy it later.
|
||||
int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...)
|
||||
for (int i = 0; i < sLen; i++) // If input is "pure" (I.e. no line separators or illegal chars) base64 this loop can be commented out.
|
||||
if (IA[str.charAt(i)] < 0)
|
||||
sepCnt++;
|
||||
|
||||
// Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045.
|
||||
if ((sLen - sepCnt) % 4 != 0)
|
||||
return null;
|
||||
|
||||
// Count '=' at end
|
||||
int pad = 0;
|
||||
for (int i = sLen; i > 1 && IA[str.charAt(--i)] <= 0;)
|
||||
if (str.charAt(i) == '=')
|
||||
pad++;
|
||||
|
||||
int len = ((sLen - sepCnt) * 6 >> 3) - pad;
|
||||
|
||||
byte[] dArr = new byte[len]; // Preallocate byte[] of exact length
|
||||
|
||||
for (int s = 0, d = 0; d < len;) {
|
||||
// Assemble three bytes into an int from four "valid" characters.
|
||||
int i = 0;
|
||||
for (int j = 0; j < 4; j++) { // j only increased if a valid char was found.
|
||||
int c = IA[str.charAt(s++)];
|
||||
if (c >= 0)
|
||||
i |= c << (18 - j * 6);
|
||||
else
|
||||
j--;
|
||||
}
|
||||
// Add the bytes
|
||||
dArr[d++] = (byte) (i >> 16);
|
||||
if (d < len) {
|
||||
dArr[d++]= (byte) (i >> 8);
|
||||
if (d < len)
|
||||
dArr[d++] = (byte) i;
|
||||
}
|
||||
}
|
||||
return dArr;
|
||||
}
|
||||
|
||||
/** Decodes a BASE64 encoded string that is known to be resonably well formatted. The method is about twice as
|
||||
* fast as {@link #decode(String)}. The preconditions are:<br>
|
||||
* + The array must have a line length of 76 chars OR no line separators at all (one line).<br>
|
||||
* + Line separator must be "\r\n", as specified in RFC 2045
|
||||
* + The array must not contain illegal characters within the encoded string<br>
|
||||
* + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.<br>
|
||||
* @param s The source string. Length 0 will return an empty array. <code>null</code> will throw an exception.
|
||||
* @return The decoded array of bytes. May be of length 0.
|
||||
*/
|
||||
public final static byte[] decodeFast(String s)
|
||||
{
|
||||
// Check special case
|
||||
int sLen = s.length();
|
||||
if (sLen == 0)
|
||||
return new byte[0];
|
||||
|
||||
int sIx = 0, eIx = sLen - 1; // Start and end index after trimming.
|
||||
|
||||
// Trim illegal chars from start
|
||||
while (sIx < eIx && IA[s.charAt(sIx) & 0xff] < 0)
|
||||
sIx++;
|
||||
|
||||
// Trim illegal chars from end
|
||||
while (eIx > 0 && IA[s.charAt(eIx) & 0xff] < 0)
|
||||
eIx--;
|
||||
|
||||
// get the padding count (=) (0, 1 or 2)
|
||||
int pad = s.charAt(eIx) == '=' ? (s.charAt(eIx - 1) == '=' ? 2 : 1) : 0; // Count '=' at end.
|
||||
int cCnt = eIx - sIx + 1; // Content count including possible separators
|
||||
int sepCnt = sLen > 76 ? (s.charAt(76) == '\r' ? cCnt / 78 : 0) << 1 : 0;
|
||||
|
||||
int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes
|
||||
byte[] dArr = new byte[len]; // Preallocate byte[] of exact length
|
||||
|
||||
// Decode all but the last 0 - 2 bytes.
|
||||
int d = 0;
|
||||
for (int cc = 0, eLen = (len / 3) * 3; d < eLen;) {
|
||||
// Assemble three bytes into an int from four "valid" characters.
|
||||
int i = IA[s.charAt(sIx++)] << 18 | IA[s.charAt(sIx++)] << 12 | IA[s.charAt(sIx++)] << 6 | IA[s.charAt(sIx++)];
|
||||
|
||||
// Add the bytes
|
||||
dArr[d++] = (byte) (i >> 16);
|
||||
dArr[d++] = (byte) (i >> 8);
|
||||
dArr[d++] = (byte) i;
|
||||
|
||||
// If line separator, jump over it.
|
||||
if (sepCnt > 0 && ++cc == 19) {
|
||||
sIx += 2;
|
||||
cc = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (d < len) {
|
||||
// Decode last 1-3 bytes (incl '=') into 1-3 bytes
|
||||
int i = 0;
|
||||
for (int j = 0; sIx <= eIx - pad; j++)
|
||||
i |= IA[s.charAt(sIx++)] << (18 - j * 6);
|
||||
|
||||
for (int r = 16; d < len; r -= 8)
|
||||
dArr[d++] = (byte) (i >> r);
|
||||
}
|
||||
|
||||
return dArr;
|
||||
}
|
||||
}
|
||||
117
hacked-libtiled/tiled/util/BasicTileCutter.java
Normal file
117
hacked-libtiled/tiled/util/BasicTileCutter.java
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright 2004-2006, Thorbjørn Lindeijer <thorbjorn@lindeijer.nl>
|
||||
* Copyright 2004-2006, Adam Turk <aturk@biggeruniverse.com>
|
||||
*
|
||||
* This file is part of libtiled-java.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package tiled.util;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Image;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
/**
|
||||
* Cuts tiles from a tileset image according to a regular rectangular pattern.
|
||||
* Supports a variable spacing between tiles and a margin around them.
|
||||
*/
|
||||
public class BasicTileCutter implements TileCutter
|
||||
{
|
||||
private int nextX, nextY;
|
||||
private BufferedImage image;
|
||||
private final int tileWidth;
|
||||
private final int tileHeight;
|
||||
private final int tileSpacing;
|
||||
private final int tileMargin;
|
||||
|
||||
public BasicTileCutter(int tileWidth, int tileHeight, int tileSpacing,
|
||||
int tileMargin)
|
||||
{
|
||||
this.tileWidth = tileWidth;
|
||||
this.tileHeight = tileHeight;
|
||||
this.tileSpacing = tileSpacing;
|
||||
this.tileMargin = tileMargin;
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return "Basic";
|
||||
}
|
||||
|
||||
public void setImage(BufferedImage image) {
|
||||
this.image = image;
|
||||
}
|
||||
|
||||
public Image getNextTile() {
|
||||
if (nextY + tileHeight + tileMargin <= image.getHeight()) {
|
||||
BufferedImage tile =
|
||||
image.getSubimage(nextX, nextY, tileWidth, tileHeight);
|
||||
nextX += tileWidth + tileSpacing;
|
||||
|
||||
if (nextX + tileWidth + tileMargin > image.getWidth()) {
|
||||
nextX = tileMargin;
|
||||
nextY += tileHeight + tileSpacing;
|
||||
}
|
||||
|
||||
return tile;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
nextX = tileMargin;
|
||||
nextY = tileMargin;
|
||||
}
|
||||
|
||||
public Dimension getTileDimensions() {
|
||||
return new Dimension(tileWidth, tileHeight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the spacing between tile images.
|
||||
* @return the spacing between tile images.
|
||||
*/
|
||||
public int getTileSpacing() {
|
||||
return tileSpacing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the margin around the tile images.
|
||||
* @return the margin around the tile images.
|
||||
*/
|
||||
public int getTileMargin() {
|
||||
return tileMargin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of tiles per row in the tileset image.
|
||||
* @return the number of tiles per row in the tileset image.
|
||||
*/
|
||||
public int getTilesPerRow() {
|
||||
return (image.getWidth() - 2 * tileMargin + tileSpacing) /
|
||||
(tileWidth + tileSpacing);
|
||||
}
|
||||
}
|
||||
79
hacked-libtiled/tiled/util/ImageHelper.java
Normal file
79
hacked-libtiled/tiled/util/ImageHelper.java
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright 2004-2008, Thorbjørn Lindeijer <thorbjorn@lindeijer.nl>
|
||||
* Copyright 2004-2008, Adam Turk <aturk@biggeruniverse.com>
|
||||
*
|
||||
* This file is part of libtiled-java.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package tiled.util;
|
||||
|
||||
import java.awt.Image;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import javax.imageio.ImageIO;
|
||||
|
||||
/**
|
||||
* This class provides functions to help out with saving/loading images.
|
||||
*/
|
||||
public class ImageHelper
|
||||
{
|
||||
/**
|
||||
* Converts an image to a PNG stored in a byte array.
|
||||
*
|
||||
* @param image
|
||||
* @return a byte array with the PNG data
|
||||
*/
|
||||
static public byte[] imageToPNG(Image image) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
|
||||
try {
|
||||
BufferedImage buffer = new BufferedImage(
|
||||
image.getWidth(null), image.getHeight(null),
|
||||
BufferedImage.TYPE_INT_ARGB);
|
||||
|
||||
buffer.createGraphics().drawImage(image, 0, 0, null);
|
||||
ImageIO.write(buffer, "PNG", baos);
|
||||
baos.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return baos.toByteArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a byte array into an image. The byte array must include the
|
||||
* image header, so that a decision about format can be made.
|
||||
*
|
||||
* @param imageData The byte array of the data to convert.
|
||||
* @return Image The image instance created from the byte array
|
||||
* @throws IOException
|
||||
* @see java.awt.Toolkit#createImage(byte[] imagedata)
|
||||
*/
|
||||
static public BufferedImage bytesToImage(byte[] imageData) throws IOException {
|
||||
return ImageIO.read(new ByteArrayInputStream(imageData));
|
||||
}
|
||||
}
|
||||
70
hacked-libtiled/tiled/util/TileCutter.java
Normal file
70
hacked-libtiled/tiled/util/TileCutter.java
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2004-2006, Thorbjørn Lindeijer <thorbjorn@lindeijer.nl>
|
||||
* Copyright 2004-2006, Adam Turk <aturk@biggeruniverse.com>
|
||||
*
|
||||
* This file is part of libtiled-java.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package tiled.util;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Image;
|
||||
import java.awt.image.BufferedImage;
|
||||
|
||||
/**
|
||||
* A generic interface to a class that implements tile cutting behavior.
|
||||
*/
|
||||
public interface TileCutter
|
||||
{
|
||||
/**
|
||||
* Sets the image that this cutter should cut in tile images.
|
||||
* @param image the image that this cutter should cut
|
||||
*/
|
||||
public void setImage(BufferedImage image);
|
||||
|
||||
/**
|
||||
* Retrieves the next tile image.
|
||||
* @return the next tile image, or <code>null</code> when no more tile
|
||||
* images are available
|
||||
*/
|
||||
public Image getNextTile();
|
||||
|
||||
/**
|
||||
* Resets the tile cutter so that the next call to <code>getNextTile</code>
|
||||
* will return the first tile.
|
||||
*/
|
||||
void reset();
|
||||
|
||||
/**
|
||||
* Returns the default tile dimensions of tiles cut by this cutter.
|
||||
* @return the default tile dimensions of tiles cut by this cutter.
|
||||
*/
|
||||
public Dimension getTileDimensions();
|
||||
|
||||
/**
|
||||
* Returns the name of this tile cutter.
|
||||
* @return the name of this tile cutter
|
||||
*/
|
||||
public String getName();
|
||||
}
|
||||
62
hacked-libtiled/tiled/util/TransparentImageFilter.java
Normal file
62
hacked-libtiled/tiled/util/TransparentImageFilter.java
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2004-2006, Thorbjørn Lindeijer <thorbjorn@lindeijer.nl>
|
||||
* Copyright 2004-2006, Adam Turk <aturk@biggeruniverse.com>
|
||||
*
|
||||
* This file is part of libtiled-java.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package tiled.util;
|
||||
|
||||
import java.awt.image.RGBImageFilter;
|
||||
|
||||
/**
|
||||
* This filter is used for filtering out a given "transparent" color from an
|
||||
* image. Sometimes known as magic pink.
|
||||
*/
|
||||
public class TransparentImageFilter extends RGBImageFilter
|
||||
{
|
||||
int trans;
|
||||
|
||||
/**
|
||||
* @param col the color to make transparent
|
||||
*/
|
||||
public TransparentImageFilter(int col) {
|
||||
trans = col;
|
||||
|
||||
// The filter doesn't depend on pixel location
|
||||
canFilterIndexColorModel = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the given pixel. It returns a transparent pixel for pixels that
|
||||
* match the transparency color, or the existing pixel for anything else.
|
||||
*/
|
||||
public int filterRGB(int x, int y, int rgb) {
|
||||
if (rgb == trans) {
|
||||
return 0;
|
||||
} else {
|
||||
return rgb;
|
||||
}
|
||||
}
|
||||
}
|
||||
54
hacked-libtiled/tiled/view/MapRenderer.java
Normal file
54
hacked-libtiled/tiled/view/MapRenderer.java
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright 2010, Thorbjørn Lindeijer <thorbjorn@lindeijer.nl>
|
||||
*
|
||||
* This file is part of libtiled-java.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package tiled.view;
|
||||
|
||||
import tiled.core.TileLayer;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* An interface defining methods to render a map.
|
||||
*/
|
||||
public interface MapRenderer
|
||||
{
|
||||
/**
|
||||
* Calculates the dimensions of the map this renderer applies to.
|
||||
*
|
||||
* @return the dimensions of the given map in pixels
|
||||
*/
|
||||
public Dimension getMapSize();
|
||||
|
||||
/**
|
||||
* Paints the given tile layer, taking into account the clip rect of the
|
||||
* given graphics context.
|
||||
*
|
||||
* @param g the graphics context to paint to
|
||||
* @param layer the layer to paint
|
||||
*/
|
||||
public void paintTileLayer(Graphics2D g, TileLayer layer);
|
||||
}
|
||||
93
hacked-libtiled/tiled/view/OrthogonalRenderer.java
Normal file
93
hacked-libtiled/tiled/view/OrthogonalRenderer.java
Normal file
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2010, Thorbjørn Lindeijer <thorbjorn@lindeijer.nl>
|
||||
*
|
||||
* This file is part of libtiled-java.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR
|
||||
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package tiled.view;
|
||||
|
||||
import tiled.core.Map;
|
||||
import tiled.core.Tile;
|
||||
import tiled.core.TileLayer;
|
||||
|
||||
import java.awt.*;
|
||||
|
||||
/**
|
||||
* The orthogonal map renderer. This is the most basic map renderer, dealing
|
||||
* with maps that use rectangular tiles.
|
||||
*/
|
||||
public class OrthogonalRenderer implements MapRenderer
|
||||
{
|
||||
private final Map map;
|
||||
|
||||
public OrthogonalRenderer(Map map) {
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
public Dimension getMapSize() {
|
||||
return new Dimension(
|
||||
map.getWidth() * map.getTileWidth(),
|
||||
map.getHeight() * map.getTileHeight());
|
||||
}
|
||||
|
||||
public void paintTileLayer(Graphics2D g, TileLayer layer) {
|
||||
final Rectangle clip = g.getClipBounds();
|
||||
final int tileWidth = map.getTileWidth();
|
||||
final int tileHeight = map.getTileHeight();
|
||||
final Rectangle bounds = layer.getBounds();
|
||||
|
||||
g.translate(bounds.x * tileWidth, bounds.y * tileHeight);
|
||||
clip.translate(-bounds.x * tileWidth, -bounds.y * tileHeight);
|
||||
|
||||
clip.height += map.getTileHeightMax();
|
||||
|
||||
final int startX = Math.max(0, clip.x / tileWidth);
|
||||
final int startY = Math.max(0, clip.y / tileHeight);
|
||||
final int endX = Math.min(layer.getWidth(),
|
||||
(int) Math.ceil(clip.getMaxX() / tileWidth));
|
||||
final int endY = Math.min(layer.getHeight(),
|
||||
(int) Math.ceil(clip.getMaxY() / tileHeight));
|
||||
|
||||
for (int x = startX; x < endX; ++x) {
|
||||
for (int y = startY; y < endY; ++y) {
|
||||
final Tile tile = layer.getTileAt(x, y);
|
||||
if (tile == null)
|
||||
continue;
|
||||
final Image image = tile.getImage();
|
||||
if (image == null)
|
||||
continue;
|
||||
|
||||
g.drawImage(
|
||||
image,
|
||||
x * tileWidth,
|
||||
(y + 1) * tileHeight - image.getHeight(null),
|
||||
null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
g.translate(-bounds.x * tileWidth, -bounds.y * tileHeight);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user