package org.andengine.util.texturepack;
import java.io.IOException;
import java.io.InputStream;
import javax.microedition.khronos.opengles.GL10;
import org.andengine.opengl.texture.ITexture;
import org.andengine.opengl.texture.PixelFormat;
import org.andengine.opengl.texture.TextureManager;
import org.andengine.opengl.texture.TextureOptions;
import org.andengine.opengl.texture.bitmap.BitmapTexture;
import org.andengine.opengl.texture.bitmap.BitmapTextureFormat;
import org.andengine.opengl.texture.compressed.pvr.PVRCCZTexture;
import org.andengine.opengl.texture.compressed.pvr.PVRGZTexture;
import org.andengine.opengl.texture.compressed.pvr.PVRTexture;
import org.andengine.opengl.texture.compressed.pvr.PVRTexture.PVRTextureFormat;
import org.andengine.opengl.texture.compressed.pvr.pixelbufferstrategy.SmartPVRTexturePixelBufferStrategy;
import org.andengine.util.SAXUtils;
import org.andengine.util.adt.DataConstants;
import org.andengine.util.adt.io.in.IInputStreamOpener;
import org.andengine.util.texturepack.exception.TexturePackParseException;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import android.content.res.AssetManager;
/**
* (c) Zynga 2011
*
* @author Nicolas Gramlich <ngramlich@zynga.com>
* @since 17:19:26 - 29.07.2011
*/
public class TexturePackParser extends DefaultHandler {
// ===========================================================
// Constants
// ===========================================================
private static final String TAG_TEXTURE = "texture";
private static final String TAG_TEXTURE_ATTRIBUTE_VERSION = "version";
private static final String TAG_TEXTURE_ATTRIBUTE_FILE = "file";
// private static final String TAG_TEXTURE_ATTRIBUTE_WIDTH = "width";
// private static final String TAG_TEXTURE_ATTRIBUTE_HEIGHT = "height";
private static final String TAG_TEXTURE_ATTRIBUTE_MINFILTER = "minfilter";
private static final String TAG_TEXTURE_ATTRIBUTE_MINFILTER_VALUE_NEAREST = "nearest";
private static final String TAG_TEXTURE_ATTRIBUTE_MINFILTER_VALUE_LINEAR = "linear";
private static final String TAG_TEXTURE_ATTRIBUTE_MINFILTER_VALUE_LINEAR_MIPMAP_LINEAR = "linear_mipmap_linear";
private static final String TAG_TEXTURE_ATTRIBUTE_MINFILTER_VALUE_LINEAR_MIPMAP_NEAREST = "linear_mipmap_nearest";
private static final String TAG_TEXTURE_ATTRIBUTE_MINFILTER_VALUE_NEAREST_MIPMAP_LINEAR = "nearest_mipmap_linear";
private static final String TAG_TEXTURE_ATTRIBUTE_MINFILTER_VALUE_NEAREST_MIPMAP_NEAREST = "nearest_mipmap_nearest";
private static final String TAG_TEXTURE_ATTRIBUTE_MAGFILTER = "magfilter";
private static final String TAG_TEXTURE_ATTRIBUTE_MAGFILTER_VALUE_NEAREST = "nearest";
private static final String TAG_TEXTURE_ATTRIBUTE_MAGFILTER_VALUE_LINEAR = "linear";
private static final String TAG_TEXTURE_ATTRIBUTE_WRAPT = "wrapt";
private static final String TAG_TEXTURE_ATTRIBUTE_WRAPS = "wraps";
private static final String TAG_TEXTURE_ATTRIBUTE_WRAP_VALUE_CLAMP = "clamp";
private static final String TAG_TEXTURE_ATTRIBUTE_WRAP_VALUE_CLAMP_TO_EDGE = "clamp_to_edge";
private static final String TAG_TEXTURE_ATTRIBUTE_WRAP_VALUE_REPEAT = "repeat";
private static final String TAG_TEXTURE_ATTRIBUTE_PREMULTIPLYALPHA = "premultiplyalpha";
private static final String TAG_TEXTURE_ATTRIBUTE_TYPE = "type";
private static final String TAG_TEXTURE_ATTRIBUTE_TYPE_VALUE_PVRCCZ = "pvrccz";
private static final String TAG_TEXTURE_ATTRIBUTE_TYPE_VALUE_PVRGZ = "pvrgz";
private static final String TAG_TEXTURE_ATTRIBUTE_TYPE_VALUE_PVR = "pvr";
private static final String TAG_TEXTURE_ATTRIBUTE_TYPE_VALUE_BITMAP = "bitmap";
private static final String TAG_TEXTURE_ATTRIBUTE_PIXELFORMAT = "pixelformat";
private static final String TAG_TEXTUREREGION = "textureregion";
private static final String TAG_TEXTUREREGION_ATTRIBUTE_ID = "id";
private static final String TAG_TEXTUREREGION_ATTRIBUTE_X = "x";
private static final String TAG_TEXTUREREGION_ATTRIBUTE_Y = "y";
private static final String TAG_TEXTUREREGION_ATTRIBUTE_WIDTH = "width";
private static final String TAG_TEXTUREREGION_ATTRIBUTE_HEIGHT = "height";
private static final String TAG_TEXTUREREGION_ATTRIBUTE_ROTATED = "rotated";
private static final String TAG_TEXTUREREGION_ATTRIBUTE_TRIMMED = "trimmed";
private static final String TAG_TEXTUREREGION_ATTRIBUTE_SOURCE = "src";
private static final String TAG_TEXTUREREGION_ATTRIBUTE_SOURCE_X = "srcx";
private static final String TAG_TEXTUREREGION_ATTRIBUTE_SOURCE_Y = "srcy";
private static final String TAG_TEXTUREREGION_ATTRIBUTE_SOURCE_WIDTH = "srcwidth";
private static final String TAG_TEXTUREREGION_ATTRIBUTE_SOURCE_HEIGHT = "srcheight";
// ===========================================================
// Fields
// ===========================================================
private final AssetManager mAssetManager;
private final String mAssetBasePath;
private final TextureManager mTextureManager;
private TexturePack mTexturePack;
private TexturePackTextureRegionLibrary mTextureRegionLibrary;
private ITexture mTexture;
private int mVersion;
// ===========================================================
// Constructors
// ===========================================================
public TexturePackParser(final AssetManager pAssetManager, final String pAssetBasePath, final TextureManager pTextureManager) {
this.mAssetManager = pAssetManager;
this.mAssetBasePath = pAssetBasePath;
this.mTextureManager = pTextureManager;
}
// ===========================================================
// Getter & Setter
// ===========================================================
public TexturePack getTexturePack() {
return this.mTexturePack;
}
// ===========================================================
// Methods for/from SuperClass/Interfaces
// ===========================================================
@Override
public void startElement(final String pUri, final String pLocalName, final String pQualifiedName, final Attributes pAttributes) throws SAXException {
if(pLocalName.equals(TexturePackParser.TAG_TEXTURE)) {
this.mVersion = SAXUtils.getIntAttributeOrThrow(pAttributes, TexturePackParser.TAG_TEXTURE_ATTRIBUTE_VERSION);
this.mTexture = this.parseTexture(pAttributes);
this.mTextureRegionLibrary = new TexturePackTextureRegionLibrary(10);
this.mTexturePack = new TexturePack(this.mTexture, this.mTextureRegionLibrary);
} else if(pLocalName.equals(TexturePackParser.TAG_TEXTUREREGION)) {
final int id = SAXUtils.getIntAttributeOrThrow(pAttributes, TexturePackParser.TAG_TEXTUREREGION_ATTRIBUTE_ID);
final int x = SAXUtils.getIntAttributeOrThrow(pAttributes, TexturePackParser.TAG_TEXTUREREGION_ATTRIBUTE_X);
final int y = SAXUtils.getIntAttributeOrThrow(pAttributes, TexturePackParser.TAG_TEXTUREREGION_ATTRIBUTE_Y);
final int width = SAXUtils.getIntAttributeOrThrow(pAttributes, TexturePackParser.TAG_TEXTUREREGION_ATTRIBUTE_WIDTH);
final int height = SAXUtils.getIntAttributeOrThrow(pAttributes, TexturePackParser.TAG_TEXTUREREGION_ATTRIBUTE_HEIGHT);
final String source = SAXUtils.getAttributeOrThrow(pAttributes, TAG_TEXTUREREGION_ATTRIBUTE_SOURCE);
// TODO Not sure how trimming could be transparently supported...
final boolean trimmed = SAXUtils.getBooleanAttributeOrThrow(pAttributes, TexturePackParser.TAG_TEXTUREREGION_ATTRIBUTE_TRIMMED);
final boolean rotated = SAXUtils.getBooleanAttributeOrThrow(pAttributes, TexturePackParser.TAG_TEXTUREREGION_ATTRIBUTE_ROTATED);
final int sourceX = SAXUtils.getIntAttributeOrThrow(pAttributes, TAG_TEXTUREREGION_ATTRIBUTE_SOURCE_X);
final int sourceY = SAXUtils.getIntAttributeOrThrow(pAttributes, TAG_TEXTUREREGION_ATTRIBUTE_SOURCE_Y);
final int sourceWidth = SAXUtils.getIntAttributeOrThrow(pAttributes, TAG_TEXTUREREGION_ATTRIBUTE_SOURCE_WIDTH);
final int sourceHeight = SAXUtils.getIntAttributeOrThrow(pAttributes, TAG_TEXTUREREGION_ATTRIBUTE_SOURCE_HEIGHT);
this.mTextureRegionLibrary.put(new TexturePackTextureRegion(this.mTexture, x, y, width, height, id, source, rotated, trimmed, sourceX, sourceY, sourceWidth, sourceHeight));
} else {
throw new TexturePackParseException("Unexpected tag: '" + pLocalName + "'.");
}
}
// ===========================================================
// Methods
// ===========================================================
protected InputStream onGetInputStream(final String pFilename) throws IOException {
return this.mAssetManager.open(this.mAssetBasePath + pFilename);
}
private ITexture parseTexture(final Attributes pAttributes) throws TexturePackParseException {
final String file = SAXUtils.getAttributeOrThrow(pAttributes, TexturePackParser.TAG_TEXTURE_ATTRIBUTE_FILE);
if(this.mTextureManager.hasMappedTexture(file)) {
return this.mTextureManager.getMappedTexture(file);
}
final String type = SAXUtils.getAttributeOrThrow(pAttributes, TexturePackParser.TAG_TEXTURE_ATTRIBUTE_TYPE);
final PixelFormat pixelFormat = TexturePackParser.parsePixelFormat(pAttributes);
final TextureOptions textureOptions = this.parseTextureOptions(pAttributes);
final ITexture texture;
if(type.equals(TexturePackParser.TAG_TEXTURE_ATTRIBUTE_TYPE_VALUE_BITMAP)) {
try {
texture = new BitmapTexture(this.mTextureManager, new IInputStreamOpener() {
@Override
public InputStream open() throws IOException {
return TexturePackParser.this.onGetInputStream(file);
}
}, BitmapTextureFormat.fromPixelFormat(pixelFormat), textureOptions);
} catch (final IOException e) {
throw new TexturePackParseException(e);
}
} else if(type.equals(TexturePackParser.TAG_TEXTURE_ATTRIBUTE_TYPE_VALUE_PVR)) {
try {
texture = new PVRTexture(this.mTextureManager, PVRTextureFormat.fromPixelFormat(pixelFormat), new SmartPVRTexturePixelBufferStrategy(DataConstants.BYTES_PER_MEGABYTE / 8), textureOptions) {
@Override
protected InputStream onGetInputStream() throws IOException {
return TexturePackParser.this.onGetInputStream(file);
}
};
} catch (final IOException e) {
throw new TexturePackParseException(e);
}
} else if(type.equals(TexturePackParser.TAG_TEXTURE_ATTRIBUTE_TYPE_VALUE_PVRGZ)) {
try {
texture = new PVRGZTexture(this.mTextureManager, PVRTextureFormat.fromPixelFormat(pixelFormat), new SmartPVRTexturePixelBufferStrategy(DataConstants.BYTES_PER_MEGABYTE / 8), textureOptions) {
@Override
protected InputStream onGetInputStream() throws IOException {
return TexturePackParser.this.onGetInputStream(file);
}
};
} catch (final IOException e) {
throw new TexturePackParseException(e);
}
} else if(type.equals(TexturePackParser.TAG_TEXTURE_ATTRIBUTE_TYPE_VALUE_PVRCCZ)) {
try {
texture = new PVRCCZTexture(this.mTextureManager, PVRTextureFormat.fromPixelFormat(pixelFormat), new SmartPVRTexturePixelBufferStrategy(DataConstants.BYTES_PER_MEGABYTE / 8), textureOptions) {
@Override
protected InputStream onGetInputStream() throws IOException {
return TexturePackParser.this.onGetInputStream(file);
}
};
} catch (final IOException e) {
throw new TexturePackParseException(e);
}
} else {
throw new TexturePackParseException(new IllegalArgumentException("Unsupported pTextureFormat: '" + type + "'."));
}
this.mTextureManager.addMappedTexture(file, texture);
return texture;
}
private static PixelFormat parsePixelFormat(final Attributes pAttributes) {
return PixelFormat.valueOf(SAXUtils.getAttributeOrThrow(pAttributes, TexturePackParser.TAG_TEXTURE_ATTRIBUTE_PIXELFORMAT));
}
private TextureOptions parseTextureOptions(final Attributes pAttributes) {
final int minFilter = TexturePackParser.parseMinFilter(pAttributes);
final int magFilter = TexturePackParser.parseMagFilter(pAttributes);
final int wrapT = this.parseWrapT(pAttributes);
final int wrapS = this.parseWrapS(pAttributes);
final boolean preMultiplyAlpha = TexturePackParser.parsePremultiplyalpha(pAttributes);
return new TextureOptions(minFilter, magFilter, wrapT, wrapS, preMultiplyAlpha);
}
private static int parseMinFilter(final Attributes pAttributes) {
final String minFilter = SAXUtils.getAttributeOrThrow(pAttributes, TexturePackParser.TAG_TEXTURE_ATTRIBUTE_MINFILTER);
if(minFilter.equals(TexturePackParser.TAG_TEXTURE_ATTRIBUTE_MINFILTER_VALUE_NEAREST)) {
return GL10.GL_NEAREST;
} else if(minFilter.equals(TexturePackParser.TAG_TEXTURE_ATTRIBUTE_MINFILTER_VALUE_LINEAR)) {
return GL10.GL_LINEAR;
} else if(minFilter.equals(TexturePackParser.TAG_TEXTURE_ATTRIBUTE_MINFILTER_VALUE_LINEAR_MIPMAP_LINEAR)) {
return GL10.GL_LINEAR_MIPMAP_LINEAR;
} else if(minFilter.equals(TexturePackParser.TAG_TEXTURE_ATTRIBUTE_MINFILTER_VALUE_LINEAR_MIPMAP_NEAREST)) {
return GL10.GL_LINEAR_MIPMAP_NEAREST;
} else if(minFilter.equals(TexturePackParser.TAG_TEXTURE_ATTRIBUTE_MINFILTER_VALUE_NEAREST_MIPMAP_LINEAR)) {
return GL10.GL_NEAREST_MIPMAP_LINEAR;
} else if(minFilter.equals(TexturePackParser.TAG_TEXTURE_ATTRIBUTE_MINFILTER_VALUE_NEAREST_MIPMAP_NEAREST)) {
return GL10.GL_NEAREST_MIPMAP_NEAREST;
} else {
throw new IllegalArgumentException("Unexpected " + TexturePackParser.TAG_TEXTURE_ATTRIBUTE_MINFILTER + " attribute: '" + minFilter + "'.");
}
}
private static int parseMagFilter(final Attributes pAttributes) {
final String magFilter = SAXUtils.getAttributeOrThrow(pAttributes, TexturePackParser.TAG_TEXTURE_ATTRIBUTE_MAGFILTER);
if(magFilter.equals(TexturePackParser.TAG_TEXTURE_ATTRIBUTE_MAGFILTER_VALUE_NEAREST)) {
return GL10.GL_NEAREST;
} else if(magFilter.equals(TexturePackParser.TAG_TEXTURE_ATTRIBUTE_MAGFILTER_VALUE_LINEAR)) {
return GL10.GL_LINEAR;
} else {
throw new IllegalArgumentException("Unexpected " + TexturePackParser.TAG_TEXTURE_ATTRIBUTE_MAGFILTER + " attribute: '" + magFilter + "'.");
}
}
private int parseWrapT(final Attributes pAttributes) {
return this.parseWrap(pAttributes, TexturePackParser.TAG_TEXTURE_ATTRIBUTE_WRAPT);
}
private int parseWrapS(final Attributes pAttributes) {
return this.parseWrap(pAttributes, TexturePackParser.TAG_TEXTURE_ATTRIBUTE_WRAPS);
}
private int parseWrap(final Attributes pAttributes, final String pWrapAttributeName) {
final String wrapAttribute = SAXUtils.getAttributeOrThrow(pAttributes, pWrapAttributeName);
if(this.mVersion == 1 && wrapAttribute.equals(TexturePackParser.TAG_TEXTURE_ATTRIBUTE_WRAP_VALUE_CLAMP)) {
return GL10.GL_CLAMP_TO_EDGE;
} else if(wrapAttribute.equals(TexturePackParser.TAG_TEXTURE_ATTRIBUTE_WRAP_VALUE_CLAMP_TO_EDGE)) {
return GL10.GL_CLAMP_TO_EDGE;
} else if(wrapAttribute.equals(TexturePackParser.TAG_TEXTURE_ATTRIBUTE_WRAP_VALUE_REPEAT)) {
return GL10.GL_REPEAT;
} else {
throw new IllegalArgumentException("Unexpected " + pWrapAttributeName + " attribute: '" + wrapAttribute + "'.");
}
}
private static boolean parsePremultiplyalpha(final Attributes pAttributes) {
return SAXUtils.getBooleanAttributeOrThrow(pAttributes, TexturePackParser.TAG_TEXTURE_ATTRIBUTE_PREMULTIPLYALPHA);
}
// ===========================================================
// Inner and Anonymous Classes
// ===========================================================
}