package org.andengine.entity.text;
import java.util.ArrayList;
import org.andengine.engine.camera.Camera;
import org.andengine.entity.shape.RectangularShape;
import org.andengine.entity.text.exception.OutOfCharactersException;
import org.andengine.entity.text.vbo.HighPerformanceTextVertexBufferObject;
import org.andengine.entity.text.vbo.ITextVertexBufferObject;
import org.andengine.opengl.font.FontUtils;
import org.andengine.opengl.font.IFont;
import org.andengine.opengl.shader.PositionColorTextureCoordinatesShaderProgram;
import org.andengine.opengl.shader.ShaderProgram;
import org.andengine.opengl.shader.constants.ShaderProgramConstants;
import org.andengine.opengl.util.GLState;
import org.andengine.opengl.vbo.DrawType;
import org.andengine.opengl.vbo.VertexBufferObjectManager;
import org.andengine.opengl.vbo.attribute.VertexBufferObjectAttributes;
import org.andengine.opengl.vbo.attribute.VertexBufferObjectAttributesBuilder;
import org.andengine.util.HorizontalAlign;
import org.andengine.util.adt.DataConstants;
import org.andengine.util.adt.list.FloatArrayList;
import org.andengine.util.adt.list.IFloatList;
import android.opengl.GLES20;
/**
* TODO Try Degenerate Triangles?
*
* (c) 2010 Nicolas Gramlich
* (c) 2011 Zynga Inc.
*
* @author Nicolas Gramlich
* @since 10:54:59 - 03.04.2010
*/
public class Text extends RectangularShape {
// ===========================================================
// Constants
// ===========================================================
public static final float LEADING_DEFAULT = 0;
public static final int VERTEX_INDEX_X = 0;
public static final int VERTEX_INDEX_Y = Text.VERTEX_INDEX_X + 1;
public static final int COLOR_INDEX = Text.VERTEX_INDEX_Y + 1;
public static final int TEXTURECOORDINATES_INDEX_U = Text.COLOR_INDEX + 1;
public static final int TEXTURECOORDINATES_INDEX_V = Text.TEXTURECOORDINATES_INDEX_U + 1;
public static final int VERTEX_SIZE = 2 + 1 + 2;
public static final int VERTICES_PER_LETTER = 6;
public static final int LETTER_SIZE = Text.VERTEX_SIZE * Text.VERTICES_PER_LETTER;
public static final int VERTEX_STRIDE = Text.VERTEX_SIZE * DataConstants.BYTES_PER_FLOAT;
public static final VertexBufferObjectAttributes VERTEXBUFFEROBJECTATTRIBUTES_DEFAULT = new VertexBufferObjectAttributesBuilder(3)
.add(ShaderProgramConstants.ATTRIBUTE_POSITION_LOCATION, ShaderProgramConstants.ATTRIBUTE_POSITION, 2, GLES20.GL_FLOAT, false)
.add(ShaderProgramConstants.ATTRIBUTE_COLOR_LOCATION, ShaderProgramConstants.ATTRIBUTE_COLOR, 4, GLES20.GL_UNSIGNED_BYTE, true)
.add(ShaderProgramConstants.ATTRIBUTE_TEXTURECOORDINATES_LOCATION, ShaderProgramConstants.ATTRIBUTE_TEXTURECOORDINATES, 2, GLES20.GL_FLOAT, false)
.build();
// ===========================================================
// Fields
// ===========================================================
protected final IFont mFont;
protected float mLineWidthMaximum;
protected float mLineAlignmentWidth;
protected TextOptions mTextOptions;
protected final int mCharactersMaximum;
protected int mCharactersToDraw;
protected int mVertexCountToDraw;
protected final int mVertexCount;
protected final ITextVertexBufferObject mTextVertexBufferObject;
protected CharSequence mText;
protected ArrayList<CharSequence> mLines = new ArrayList<CharSequence>(1);
protected IFloatList mLineWidths = new FloatArrayList(1);
// ===========================================================
// Constructors
// ===========================================================
public Text(final float pX, final float pY, final IFont pFont, final CharSequence pText, final VertexBufferObjectManager pVertexBufferObjectManager) {
this(pX, pY, pFont, pText, pVertexBufferObjectManager, DrawType.STATIC);
}
public Text(final float pX, final float pY, final IFont pFont, final CharSequence pText, final VertexBufferObjectManager pVertexBufferObjectManager, final ShaderProgram pShaderProgram) {
this(pX, pY, pFont, pText, pVertexBufferObjectManager, DrawType.STATIC, pShaderProgram);
}
public Text(final float pX, final float pY, final IFont pFont, final CharSequence pText, final VertexBufferObjectManager pVertexBufferObjectManager, final DrawType pDrawType) {
this(pX, pY, pFont, pText, new TextOptions(), pVertexBufferObjectManager, pDrawType);
}
public Text(final float pX, final float pY, final IFont pFont, final CharSequence pText, final VertexBufferObjectManager pVertexBufferObjectManager, final DrawType pDrawType, final ShaderProgram pShaderProgram) {
this(pX, pY, pFont, pText, new TextOptions(), pVertexBufferObjectManager, pDrawType, pShaderProgram);
}
public Text(final float pX, final float pY, final IFont pFont, final CharSequence pText, final TextOptions pTextOptions, final VertexBufferObjectManager pVertexBufferObjectManager) {
this(pX, pY, pFont, pText, pTextOptions, pVertexBufferObjectManager, DrawType.STATIC);
}
public Text(final float pX, final float pY, final IFont pFont, final CharSequence pText, final TextOptions pTextOptions, final VertexBufferObjectManager pVertexBufferObjectManager, final ShaderProgram pShaderProgram) {
this(pX, pY, pFont, pText, pTextOptions, pVertexBufferObjectManager, DrawType.STATIC, pShaderProgram);
}
public Text(final float pX, final float pY, final IFont pFont, final CharSequence pText, final TextOptions pTextOptions, final VertexBufferObjectManager pVertexBufferObjectManager, final DrawType pDrawType) {
this(pX, pY, pFont, pText, pText.length(), pTextOptions, pVertexBufferObjectManager, pDrawType);
}
public Text(final float pX, final float pY, final IFont pFont, final CharSequence pText, final TextOptions pTextOptions, final VertexBufferObjectManager pVertexBufferObjectManager, final DrawType pDrawType, final ShaderProgram pShaderProgram) {
this(pX, pY, pFont, pText, pText.length(), pTextOptions, pVertexBufferObjectManager, pDrawType, pShaderProgram);
}
public Text(final float pX, final float pY, final IFont pFont, final CharSequence pText, final int pCharactersMaximum, final VertexBufferObjectManager pVertexBufferObjectManager) {
this(pX, pY, pFont, pText, pCharactersMaximum, pVertexBufferObjectManager, DrawType.STATIC);
}
public Text(final float pX, final float pY, final IFont pFont, final CharSequence pText, final int pCharactersMaximum, final VertexBufferObjectManager pVertexBufferObjectManager, final ShaderProgram pShaderProgram) {
this(pX, pY, pFont, pText, pCharactersMaximum, pVertexBufferObjectManager, DrawType.STATIC, pShaderProgram);
}
public Text(final float pX, final float pY, final IFont pFont, final CharSequence pText, final int pCharactersMaximum, final VertexBufferObjectManager pVertexBufferObjectManager, final DrawType pDrawType) {
this(pX, pY, pFont, pText, pCharactersMaximum, new TextOptions(), pVertexBufferObjectManager, pDrawType);
}
public Text(final float pX, final float pY, final IFont pFont, final CharSequence pText, final int pCharactersMaximum, final VertexBufferObjectManager pVertexBufferObjectManager, final DrawType pDrawType, final ShaderProgram pShaderProgram) {
this(pX, pY, pFont, pText, pCharactersMaximum, new TextOptions(), pVertexBufferObjectManager, pDrawType, pShaderProgram);
}
public Text(final float pX, final float pY, final IFont pFont, final CharSequence pText, final int pCharactersMaximum, final TextOptions pTextOptions, final VertexBufferObjectManager pVertexBufferObjectManager) {
this(pX, pY, pFont, pText, pCharactersMaximum, pTextOptions, pVertexBufferObjectManager, DrawType.STATIC);
}
public Text(final float pX, final float pY, final IFont pFont, final CharSequence pText, final int pCharactersMaximum, final TextOptions pTextOptions, final VertexBufferObjectManager pVertexBufferObjectManager, final DrawType pDrawType) {
this(pX, pY, pFont, pText, pCharactersMaximum, pTextOptions, new HighPerformanceTextVertexBufferObject(pVertexBufferObjectManager, Text.LETTER_SIZE * pCharactersMaximum, pDrawType, true, Text.VERTEXBUFFEROBJECTATTRIBUTES_DEFAULT));
}
public Text(final float pX, final float pY, final IFont pFont, final CharSequence pText, final int pCharactersMaximum, final TextOptions pTextOptions, final VertexBufferObjectManager pVertexBufferObjectManager, final DrawType pDrawType, final ShaderProgram pShaderProgram) {
this(pX, pY, pFont, pText, pCharactersMaximum, pTextOptions, new HighPerformanceTextVertexBufferObject(pVertexBufferObjectManager, Text.LETTER_SIZE * pCharactersMaximum, pDrawType, true, Text.VERTEXBUFFEROBJECTATTRIBUTES_DEFAULT), pShaderProgram);
}
public Text(final float pX, final float pY, final IFont pFont, final CharSequence pText, final int pCharactersMaximum, final TextOptions pTextOptions, final ITextVertexBufferObject pTextVertexBufferObject) {
this(pX, pY, pFont, pText, pCharactersMaximum, pTextOptions, pTextVertexBufferObject, PositionColorTextureCoordinatesShaderProgram.getInstance());
}
public Text(final float pX, final float pY, final IFont pFont, final CharSequence pText, final int pCharactersMaximum, final TextOptions pTextOptions, final ITextVertexBufferObject pTextVertexBufferObject, final ShaderProgram pShaderProgram) {
super(pX, pY, 0, 0, pShaderProgram);
this.mFont = pFont;
this.mTextOptions = pTextOptions;
this.mCharactersMaximum = pCharactersMaximum;
this.mVertexCount = Text.VERTICES_PER_LETTER * this.mCharactersMaximum;
this.mTextVertexBufferObject = pTextVertexBufferObject;
this.onUpdateColor();
this.setText(pText);
this.setBlendingEnabled(true);
this.initBlendFunction(this.mFont.getTexture());
}
// ===========================================================
// Getter & Setter
// ===========================================================
public IFont getFont() {
return this.mFont;
}
public int getCharactersMaximum() {
return this.mCharactersMaximum;
}
public CharSequence getText() {
return this.mText;
}
/**
* @param pText
* @throws OutOfCharactersException leaves this {@link Text} object in an undefined state, until {@link Text#setText(CharSequence)} is called again and no {@link OutOfCharactersException} is thrown.
*/
public void setText(final CharSequence pText) throws OutOfCharactersException {
this.mText = pText;
final IFont font = this.mFont;
this.mLines.clear();
this.mLineWidths.clear();
if(this.mTextOptions.mAutoWrap == AutoWrap.NONE) {
this.mLines = FontUtils.splitLines(this.mText, this.mLines); // TODO Add whitespace-trimming.
} else {
this.mLines = FontUtils.splitLines(this.mFont, this.mText, this.mLines, this.mTextOptions.mAutoWrap, this.mTextOptions.mAutoWrapWidth);
}
final int lineCount = this.mLines.size();
float maximumLineWidth = 0;
for (int i = 0; i < lineCount; i++) {
final float lineWidth = FontUtils.measureText(font, this.mLines.get(i));
maximumLineWidth = Math.max(maximumLineWidth, lineWidth);
this.mLineWidths.add(lineWidth);
}
this.mLineWidthMaximum = maximumLineWidth;
if(this.mTextOptions.mAutoWrap == AutoWrap.NONE) {
this.mLineAlignmentWidth = this.mLineWidthMaximum;
} else {
this.mLineAlignmentWidth = this.mTextOptions.mAutoWrapWidth;
}
super.mWidth = this.mLineAlignmentWidth;
super.mHeight = lineCount * font.getLineHeight() + (lineCount - 1) * this.mTextOptions.mLeading;
this.mRotationCenterX = super.mWidth * 0.5f;
this.mRotationCenterY = super.mHeight * 0.5f;
this.mScaleCenterX = this.mRotationCenterX;
this.mScaleCenterY = this.mRotationCenterY;
this.onUpdateVertices();
}
public ArrayList<CharSequence> getLines() {
return this.mLines;
}
public IFloatList getLineWidths() {
return this.mLineWidths;
}
public float getLineAlignmentWidth() {
return this.mLineAlignmentWidth;
}
public float getLineWidthMaximum() {
return this.mLineWidthMaximum;
}
public float getLeading() {
return this.mTextOptions.mLeading;
}
public void setLeading(final float pLeading) {
this.mTextOptions.mLeading = pLeading;
this.invalidateText();
}
public HorizontalAlign getHorizontalAlign() {
return this.mTextOptions.mHorizontalAlign;
}
public void setHorizontalAlign(final HorizontalAlign pHorizontalAlign) {
this.mTextOptions.mHorizontalAlign = pHorizontalAlign;
this.invalidateText();
}
public AutoWrap getAutoWrap() {
return this.mTextOptions.mAutoWrap;
}
public void setAutoWrap(final AutoWrap pAutoWrap) {
this.mTextOptions.mAutoWrap = pAutoWrap;
this.invalidateText();
}
public float getAutoWrapWidth() {
return this.mTextOptions.mAutoWrapWidth;
}
public void setAutoWrapWidth(final float pAutoWrapWidth) {
this.mTextOptions.mAutoWrapWidth = pAutoWrapWidth;
this.invalidateText();
}
public TextOptions getTextOptions() {
return this.mTextOptions;
}
public void setTextOptions(final TextOptions pTextOptions) {
this.mTextOptions = pTextOptions;
}
public void setCharactersToDraw(final int pCharactersToDraw) {
if(pCharactersToDraw > this.mCharactersMaximum) {
throw new OutOfCharactersException("Characters: maximum: '" + this.mCharactersMaximum + "' required: '" + pCharactersToDraw + "'.");
}
this.mCharactersToDraw = pCharactersToDraw;
this.mVertexCountToDraw = pCharactersToDraw * Text.VERTICES_PER_LETTER;
}
// ===========================================================
// Methods for/from SuperClass/Interfaces
// ===========================================================
@Override
public ITextVertexBufferObject getVertexBufferObject() {
return this.mTextVertexBufferObject;
}
@Override
protected void preDraw(final GLState pGLState, final Camera pCamera) {
super.preDraw(pGLState, pCamera);
this.mFont.getTexture().bind(pGLState);
this.mTextVertexBufferObject.bind(pGLState, this.mShaderProgram);
}
@Override
protected void draw(final GLState pGLState, final Camera pCamera) {
this.mTextVertexBufferObject.draw(GLES20.GL_TRIANGLES, this.mVertexCountToDraw);
}
@Override
protected void postDraw(final GLState pGLState, final Camera pCamera) {
this.mTextVertexBufferObject.unbind(pGLState, this.mShaderProgram);
super.postDraw(pGLState, pCamera);
}
@Override
protected void onUpdateColor() {
this.mTextVertexBufferObject.onUpdateColor(this);
}
@Override
protected void onUpdateVertices() {
this.mTextVertexBufferObject.onUpdateVertices(this);
}
// ===========================================================
// Methods
// ===========================================================
public void invalidateText() {
this.setText(this.mText);
}
// ===========================================================
// Inner and Anonymous Classes
// ===========================================================
}