content: //package ri.blog.opengl008;
  package com.introlab.rtabmap;
  
  import java.nio.ByteBuffer;
  import java.nio.ByteOrder;
  import java.nio.FloatBuffer;
  import java.nio.ShortBuffer;
  import java.util.Iterator;
  import java.util.Vector;
  
  import android.content.Context;
  import android.graphics.Bitmap;
  import android.graphics.Canvas;
  import android.graphics.Color;
  import android.graphics.Rect;
  import android.graphics.Typeface;
  import android.opengl.GLES20;
  import android.opengl.GLUtils;
  import android.text.TextPaint;
  import android.util.Log;
  
  public class TextManager {
  	
  	/* SHADER Text
  	 * 
  	 * This shader is for rendering 2D text textures straight from a texture
  	 * Color and alpha blended.
  	 * 
  	 */
  	public static final String vs_Text =
  		"uniform mat4 uMVPMatrix;" +
  		"attribute vec4 vPosition;" +
  		"attribute vec2 a_texCoord;" +
  		"varying vec2 v_texCoord;" +
  	    "void main() {" +
  	    "  gl_Position = uMVPMatrix * vPosition;" +
  	    "  v_texCoord = a_texCoord;" +
  	    "}";
  	public static final String fs_Text =
  	    "precision mediump float;" +
  	    "uniform float uColor;" +
  	    "varying vec2 v_texCoord;" +
          "uniform sampler2D s_texture;" +
  	    "void main() {" +
  	    "  gl_FragColor = texture2D( s_texture, v_texCoord );" +
  	    "  gl_FragColor.rgb *= uColor;" +
  	    "}";
  	
  	public static int sp_Text;
  	
  	public static final int RI_TEXT_TEXTURE_SIZE = 512; // 512
  	public static final float RI_TEXT_HEIGHT_BASE = 32.0f;
  	public static final char RI_TEXT_START = ' ';
  	public static final char RI_TEXT_STOP = '\u00B0'+1;
  	
  	public float getMaxTextHeight() {return mTextHeight;}
  	
  	private float mUVWidth;
  	private float mUVHeight;
  	private float mTextHeight;
  	private float mColor;
  	
  	private FloatBuffer vertexBuffer;
  	private FloatBuffer textureBuffer;
  	private ShortBuffer drawListBuffer;
  	
  	private float[] vecs;
  	private float[] uvs;
  	private short[] indices;
  	
  	private int index_vecs;
  	private int index_indices;
  	private int index_uvs;
  
  	private int texturenr;
  	private int[] mTextures;
  	
  	private float uniformscale = 1.0f;
  	
  	float[] mCharacterWidth;
  	
  	public TextManager(Context context)
  	{
  		// Create the arrays
  		vecs = new float[3 * 10];
  		uvs = new float[2 * 10];
  		indices = new short[10];
  		
  		// init as 0 as default
  		texturenr = 0;
  		
  		// Text shader
  		int vshadert = TextManager.loadShader(GLES20.GL_VERTEX_SHADER, TextManager.vs_Text);
  		int fshadert = TextManager.loadShader(GLES20.GL_FRAGMENT_SHADER, TextManager.fs_Text);
  
  		TextManager.sp_Text = GLES20.glCreateProgram();
  		GLES20.glAttachShader(TextManager.sp_Text, vshadert);
  		GLES20.glAttachShader(TextManager.sp_Text, fshadert); 		// add the fragment shader to program
  		GLES20.glLinkProgram(TextManager.sp_Text);                  // creates OpenGL ES program executables
  		
  		// Generate Textures, if more needed, alter these numbers.
  		mTextures = new int[1];
  		GLES20.glGenTextures(1, mTextures, 0);
  		
  		// Create an empty, mutable bitmap
  		Bitmap bitmap = Bitmap.createBitmap(RI_TEXT_TEXTURE_SIZE, RI_TEXT_TEXTURE_SIZE, Bitmap.Config.ARGB_4444);
  		// get a canvas to paint over the bitmap
  		Canvas canvas = new Canvas(bitmap);
  		bitmap.eraseColor(0);
  
  		// Draw the text
  		TextPaint textPaint = new TextPaint();
  		textPaint.setTextSize(RI_TEXT_HEIGHT_BASE);
  		textPaint.setColor(Color.WHITE);
  		textPaint.setAntiAlias(true);
  		textPaint.setTypeface(Typeface.create("Arial", Typeface.BOLD));
  		
  		// compute real maximum height
  		mTextHeight=0.0f;
  		for(char c=RI_TEXT_START; c<RI_TEXT_STOP; ++c)
  		{
  			Rect textBounds = new Rect();
  			textPaint.getTextBounds(String.valueOf(c), 0, 1, textBounds);
  			if(textBounds.height() + textBounds.bottom > mTextHeight)
  			{
  				mTextHeight = textBounds.height() + textBounds.bottom;
  			}
  		}
  		mUVWidth = (float)RI_TEXT_HEIGHT_BASE/(float)RI_TEXT_TEXTURE_SIZE;
  		mUVHeight = mTextHeight/(float)RI_TEXT_TEXTURE_SIZE;
  		mColor = 1.0f;
  		
  		int colCount = RI_TEXT_TEXTURE_SIZE/(int)RI_TEXT_HEIGHT_BASE;
  		mCharacterWidth = new float[RI_TEXT_STOP-RI_TEXT_START];
  		int i=0;
  		for(char c=RI_TEXT_START; c<RI_TEXT_STOP; ++c)
  		{
  			Rect textBounds = new Rect();
  			textPaint.getTextBounds(String.valueOf(c), 0, 1, textBounds);
  			canvas.drawText(String.valueOf(c), (i%colCount)*RI_TEXT_HEIGHT_BASE, (i/colCount) * mTextHeight + RI_TEXT_HEIGHT_BASE, textPaint);
  			mCharacterWidth[i] = textPaint.measureText(String.valueOf(c));
  			++i;
  		}
  		
  		GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextures[0]);
  		GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
          GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
          GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
  		
  		//Clean up
  		bitmap.recycle();
  	}
  	
  	public void setTextureID(int val)
  	{
  		texturenr = val;
  	}
  	
  	public static int loadShader(int type, String shaderCode){
  
  	    // create a vertex shader type (GLES20.GL_VERTEX_SHADER)
  	    // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
  	    int shader = GLES20.glCreateShader(type);
  
  	    // add the source code to the shader and compile it
  	    GLES20.glShaderSource(shader, shaderCode);
  	    GLES20.glCompileShader(shader);
  	    
  	    // return the shader
  	    return shader;
  	}
  	
  	
  	public void AddCharRenderInformation(float[] vec, float[] cs, float[] uv, short[] indi)
  	{
  		// We need a base value because the object has indices related to 
  		// that object and not to this collection so basicly we need to 
  		// translate the indices to align with the vertexlocation in ou
  		// vecs array of vectors.
  		short base = (short) (index_vecs / 3);
  			
  		// We should add the vec, translating the indices to our saved vector
  		for(int i=0;i<vec.length;i++)
  		{
  			vecs[index_vecs] = vec[i];
  			index_vecs++;
  		}
  		
  		// We should add the uvs
  		for(int i=0;i<uv.length;i++)
  		{
  			uvs[index_uvs] = uv[i];
  			index_uvs++;
  		}
  
  		// We handle the indices
  		for(int j=0;j<indi.length;j++)
  		{
  			indices[index_indices] = (short) (base + indi[j]);
  			index_indices++;
  		}
  	}
  
  	public void PrepareDrawInfo(Vector<TextObject> txtcollection)
  	{
  		// Reset the indices.
  		index_vecs = 0;
  		index_indices = 0;
  		index_uvs = 0;
  		
  		// Get the total amount of characters
  		int charcount = 0;
  		for (TextObject txt : txtcollection) {
  			if(txt!=null)
  			{
  				if(!(txt.text==null))
  				{
  					charcount += txt.text.length(); 
  				}
  			}
  		}
  		
  		// Create the arrays we need with the correct size.
  		vecs = null;
  		uvs = null;
  		indices = null;
  		
  		vecs = new float[charcount * 12];
  		uvs = new float[charcount * 8];
  		indices = new short[charcount * 6];
  
  	}
  	
  	public void PrepareDraw(Vector<TextObject> txtcollection)
  	{
  		// Setup all the arrays
  		PrepareDrawInfo(txtcollection);
  		
  		// Using the iterator protects for problems with concurrency
  		for( Iterator< TextObject > it = txtcollection.iterator(); it.hasNext() ; )
  	    {
  	    	TextObject txt = it.next();
  	    	if(txt!=null)
  			{
  		    	if(!(txt.text==null))
  				{
  					convertTextToTriangleInfo(txt);
  				}
  			}
  	    }
  	}
  	
  	public void Draw(float[] m)
  	{
  		if(vecs.length > 0)
  		{
  			GLES20.glDisable(GLES20.GL_DEPTH_TEST);
  			GLES20.glEnable(GLES20.GL_BLEND);
  			
  			// Set the correct shader for our grid object.
  			GLES20.glUseProgram(sp_Text);
  			
  			// The vertex buffer.
  			ByteBuffer bb = ByteBuffer.allocateDirect(vecs.length * 4);
  			bb.order(ByteOrder.nativeOrder());
  			vertexBuffer = bb.asFloatBuffer();
  			vertexBuffer.put(vecs);
  			vertexBuffer.position(0);
  			
  			// The texture buffer
  			ByteBuffer bb2 = ByteBuffer.allocateDirect(uvs.length * 4);
  			bb2.order(ByteOrder.nativeOrder());
  			textureBuffer = bb2.asFloatBuffer();
  			textureBuffer.put(uvs);
  			textureBuffer.position(0);
  			
  			// initialize byte buffer for the draw list
  			ByteBuffer dlb = ByteBuffer.allocateDirect(indices.length * 2);
  			dlb.order(ByteOrder.nativeOrder());
  			drawListBuffer = dlb.asShortBuffer();
  			drawListBuffer.put(indices);
  			drawListBuffer.position(0);
  			
  			// get handle to vertex shader's vPosition member
  		    int mPositionHandle = GLES20.glGetAttribLocation(sp_Text, "vPosition");
  		    
  		    // Enable a handle to the triangle vertices
  		    GLES20.glEnableVertexAttribArray(mPositionHandle);
  		 
  		    // Prepare the background coordinate data
  		    GLES20.glVertexAttribPointer(mPositionHandle, 3,
  		                                 GLES20.GL_FLOAT, false,
  		                                 0, vertexBuffer);
  		    
  		    int mTexCoordLoc = GLES20.glGetAttribLocation(sp_Text, "a_texCoord" );
  		    
  		    // Prepare the texturecoordinates
  		    GLES20.glVertexAttribPointer ( mTexCoordLoc, 2, GLES20.GL_FLOAT,
  	                false, 
  	                0, textureBuffer);
  	
  		    GLES20.glEnableVertexAttribArray ( mPositionHandle );
  	        GLES20.glEnableVertexAttribArray ( mTexCoordLoc );
  	
  		    // get handle to shape's transformation matrix
  	        int mtrxhandle = GLES20.glGetUniformLocation(sp_Text, "uMVPMatrix");
  	        
  	    	// Apply the projection and view transformation
  	        GLES20.glUniformMatrix4fv(mtrxhandle, 1, false, m, 0);
  	        
  	        // get handle to color value
  	        int colorhandle = GLES20.glGetUniformLocation(sp_Text, "uColor");
  	        GLES20.glUniform1f(colorhandle, mColor);
  	        
  	        int mSamplerLoc = GLES20.glGetUniformLocation (sp_Text, "s_texture" );
  	        
  	        // Texture activate unit 0
  	        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
  			// Bind the texture to this unit.
  	        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextures[0]);
  	        // Set the sampler texture unit to our selected id
  		    GLES20.glUniform1i ( mSamplerLoc, texturenr);
  		    
  	        // Draw the triangle
  	        GLES20.glDrawElements(GLES20.GL_TRIANGLES, indices.length, GLES20.GL_UNSIGNED_SHORT, drawListBuffer);
  	
  	        // Disable vertex array
  	        GLES20.glDisableVertexAttribArray(mPositionHandle);
  	        GLES20.glDisableVertexAttribArray(mTexCoordLoc);
  		}
  	}
  	
  	private void convertTextToTriangleInfo(TextObject val)
  	{		
  		// Get attributes from text object
  		float x = val.x;
  		float y = val.y;
  		String text = val.text;
  		
  		Log.i("RTABMapActivity", String.format("convertTextToTriangleInfo() set status=%s", text));
  		
  		// Create 
  		for(int j=0; j<text.length(); j++)
  		{
  			// get ascii value
  			char c = text.charAt(j);
  			int c_val = (int)c;
  			
  			int indx = c_val-RI_TEXT_START;
  			
  			if(indx<0 || indx>=mCharacterWidth.length) {
  				// unknown character, we will add a space for it to be save.
  				indx = 0;
  			}
  			
  			int colCount = RI_TEXT_TEXTURE_SIZE/(int)RI_TEXT_HEIGHT_BASE;
  			
  			// Calculate the uv parts
  			int row = indx / colCount;
  			int col = indx % colCount;
  			
  			float v = row * mUVHeight;
  			float v2 = v + mUVHeight;
  			float u = col * mUVWidth;
  			float u2 = u + mCharacterWidth[indx]/(float)RI_TEXT_TEXTURE_SIZE;
  			
  			// Creating the triangle information
  			float[] vec = new float[12];
  			float[] uv = new float[8];
  			float[] colors = new float[16];
  			
  			vec[0] = x;
  			vec[1] = y + (mTextHeight * uniformscale);
  			vec[2] = 0.99f;
  			vec[3] = x;
  			vec[4] = y;
  			vec[5] = 0.99f;
  			vec[6] = x + (mCharacterWidth[indx] * uniformscale);
  			vec[7] = y;
  			vec[8] = 0.99f;
  			vec[9] = x + (mCharacterWidth[indx] * uniformscale);
  			vec[10] = y + (mTextHeight * uniformscale);
  			vec[11] = 0.99f;
  		
  			colors = new float[]
  				    	{val.color[0], val.color[1], val.color[2], val.color[3],
  						val.color[0], val.color[1], val.color[2], val.color[3],
  						val.color[0], val.color[1], val.color[2], val.color[3],
  						val.color[0], val.color[1], val.color[2], val.color[3]
  				    			  		           };
  			// 0.001f = texture bleeding hack/fix
  			uv[0] = u+0.001f;
  			uv[1] = v+0.001f;
  			uv[2] = u+0.001f;
  			uv[3] = v2-0.001f;
  			uv[4] = u2-0.001f;
  			uv[5] = v2-0.001f;
  			uv[6] = u2-0.001f;
  			uv[7] = v+0.001f;
  			
  			short[] inds = {0, 1, 2, 0, 2, 3};
  			
  			// Add our triangle information to our collection for 1 render call.
  			AddCharRenderInformation(vec, colors, uv, inds);
  			
  			// Calculate the new position
  			x += (mCharacterWidth[indx]  * uniformscale);
  		}
  	}
  
  	public float getUniformscale() {
  		return uniformscale;
  	}
  
  	public void setUniformscale(float uniformscale) {
  		this.uniformscale = uniformscale;
  	}
  	
  	public void setColor(float color) {
  		mColor = color;
  	}
  }
  