Saturday, 21 April 2012

Rendering Text in OpenGL on Android




Updates:
* November 2013
  - Added v1.1 of the standard example code, with a small
    fix to clear vertex array state after drawing.
* February 2013
  - Added a version that works with OpenGL ES 2.0, provided
    by Aleksandar Kodzhabashev.
* January 2013
  - Added a version that includes z-values for rendering text
    in 3D space. It was written by Jocelyn Demoy.
* September 2012
  - Added a mirror for the code download.
  - Added a Scala port of the code, provided by Alexey Aksenov.


This post is intended for developers. It describes the method I used for rendering high-quality dynamic text efficiently using OpenGL ES 1.0, with TrueType/OpenType font files.

One of the most frustrating things I have encountered while working with 3D APIs (such as OpenGL) is the complete lack of support for text rendering. Because of this unexplainable shortcoming, we have to implement text rendering on our own. Before starting with my Android game, I had already done this multiple times for both Direct3D and OpenGL, using methods ranging from static text on a bitmap to complete bitmap fonts (usually generated via a tool).

Sadly, these implementations did not translate well to OpenGL ES....so I had to do it again :)

Interested? Keep reading for details and a download of full source code that can be used immediately in any project, without limitation!

Initially, I went with a quick implementation using Codehead's Bitmap Font Generator (CBFG). It was easy, fast and great to have text display implemented without any real work :) It's a great tool.

However, at some point I realized that in order for me to support high quality text (i.e. without using scaling) at all resolutions, I would have to generate a font bitmap for each resolution. This was compounded by the fact that I wanted two different sized fonts (and again did not want to do any scaling). The result was around 6-10 Megabytes worth of uncompressed font bitmaps (and that was without support for obscure resolutions). It just wasn't going to work out for me...so I decided that I needed a better solution.

I wanted to build a dynamic text engine that works with OpenGL ES in Android. The idea of this system is to "generate" the same type of font bitmap that CBFG does, but it does so at run-time using a standard TrueType (.ttf) or OpenType (.otf) font file. Using this method I would only need to include the font file(s), which are usually quite small, and it could then generate correctly scaled fonts for whatever resolution the game was run on.

I had implemented a dynamic text rendering system like this once before using Direct3D on Windows, but never in OpenGL. Also, the Direct3D one was using Windows GDI for generating the font bitmaps so it was basically useless for Android. I could not find any description or code that does exactly this anywhere on the web. The closest was some Android SDK example which renders full strings to a texture and then displays the texture. This was not nearly "dynamic" enough for strings that change "per frame", but it provided the groundwork for what I had in mind.

NOTE: As far as I know, libgdx uses this same way of rendering text, but I did not want to spend the time "unhooking" their implementation from the rest of the engine. I wanted code that I could use without having to employ an entire game engine.

But enough about what led me to this solution. Let's look at how it works:

The entire generation of the font bitmap (or atlas, as some people call it) is performed in a single method. The following image shows what a font bitmap, generated by the code I use here, looks like.


Note that it is rendered on a grey background; the actual bitmap has a transparent background. Also, my method uses alpha only for the characters, and they can be rendered in any color.

The whole process is actually quite easy. We generate the bitmap (as a texture), calculate and store the size of each character, as well as it's location on the texture (UV coordinates). There are some other finer details, but we'll get to that.

The Code


License: The code is released under the CC0 1.0 public domain license - in English it means that there are NO restrictions placed on usage of the code in any way.

Download the source code (mirror).
Download the source code v1.1.

Download the source code (includes a copy of the CC0 1.0 public domain license).
Download the source code v1.1 (includes a copy of the CC0 1.0 public domain license).

Update (Sept 2012): The code has been ported to Scala by Alexey Aksenov. It can be found here.

Update (Jan 2013): A new version of the example which uses z-values for rendering (allowing placement in 3D space) was done by Jocelyn Demoy. It can be found here.

Update (Feb 2013): A new version of the example which uses OpenGL ES 2.0 for rendering is provided by Aleksandar Kodzhabashev. It can be found here.

The included source files are as follows:
  • Texample.java and TexampleRenderer.java - these files are from the OpenGL ES 1.0 Android SDK sample. They only serve as a place to test the rest of the code. Ignore these if you intend to use the code in an existing project.
  • Vertices.java and SpriteBatch.java - these are used for "batch-rendering" characters, which is the only efficient way of rendering dynamic text. These provide no special text-related operations and can be replaced if you have an existing rendering solution.
  • TextureRegion.java - this is just a little helper class that makes it easier to define a region on a texture (i.e. the UV coordinates) for each character.
  • GLText.java - this holds the GLText class which does all of the text-related work (generation, rendering, etc.). If you replace the render calls, this is the only file that you need.

So let's look at the code for GLText and I'll discuss the subtle details :)

Constants & Members


First, we need to define some constant values:

public final static int CHAR_START = 32;
   public final static int CHAR_END = 126;
   public final static int CHAR_CNT = (((CHAR_END - CHAR_START)+1)+1 );

   public final static int CHAR_NONE = 32;
   public final static int CHAR_UNKNOWN = (CHAR_CNT-1);

   public final static int FONT_SIZE_MIN = 6;
   public final static int FONT_SIZE_MAX = 180;
   public final static int CHAR_BATCH_SIZE = 100;

  • CHAR_START and CHAR_END define the ASCII character range of the characters we want.
  • CHAR_CNT is the total number of characters (the extra +1 is for the "unknown" character).
  • CHAR_NONE defines which ASCII character to use for "unknown" characters. Here I used '32', which is the ASCII code for space, but you could change this to any valid ASCII code.
  • CHAR_UNKNOWN is the index of the "unknown" character.
  • FONT_SIZE_MIN/MAX is the minimum and maximum pixel size supported for creating fonts. The minimum size is not that important but maximum is needed since maximum texture size is limited.
  • CHAR_BATCH_SIZE is the number of characters that are batched when rendering (this value should be tweaked depending on requirements...smaller values use less memory, larger values use more).

Next up, the members of the class:

GL10 gl;
   AssetManager assets;
   SpriteBatch batch;

   int fontPadX, fontPadY;

   float fontHeight;
   float fontAscent;
   float fontDescent;

   int textureId;
   int textureSize;
   TextureRegion textureRgn;
   float charWidthMax;
   float charHeight;
   final float[] charWidths;
   TextureRegion[] charRgn;
   int cellWidth, cellHeight;
   int rowCnt, colCnt;

   float scaleX, scaleY;
   float spaceX;

  • gl and assets - these are obtained from the GLSurfaceView and Context (respectively) and cached.
  • fontPadX, fontPadY - these are padding values to use for each character. They indicate the number of spacing pixels to add on each axis between characters. Padding is added to both sides of each axis (i.e. a fontPadX value of 1 means 1 pixel on the left and one pixel on the right). Use these if characters in a font overlap. Note that these values do not affect spacing beween characters when rendering (use spaceX for that).
  • fontHeight, fontAscent, fontDescent - these are the metrics of the font obtained from Android. Use them manually when rendering for accurate text positioning.
  • textureId, textureSize, textureRgn - the ID of the generated texture (containing the font characters), it's size (square, power of two), and a region used to render it (for debug purposes).
  • charWidthMax - the width of the widest character in the font.
  • charHeight - the height of all characters (maximum).
  • charWidths - the array of character widths (exact width per character).
  • charRgn - the array of character regions on the texture.
  • cellWidth, cellHeight - the width and height of each cell on the texture (including padding, etc).
  • rowCnt, colCnt - the number of rows and columns on the texture.
  • scaleX, scaleY - these are rendering values used to scale the rendered text. A value of 1.0 means unscaled, values less than one make the text smaller, and values larger than one make it bigger.
  • spaceX - this value indicates the amount of extra spacing (on the X-axis) to add between characters when rendering.

The class contains various accessors and utility methods which are self explanatory (and well commented). Here I will only focus on two methods: load() and draw(), which create the font from a file and renders a string with it, respectively.

The load() Method


First up is the load() method, which has the following prototype:

public boolean load(String file, int size, int padX, int padY)

  • file - the filename of the TrueType (.ttf) or OpenType (.otf) font file in the assets folder. You could use files from other locations and it should be easy to do if required (see the code below).
  • size - the desired size (height) of the font.
  • padX, padY - the desired X and Y axis padding to use.

And now for the actual code:

Typeface tf = Typeface.createFromAsset( assets, file );
   Paint paint = new Paint();
   paint.setAntiAlias( true );
   paint.setTextSize( size );
   paint.setColor( 0xffffffff );
   paint.setTypeface( tf );

   Paint.FontMetrics fm = paint.getFontMetrics();
   fontHeight = (float)Math.ceil( Math.abs( fm.bottom ) + Math.abs( fm.top ) );
   fontAscent = (float)Math.ceil( Math.abs( fm.ascent ) );
   fontDescent = (float)Math.ceil( Math.abs( fm.descent ) );

Here we create a TypeFace instance from the specified font file (if you want to use font files from locations other than the assets folder, this call would need to be changed). Then we create and configure a Paint instance, which will be used to draw the actual characters to the texture. We also set the typeface for the Paint instance to the one created from the file. And finally, we get the font metrics from the Paint instance and save the values for future use.

char[] s = new char[2];
   charWidthMax = charHeight = 0;
   float[] w = new float[2];
   int cnt = 0;
   for ( char c = CHAR_START; c <= CHAR_END; c++ ) {
      s[0] = c;
      paint.getTextWidths( s, 0, 1, w );
      charWidths[cnt] = w[0];
      if ( charWidths[cnt] > charWidthMax )
         charWidthMax = charWidths[cnt];
      cnt++;
   }
   s[0] = CHAR_NONE;
   paint.getTextWidths( s, 0, 1, w );
   charWidths[cnt] = w[0];
   if ( charWidths[cnt] > charWidthMax )
      charWidthMax = charWidths[cnt];
   cnt++;

   charHeight = fontHeight;

Next we use the Paint instance (with set TypeFace) to determine the exact width of each character, and save that to an array which is used at render time to properly space the characters in a string. During this operation we also track and save the widest character's width. Finally we simply set the character height to the font height.

cellWidth = (int)charWidthMax + ( 2 * fontPadX );
   cellHeight = (int)charHeight + ( 2 * fontPadY );
   int maxSize = cellWidth > cellHeight ? cellWidth : cellHeight;
   if ( maxSize < FONT_SIZE_MIN || maxSize > FONT_SIZE_MAX )
      return false;

At this point we calculate the cell sizes to use for each character on the texture. Each cell is the exact same size, so we need to use a size that is large enough to contain the widest character. We also need to take the specified padding into consideration. Finally we get the largest dimension (from width and height) and make sure that the cell size fits within our defined minimum and maximum size range (if not, we return an error).

if ( maxSize <= 24 )
      textureSize = 256;
   else if ( maxSize <= 40 )
      textureSize = 512;
   else if ( maxSize <= 80 )
      textureSize = 1024;
   else
      textureSize = 2048;

Now that we have the maximum size, we need to determine how large the texture must be in order to hold all of the defined characters. Here I have done that manually, but it could be computed from the cell size (if desired). The main thing is that these values will require modification if the defined character range changes.

Bitmap bitmap;
   bitmap = Bitmap.createBitmap( textureSize, textureSize, Bitmap.Config.ALPHA_8 );
   Canvas canvas = new Canvas( bitmap );
   bitmap.eraseColor( 0x00000000 );

   colCnt = textureSize / cellWidth;
   rowCnt = (int)Math.ceil( (float)CHAR_CNT / (float)colCnt );

At this point we can create a Bitmap of the determined texture size. Notice that we use only an alpha channel for the bitmap. This is not compulsory - you could use full ARGB (it would be required if you intend to use stroked fonts). However, I found that using alpha only created better looking text (and I have no intention of ever using stroked fonts). Use of an alpha channel only also decreases the memory requirement of the texture. We also create a Canvas and attach the Bitmap to it so that we can draw to it. We then set the bitmap to have a transparent background (alpha = 0). Finally, we calculate the number of columns and rows for the characters on the bitmap (while this is not actually needed in this implementation, I left it since it may be useful at some point).

float x = fontPadX;
   float y = ( cellHeight - 1 ) - fontDescent - fontPadY;
   for ( char c = CHAR_START; c <= CHAR_END; c++ ) {
      s[0] = c;
      canvas.drawText( s, 0, 1, x, y, paint );
      x += cellWidth;
      if ( ( x + cellWidth - fontPadX ) > textureSize ) {
         x = fontPadX;
         y += cellHeight;
      }
   }
   s[0] = CHAR_NONE;
   canvas.drawText( s, 0, 1, x, y, paint );

Finally we can generate our font bitmap. First, we set the (x,y) coordinate (bottom/left of character) for the first character (note that padding is included in this). Then we loop for each of our defined characters and render them to the canvas (i.e. the bitmap) using our configured paint instance (which has the font file set as typeface). After rendering, we advance on the x-axis for the next character. Finally we test to see if the edge of the bitmap has been reached, and if so we reset the x-axis and move down to the next line on the y-axis. After rendering the defined characters we also render the "unknown" character.

int[] textureIds = new int[1];
   gl.glGenTextures( 1, textureIds, 0 );
   textureId = textureIds[0];

   gl.glBindTexture( GL10.GL_TEXTURE_2D, textureId );
   gl.glTexParameterf( GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER,
                       GL10.GL_NEAREST );
   gl.glTexParameterf( GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER,
                       GL10.GL_LINEAR );
   gl.glTexParameterf( GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S,
                       GL10.GL_CLAMP_TO_EDGE );
   gl.glTexParameterf( GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T,
                       GL10.GL_CLAMP_TO_EDGE );

   GLUtils.texImage2D( GL10.GL_TEXTURE_2D, 0, bitmap, 0 );

   gl.glBindTexture( GL10.GL_TEXTURE_2D, 0 );
   bitmap.recycle();

Next we generate a new texture, bind it, and set the filtering and wrapping to use. Very basic OpenGL stuff. Then we transfer the bitmap to the texture using GLUtils (which makes it very simple). Finally we unbind the texture and release the bitmap.

x = 0;
   y = 0;
   for ( int c = 0; c < CHAR_CNT; c++ ) {
      charRgn[c] = new TextureRegion( textureSize, textureSize,
                          x, y, cellWidth-1, cellHeight-1 );
      x += cellWidth;
      if ( x + cellWidth > textureSize ) {
         x = 0;
         y += cellHeight;
      }
   }

Finally we setup a texture region (ie. UV coordinates) for each character on the bitmap. We could have done this while rendering the characters, but I prefer to keep it separate. The rendering code will use these regions to quickly select the correct character to render. Note that these do not use the padding values, they use to the entire cell size (which includes the padding already).

And that is that. Our font bitmap is created, and we have all the information required to render strings using it. Which brings us to...

The draw() Method


The draw() method has the following prototype:

public void draw(String text, float x, float y)

  • text - the string to render.
  • x, y - the (x,y) position to render the text at. Note that this is the bottom/left corner of the first character.

The code for the draw method is very short and simple:

float chrHeight = cellHeight * scaleY;
   float chrWidth = cellWidth * scaleX;
   int len = text.length();
   x += ( chrWidth / 2.0f ) - ( fontPadX * scaleX );
   y += ( chrHeight / 2.0f ) - ( fontPadY * scaleY );
   for ( int i = 0; i < len; i++ ) {
      int c = (int)text.charAt( i ) - CHAR_START;
      if ( c < 0 || c >= CHAR_CNT )
         c = CHAR_UNKNOWN;
      batch.drawSprite( x, y, chrWidth, chrHeight, charRgn[c] );
      x += ( charWidths[c] + spaceX ) * scaleX;
   }

This code renders the actual string. First we calculate our scaled cell width and height (we do this here since we may want to change scaling values between draw calls, so caching them won't work).
Next we calculate the length of the string (i.e. the number of characters it contains).
Then we setup the initial rendering position based on the given (x,y) coordinates. The reason for the weird looking code is that the SpriteBatch used renders sprites centered at (x,y) but we want text to originate at the bottom/left. What is important however is that we include the scale and font padding in this calculation, to properly position the first character.
Once we have our starting position we loop for each character in the string (i.e. the remaining code is executed for each character).
We extract an index for the character, based on it's ASCII code. Basically, we get the code of the character (the charAt() call) and then adjust it by our start character's ASCII code (since that would be at position 0 in our arrays). We also need to validate that the character exists within our defined range, and if not we use the "unknown" character instead.
The sprite batch then takes care of the drawing, given the (x,y) coordinate, the size to render, as well as the region on the texture for this character. Again, any rendering code can be used here, all that is important is to use these values for rendering.
Finally, we adjust the x-axis coordinate for the next character. To do this we advance by the current character's width (as calculated in load(), not the cell width) as well as by the currently set spaceX value. The resulting amount must also be scaled by the currently set scaleX value.

And that is that. Dynamic text rendering using font files...check!

Usage & Notes


Included in the source code is an example of using the class. See the TexampleRenderer.java file for full details (and comments).

First we need to create an instance and then load the font - this should be done at initialization time, for example in the onSurfaceCreated() method. We can create any number of font instances, as required. This code assumes that the context member has been set, and a font file called "Roboto-Regular.ttf" exists in the assets folder. The code looks as follows:

private GLText glText;

   public void onSurfaceCreated(GL10 gl, EGLConfig config) {
      glText = new GLText( gl, context.getAssets() );
      glText.load( "Roboto-Regular.ttf", 14, 2, 2 );
   }

Next, and this is very important, we must prepare for rendering by enabling 2D texturing, as well as alpha blending (and setting a blend function). Without these, rendering won't work (regardless of what method you use to render). The reason being that the font bitmap is a texture which requires alpha blending. This code would go in the onDrawFrame() method (or a similar location, where rendering is required). The code looks as follows (very basic example):

public void onDrawFrame(GL10 gl) {
      gl.glClear( GL10.GL_COLOR_BUFFER_BIT );
      gl.glMatrixMode( GL10.GL_MODELVIEW );
      gl.glLoadIdentity();

      gl.glEnable( GL10.GL_TEXTURE_2D );
      gl.glEnable( GL10.GL_BLEND );
      gl.glBlendFunc( GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA );

      glText.begin( 1.0f, 1.0f, 1.0f, 1.0f );
      glText.draw( "Test String :)", 0, 0 );
      glText.draw( "Line 1", 50, 50 );
      glText.draw( "Line 2", 100, 100 );
      glText.end();

      glText.begin( 0.0f, 0.0f, 1.0f, 1.0f );
      glText.draw( "More Lines...", 50, 150 );
      glText.draw( "The End.", 50, 150 + glText.getCharHeight() );
      glText.end();

      gl.glDisable( GL10.GL_BLEND );
      gl.glDisable( GL10.GL_TEXTURE_2D );
   }

NOTE: the glText.begin() and glText.end() methods were not discussed, but basically they just start and end a batch rendering. They also provide an easy way to set the font color (using this rendering system, font color is set per batch, not per draw call).

Source Code


License: The code is released under the CC0 1.0 public domain license - in English it means that there are NO restrictions placed on usage of the code in any way.

Download the source code (mirror).

Download the source code (includes a copy of the CC0 1.0 public domain license).

Update (Sept 2012): The code has been ported to Scala by Alexey Aksenov. It can be found here.

Update (Jan 2013): A new version of the example which uses z-values for rendering (allowing placement in 3D space) was done by Jocelyn Demoy. It can be found here.

Update (Feb 2013): A new version of the example which uses OpenGL ES 2.0 for rendering is provided by Aleksandar Kodzhabashev. It can be found here.

The archive contains two folders:
"Texample" - full IntelliJ IDEA CE (my IDE of choice for Android development) project
"Texample (Code Only)" - just the 5 .java source files (create your own project, using your preferred IDE)

For this example I used a min-sdk=3 and a target-sdk=15 (i.e. Android 1.6 to Android 4.0), since the code should work on all android versions.

Here is a screenshot of the test application running:


Hope this makes text rendering much easier for some people :) Feel free to leave comments and check out my free game flipt on the Android market for an example of the text rendering in action, or just for some fun!

99 comments:

  1. Thank you ...
    It works great out-of-the-box.

    ReplyDelete
  2. Thank you.Can you reupload the source code?

    ReplyDelete
  3. The download seems to be working...maybe just a temporary problem. I added a download mirror just to be safe :)

    ReplyDelete
  4. From this I was able to create a version for OpenGL ES 2.0 (though I had to take some liberties)

    Can't thank you enough

    ReplyDelete
    Replies
    1. Could you please post a version for OpenGL ES 2.0 somwhere?

      Delete
  5. Thank you very much. That's what I was looking for. :D

    ReplyDelete
  6. @JP: would you mind sharing your results? :)

    ReplyDelete
  7. Fantastic! This is a great solution to text rendering. Thanks for sharing your code and taking the time to explain how it works.

    You mention that the "full source code .. can be used immediately in any project, without limitation!". I couldn't find any copyright or license notices in the files I downloaded. Can you please add a notice of some kind to these files so I feel better about reusing them in my (closed source) project?

    Thanks again for sharing your solution. :)

    ReplyDelete
    Replies
    1. Sure, no problem. Can't be too careful nowadays I suppose :)

      I have added a source package that includes the CC0 1.0 public domain license :)

      Delete
  8. thanks...great solution. Actually I cannot get it working on my Galaxy, but I learnt lot of stuff.

    Now... what I wanted to use it in a different way, let's say, in an augmented Reality App?

    I would need to draw specific strings only when a certain marker is detected (I use AndAR), so the solution of drawing text in the OnDrawFrame would not work.

    How could I create "several" GLText objects and draw different string depending on the markers in the scene?

    Anyone can help me with that?

    ReplyDelete
    Replies
    1. Go have a look here for various solutions that might fit your needs

      http://stackoverflow.com/questions/1339136/draw-text-in-opengl-es-android

      Otherwise you could just have a signal (some variables or whatver) that you could SET when an event occurs, and in OnDrawFrame just check if these are set (i.e. signalled) and render the correct string.

      Delete
  9. This saves me a bunch of time, Thank you very much!

    ReplyDelete
    Replies
    1. but i found, that it measure wrogly wigth of some characters. But it looks like problem of Android API, because for example in Photoshop same font is displayed correctly.

      Delete
    2. I haven't experienced this personally. Did it only happen with one font or have you seen this with various fonts? Could be that Android and Photoshop handles some advanced font details (such as kerning) differently.

      I have had fonts where a lot of the characters overlap (but they were that way in photoshop too). That is why I added the padding values :)

      You could try to use something other than 'paint.getTextWidths' for obtaining the the widths (there are a couple of ways as far as I recall) and see if that fixes the problem.

      Delete
  10. I'm trying to get this to work over a 3d scene. Any idea what changes I'd have to make to get it working with a perspective rather than and orthographic view? All my attempts are proving useless.

    ReplyDelete
    Replies
    1. If you mean using this to display text "in" the 3D scene (i.e. not mapped to the display's X,Y), then you would simply need to replace the "batch.drawSprite()" call in the "draw()" method with code that would generate quads in the 3D space (i.e. which would include z coordinates).

      The easiest way would be to include a Z coordinate and set it to zero (0). Then once you have this geometry (which would be a group of quads at z=0), you could just transform it like any other geometry in 3D space. You may have to alter the origin of the quads depending on how you want to transform the "string" - for a start you would always use (0,0) for (x,y) since you would actually position it with transformations instead of placing them directly in world space.

      The included SpriteBatch, while completely functional, is just an example I added to illustrate usage. It would be best to write your own code to generate the quads (in local or world space, as needed). The draw() method calculates/provides position (x,y), character sizes, and texture coords (u,v) for each character.

      If you are looking for an example of this type of thing, check out an example of Billboarding. Same basic idea, except that you would not actually need to align the quads to the eye (camera); unless that is what you are trying to do ;)

      Hope that helps :)

      Delete
    2. I've got it working aligned to the camera now. Thanks

      Delete
  11. Here is port of your example to Scala. I hope that Apache license is compatible with CC0 1.0 public domain license. Thank you very much for your efforts. https://github.com/ezh/android-DigiLib/tree/develop-0.3/src/main/scala/org/digimead/digi/ctrl/lib/androidext/gl

    King regards,
    Alexey

    ReplyDelete
    Replies
    1. Thanks for the effort and for sharing. I will add your link to the post :D

      Delete
  12. I decomposed my library and moved opengl stuff to separate part http://ezh.github.com/digi-lib-opengl. There is only your code now. There are autogenerated web site, API, and maven repository with artifacts for Scala 2.8.0, 2.8.1, 2.8.2, 2.9.0, 2.9.0-1, 2.9.1, 2.9.2. Please, give me a sign if you think that I need change description (maybe add your to authors ?)

    Thank you very much
    Alexey

    ReplyDelete
    Replies
    1. I see no problem with it the way it is. Thanks for including the link back here, that is more than enough :) You are the author of that particular implementation, so I really don't need to be credited for it.

      I put this up to make it easier for others to quickly get a working solution and appreciate that you have contributed to this by porting it and making it available, since under CC0 that is not a requirement.

      So thanks to you too :)

      PS: I will update the links to the new location.

      Delete
  13. Thank you a lot it saves me lot of time. You should add it on github.

    ReplyDelete
    Replies
    1. My pleasure :) I had thought about adding it to a repository, but would like to clean it up a bit and add some improvements that I've made since - and maybe do a OpenGL ES 2.0 implementation as well. Right now I am a bit swamped, but I'll get around to it.

      Delete
  14. This is very cool. And very helpful.

    Thank you!

    I screwed around with placing text in 3d but I seem to get z-fighting shimmering when I rotate the camera around the text. The first char of a string never shimmers but subsequent chars do especially if narrow (which is why I concluded it is z fighting with the next two triangles). Been playing with it and can't figure out how to tweak the code but I am a bit of an OpenGL noob. Can anyone offer insights?

    ReplyDelete
    Replies
    1. Glad you found it helpful :)

      I have not personally used this system in 3D space, but I don't see how the z-order can cause problems for the overlapping triangles since they have zero opacity at the overlapping areas - no pixels are rendered there, so the z-buffer "should" not be updated. Unless the OpenGL ES implementation has some strange bug that causes this, it should NOT happen - that said, of course for you it IS happening and this is not much help.

      Have you tried disabling Z-Buffering temporarily (to ensure that it is in fact causing the problem)?
      It could also be that the texture filtering (or bit depth) is causing "very low" alpha values (instead of zero) at these areas, which could then causes some z-fighting even though the area appears to be transparent.
      You could also try to increase the x-axis spacing (using the spaceX value in the class) to ensure that no triangles overlap - use a large value just to see if that fixes the problem.

      I hope it helps, but without actually testing it I'm really just guessing :)

      Delete
  15. Is there an easy way to configure this to display in landscape orientation?

    ReplyDelete
    Replies
    1. Sure, just add android:screenOrientation="landscape" (or "sensorLandscape") to the activity tag in the AndroidManifest.xml file and you should be good to go :)

      Delete
    2. That does work, however, it changes the rest of my graphics to display in landscape mode as well (obviously). My problem is that the activity's screen orientation is in portrait mode, but I draw my game as if it is in landscape mode. It doesn't look like there is an easy, built in way for your GLText class to work as I want it to. Let me know if there is, otherwise I'll probably need to make some modifications.

      Delete
    3. Unfortunately the renderer of the example is very basic - I wanted to focus the attention on the text generation and only provided the rendering code as an example of how to use it.

      If you need to render into a non-standard coordinate system then you will have to replace (or modify) the render code.

      Delete
  16. It was working fine since i decided to use 3D
    I use to configure my renderer with
    GLU.gluOrtho2D(gl, 0, x, 0, y);

    And now I use z index :
    GLU.gluPerspective(gl, 45.0f, (float) x / (float) y, 0.1f, 10000.0f);

    draw text does not display anymore, please could you give me some hints to make it works ? I would really appreciate.

    ReplyDelete
    Replies
    1. Hi, actually I managed to make to text display correctly on (x,y), the only litte problem I have is when my camera move on the z axix the text doesnt. How could I add the z traslation ?

      gl.glTranslatef(0 , 0, zindex) // this has no effect on text draw.

      Thank you

      Delete
    2. Hi Jocelyn,

      The sample renderer (SpriteBatch.java) only uses x and y for its vertices. You will need to add a z axis to the vertices.

      Change the value of VERTEX_SIZE size from 4 to 5. And add an extra argument to the Vertices() constructor call with a value of 'true'. Then you would need to alter the drawSprite() method to add z values to the vertex buffer - you could send them as an argument, or use a constant value (such as 0).

      That should get you started...good luck :)

      Delete
    3. Big thank for your help man, I ll send you a free copy of my game with a unique achievement made just for u. I'll give you feedback.

      Delete
    4. your explainations were neat : I did the changes and the little extra z coord on the methods signatures and it work on the first time !

      Would you like me to put the update on github ?

      Delete
  17. I'm glad that you got it working :)

    If you do put the updated renderer on github, I will link to it in the post above so that others can benefit from the changes you made.

    ReplyDelete
    Replies
    1. Here is the git hub repo with a functional example of z index use.
      https://github.com/jodem/glText-Android

      Delete
    2. Thanks for the update and the link. I have added it to the post so that everyone can benefit from the work you've done :) Good luck with your game!

      Delete
    3. Feel free to join my first beta version, every text used on the game comes from your library !

      https://plus.google.com/u/0/communities/101483204826024792834

      Delete
  18. Hi! Thanks a lot for this code :) I'm implementing it for an application and it's helping me a lot, but I'm having a problem :(,when I use it with another OpenGL's objects that use textures, the text appears over my other images with a blue background, and when I press de Go Back Button, all my images appear for a second and then the activity finishes, do you have any idea why it could be happening? Thanks for your help

    ReplyDelete
    Replies
    1. Hey there,

      As to the text having a blue background when rendering, I suspect that alpha blending is disabled. For the text to render properly, texture mapping and alpha blending must be enabled (see the DrawFrame() method in the TexampleRenderer.java file).

      And as for why the activity finishes, that will depend on what exactly happens when you Go Back. I would check the logcat when that happens and see if there are any errors (or warnings) related to your app when the activity ends prematurely.

      Hope that helps.

      Delete
    2. Thanks for your answer! Well I did what you said and I'm having the same problem, this is my onDrawFrame code:

      public void onDrawFrame(GL10 gl) {
      // TODO Auto-generated method stub



      gl.glClear( GL10.GL_COLOR_BUFFER_BIT );


      gl.glMatrixMode( GL10.GL_MODELVIEW );
      gl.glLoadIdentity();


      gl.glEnable( GL10.GL_TEXTURE_2D );
      gl.glEnable( GL10.GL_BLEND );
      gl.glBlendFunc( GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA );


      dibujarLayout(gl);

      gl.glDisable( GL10.GL_BLEND );
      gl.glDisable( GL10.GL_TEXTURE_2D );
      }
      Where "dibujarLayout(gl)" it's a Method that I use to set all the objects in the screen. And about the go back, it seems like if the screen redraws and then it goes back to the previous activity, so maybe theres is a problem in how I'm drawing the text or that's what I think, thanks for your help, I really apreciate it, and sorry for my bad english jeje

      Delete
    3. Just as a test, try to draw some text in onDrawFrame() before the call to the dibujarLayout(gl) - if that works then the problem can be isolated to somewhere in that call.

      The best way is to perform the rendering in different places to try and isolate the area that is causing problems.

      Going back is supposed to go to the previous activity so it could be that it is behaving as it should. You have to intercept the call to back press in order to prevent that. Unless I am misunderstanding you.

      Delete
    4. Thanks for your help, I did it as you said but I still having the problem with the text :(. about the go back button don't worry about it, I solved the problem, it was a lil misunderstood. Have a nice day and I will be triying some ideas I have, maybe my problem it's in how I'm rendering my objects and my text. Thanks a lot fot taking your time to answer me.

      Delete
    5. Well I think that I'm a lil bit stuck with this, I tried everything that it comes to my mind and still doesn't working. The problem is that it's like if the app first renders my images and then overlap the text, so my images are behind the text texture. Hope you can help me, and I really thanks you for the time that you spend trying to help me.

      Delete
    6. Without actually seeing a screenshot and/or the rendering code I can only guess what the cause of your problem is.

      However, as a test I added a textured triangle (behind the text) to the example, and everything renders properly - you can grab the code for the modified example here:
      http://sites.google.com/site/fractiousgames/Texample2CodeWithLicense.zip
      (the included texture should go into assets)

      An important thing to remember about OpenGL is that states (such as color, texture, etc.) remain set until they are changed. To find your problem you will have to look at which states the other objects in your rendering code change and how they could be affecting the text rendering.

      Maybe you can see something in the new example that might explain why your code is not working as expected :) I hope this helps!

      Delete
    7. Thanks! I'm checking your code, here's a link via dropbox to my project.

      https://www.dropbox.com/s/jdf6tifl7i5a7cz/codigo.rar

      The class "objetosLayout" it's the one that I use to set my textures, and the problem is in my class called "pantallaInicial" I copied your classes to my project to try to fix the problem, but it's not working. I'm testing my code in a LG phone and in a Samsung tablet. I hope this information give to you a better idea about what I want to do. Thank you.

      Delete
    8. I think I understand the problem now. As I mentioned, OpenGL states remain set/unset until they are changed. The batch renderer I use for the text will set the states it requires in the .begin() call, and will unset them in the .end() call. Maybe I just missed it, but I could not find where you enable the vertex/texture_coord array states, and I know that the text rendering will set AND unset them...so give the following a try :) In the "onDraw" method of the "pantallaInicial" class:

      Add the following immediately BEFORE the "fondo.dibujar()" call:
      gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
      gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

      Then add the following BEFORE the "texto_OpenGL.begin()" call:
      gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
      gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);

      You may also need to add a "gl.glLoadIdentity()" before rendering text, but I'm not sure if it will be needed - just add it if the text renders in the wrong place/orientation.

      Give that a try and let me know if you still have problems/questions :)

      Delete
    9. It's working!! reading your code I had a similar idea but I was not sure where I had to put the code. Thanks a lot for all the time you dedicated to my problem, it's nice to found people that it's really interested in help others.

      Delete
    10. Great, I'm glad you got it working...glad to help :) Good luck with your project!

      Delete
  19. Thanks for the code! I used it as a base to start my own openGL Android app. Lots of work to do yet, but this went a huge long way to getting me on my feet!

    ReplyDelete
    Replies
    1. Glad to help! Good luck with your app!

      Delete
  20. Hi! Thank you very much for your code - I used it as a base for a OpenGL 2 ES port which I use in my game. Here is the code - https://github.com/d3kod/Texample2. Let me know if you approve it or not! Thanks again, really saved me tons of time :-)

    ReplyDelete
    Replies
    1. Glad you found the code useful...and thank you for taking the time to post an OpenGL ES 2.0 version! I just checked it out and it works like a charm :) This will save others working in GLES 2.0 a bunch of time - +1000 karma points :D

      I will add a link to the code in the post so that everyone can benefit from your work! Thanks!

      Delete
  21. Thanks you for posting this great article and making available the source!
    I am developing a little game and this is my first Android project. I need to draw changing text on screen so this is a really big help for me.:)

    ReplyDelete
    Replies
    1. It is a pleasure! Have fun with android :)

      Delete
  22. I have added the OpenGL 2.0 version to my code but only the first letters of each line get rendered properly. The draw texture works fine but each line your get the first letter and the rest will be pixels. Any idea?

    ReplyDelete
    Replies
    1. I'm not exactly sure what you mean with "pixels" - do you mean a single pixel per extra letter (after the first)?

      Here are some things you could try to determine where the problem lies:
      * Use a completely different font file (just to make sure that it is not your font file).
      * Move the position of the GLText.draw() call around (some state that you set before or after the call may be affecting it).
      * Step through the load() and draw() calls in debug mode - pay attention to the values (such as position and scale) to see that they are set correctly.
      * Make sure the spaceX and scaleX values are set properly as these will affect the placement of all characters following the first.
      * Try to remove some of your other render calls temporarily and see if that eliminates the problem - if it does you could isolate the problem by eliminating them one by one until the problem goes away.

      As I have not personally used the OpenGL 2.0 version in any of my projects I am not exactly sure what other factors may be affecting it. One thing that has tripped me up a couple of times when using OpenGL is the fact that states persist - even if the state is set after a particular call, it will affect that call the next frame unless the state is reset (or set to some other value).

      Unfortunately it is very hard to guess the cause given this little info. If you are still having trouble don't hesitate to ask, but please provide a bit more detail and context.

      Delete
  23. Does it support any other languages like Chinese?
    Thanks

    ReplyDelete
    Replies
    1. Unfortunately, the current system only supports ASCII text. It is possible to make use of unicode fonts but it requires a lot of changes to the code. You could try to make it work using the example as a starting point, but I am afraid I won't be able to offer much assistance on the specifics. Here are some hints about what would need to be done:

      * First you would need to alter the range of characters CHAR_START and CHAR_END. This would need to change to include all the characters you want to use from the font file.
      * Next, in the load() method you would need to alter the code that calculates the 'textureSize' to work with the amount of characters that you use.
      * Depending on the number of characters (and their size) you may also need to use multiple textures, which will increase the rendering complexity quite a lot. For example, if you are using multiple textures, it would be best to use a larger cache (batch) and sort characters per texture to minimize texture changes when rendering.
      * In the draw() method you may also need to alter the indexing into the array (the 'c' variable in the loop) depending on how the language works.

      I hope this provides you with some helpful information :)
      Good luck

      Delete
  24. Hi!
    Thank you so much for the code!
    I'm trying to use the OPENGL ES 2.0 version, and although it works on device, it always crashes the emulator. Even running the example project provided, without modifications, the emulator still crash.
    Any idea?

    ReplyDelete
    Replies
    1. Hey,

      I just checked and indeed it does crash the emulator :( Sadly, as this is not my port I have no idea as to the reason for the crash, and I don't have the time right now to investigate.

      However, d3kod has a blog post about the port here: http://primalpond.wordpress.com/2013/02/26/rendering-text-in-opengl-2-0-es-on-android/

      Try asking there for now - I will also look into it when I have some time.

      Good luck :)

      Delete
    2. Thank you for the directions. If I find out a solution, I will post it back here.

      Delete
  25. Hi!!
    Im trying all day to make your magic stuff work but i cant.
    The problem that i encounter is that i see only the first letter of glText.draw( "Test String :)", 0, 0, 0 ); command on my screen.So only the letter "T" appears and some other random lines following.Am i missing something? Thanks in advance!

    ReplyDelete
    Replies
    1. Hey there,

      I'm assuming you are referring to the test application. I just added a screenshot to the post (at the bottom, just above the comments), which is what it looks like when I run it on the emulator. Could you post a screenshot and give some more info as to where you are running it (emulator/device, android version, etc), as well as which version of the example you are running (the original or one of the ones added by others).

      Delete
    2. Hey thanks for the quick reply! Problem solved! I was using gl.glEnableClientState(GL10.GL_COLOR_ARRAY); command ,to color my shapes and probably was messing with the letters too.I just used gl.glDisableClientState(GL10.GL_COLOR_ARRAY); after my shapes and it works like a charm. Thanks a lot for your solution!Keep it up!

      Delete
    3. Glad you got it figured out. Good luck!

      Delete
  26. hi Fractious,
    I hope you will update you code to make it works with unicode text.
    Thanks for your great word.

    ReplyDelete
    Replies
    1. Hey there,

      Unfortunately I have no plans to do a unicode version of this anytime soon. While it would certainly be possible to do, it would require a great deal of work as well as a very deep understanding of how the target language works in order to make an efficient rendering system. Unfortunately I just do not have the time for that right now.

      If you feel like implementing it yourself, here are some thoughts on how to go about it:
      * First you will need to have multiple textures because of the large set of possible unicode characters. You will also need to be able to determine which texture holds which character - using a formula and/or lookup table. This alone complicates things quite dramatically when it comes to rendering. However, depending on the language you use and how it works, you could minimize the amount of characters needed to only include the essentials.
      * When you render using multiple textures, you will need to have a vertex buffer for each texture; this way you can switch textures only once per batch, instead of switching them for every character (which would kill render performance instantly). The idea is that in the draw call, you calculate the position of each character, determine on which texture it is, and then add it to the matching vertex buffer for that texture. Once the buffers are filled you then render the "batch" by setting a texture and rendering all characters for that texture from it's matching vertex buffer. Then you switch to the next texture and it's vertex buffer and render those, and so on until you have rendered all characters.
      * This approach would certainly work, but I suspect that there might be a great deal of optimization possible by having a good understanding of the target language.

      If you run into any problems with the implementation, feel free to ask and I will try to help.

      Good luck!

      Delete
  27. I am working with a small Games using some Unicode text.

    Thanks

    ReplyDelete
  28. This comment has been removed by the author.

    ReplyDelete
  29. Dear Fractious,
    Thanks for your quickly reply.
    It'll help me a lot later. At this time, I will temporary postpone this task to finish others.

    I will follow up your instruction to "implements" unicode when everything else OK.

    Regards,

    ReplyDelete
  30. Thanks for the code, very useful,

    BTW, without implementing unicode support, I was able to render more characters (ie those with accents) by just setting CHAR_END to 255.

    ReplyDelete
  31. Thanks for the code but Im doing a 3d bar graph using openGL and I want to show some text below the bar graph
    im having problem with this code

    glText.begin(1.0f, 1.0f, 1.0f, 1.0f);

    whenever im using this code my bar graph disappears.
    i have scaled 4 cube as the bars and i have drawed those 4 cubes in a for loop after that im drawing the text but it is not shown.

    please help me out

    ReplyDelete
    Replies
    1. Hey there,

      Do you have a matching glText.end() call?

      If you do, your problem is related to the OpenGL state machine. I'm guessing you do not explicitly set some state which is modified in the glText.begin() call. This call sets the following states:

      * gl.glColor4f() - sets render color, but it is reset to white with full alpha in the glText.end() call
      * gl.glBindTexture() - to bind the font texture
      * gl.glEnableClientState()/gl.glDisableClientState()
      * gl.glVertexPointer()
      * gl.glColorPointer()
      * gl.glTexCoordPointer()
      * gl.glNormalPointer()

      If you are using **ANY** of these for your own rendering, be sure to explicitly set them before performing your rendering. The call to glText.begin() will alter these and they will persist **until you manually alter them** (even across frames).

      Hope it helps :)

      Delete
    2. i have gl.glColor4f() and gl.glBindTexture() in that method i have done as per your code only if I'm executing your project only then its working fine but truely speaking i don't need a texture to show my text below the bar graph

      i need the color only, i even commented out the gl.glBindTexture() but the text is not showing and my back ground is not white

      Delete
    3. You can NOT remove the texture binding since the texture is where the actual font characters are created - doing so is like NOT calling the text rendering at all :)

      Do NOT alter the text rendering code (unless you know what you are doing) - rather make sure that you are properly setting the states for the rendering you do yourself.

      This is the nature of OpenGL, you have to understand how/where to set various states and NEVER assume that some state is set or not, especially if you are using code (such as this text renderer) that you did not write yourself.

      Delete
    4. but i have already told you that I'm rendering 4 cubes as bars (which doesn't have any texture effect) for the bar graph i have create them in a for loop just after that I'm doing your code
      It's possible that may be the bars are going out of the view because of this but neither is the text shown at that time.


      code:

      translatex = 3.5f;
      scale = 10;
      for (int i = 0; i < vals.length; i++) {
      gl.glPushMatrix();
      Log.i("scale", "val[i]= "+String.valueOf(i)+" "+String.valueOf(scale));
      mTransY = -scale + (scaly * vals[i]);
      gl.glTranslatef((-5.0f + (translatex * i)), mTransY, translatez);
      gl.glRotatef(mAngleX, 1.0f, 1.0f, 1.0f);
      gl.glScalef(0.75f, scaly * vals[i], 0.75f);
      gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
      // gl.glEnableClientState(GL10.GL_COLOR_ARRAY);
      mfcube[i].draw(gl);
      gl.glPopMatrix();
      }
      glText.begin(1.0f, 1.0f, 1.0f, 1.0f); // Begin Text Rendering (Set Color WHITE)

      WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
      Display display = wm.getDefaultDisplay();
      Point size=new Point();
      display.getSize(size);
      // glText.draw("3D Bar Graph", size.x/2, size.y/2); // Draw Test String
      glText.draw("3D Bar Graph", 0, 0); // Draw Test String
      glText.end(); // End Text Rendering

      Delete
    5. I don't see you setting the vertices to use, you only enable the vertex array client state. It could be that you set the vertices to use outside of the render loop - and since the text rendering uses it's own vertices the vertices you set are no longer valid after the first time the text rendering is done.

      I can not stress this enough - EVERY state you NEED for your rendering must be explicitly set every iteration before you do the rendering. Of course this is oversimplified as some states will not change for the life of your app, but for the sake of finding a problem, it is best to add all the states and remove them one by one until you find the culprit.

      Does the text render when you comment out ALL your bar rendering code? If not, then it is could be a problem with the way your view and/or camera is set up. Or even a problem with depth buffering.

      Delete
    6. my cube vertices are set in another class mfcube is a array object of that class

      if text rendering uses different set of vertices of its own then the bars shouldn't be visible at all but the bar is shown perfectly but the text can't be seen

      and yes the text can't be seen even if the bars are commented out

      i didnt set any camera angle for the bars so it must be using the default camera setup

      Delete
    7. Did you enable alpha blending and texture mapping as instructed in the "Usage & Notes" section of the tutorial? As mentioned, this is absolutely critical in order for the text to be displayed.

      Other than that, what about your projection. This system assumes that you are using an orthographic projection (again, see the tutorial example code). If you are using perspective projection you may need to make some changes to the text rendering code - these have been discussed in the comments above, and a version of the text renderer that uses 3D coordinates is also provided in the download section.

      Delete
  32. This comment has been removed by the author.

    ReplyDelete
  33. How is it possible to rotate the textual drawing if in landscape for example. I cannot get glRotate to work.

    ReplyDelete
    Replies
    1. The example SpriteBatch generates the vertices for the quad directly at render-time. In order to rotate these you will need to create an overloaded drawSprite() method that does the rotation while generating the vertices. Here is a very quick example of what that would look like:
      ---------
      public void drawSprite(float x, float y, float angle, float width, float height, TextureRegion region) {
      if ( numSprites == maxSprites ) {
      endBatch();
      numSprites = 0;
      bufferIndex = 0;
      }

      float halfWidth = width / 2.0f; // Half Width
      float halfHeight = height / 2.0f; // Half Height

      float rad = angle * ( ( 1.0f / 180.0f ) * (float)Math.PI ); // Get Angle in Radians
      float cos = FloatMath.cos( rad ); // Cosine of Angle
      float sin = FloatMath.sin( rad ); // Sine of Angle

      float halfWidthCos = halfWidth * cos; // Cosine of Half Width
      float halfWidthSin = halfWidth * sin; // Sine of Half Width
      float halfHeightCos = halfHeight * cos; // Cosine of Half Height
      float halfHeightSin = halfHeight * sin; // Sine of Half Height

      vertexBuffer[bufferIndex++] = ( -halfWidthCos ) - ( -halfHeightSin ) + x;
      vertexBuffer[bufferIndex++] = ( -halfWidthSin ) + ( -halfHeightCos ) + y;
      vertexBuffer[bufferIndex++] = r;
      vertexBuffer[bufferIndex++] = g;
      vertexBuffer[bufferIndex++] = b;
      vertexBuffer[bufferIndex++] = a;
      vertexBuffer[bufferIndex++] = region.u1;
      vertexBuffer[bufferIndex++] = region.v2;

      vertexBuffer[bufferIndex++] = ( halfWidthCos ) - ( -halfHeightSin ) + x;
      vertexBuffer[bufferIndex++] = ( halfWidthSin ) + ( -halfHeightCos ) + y;
      vertexBuffer[bufferIndex++] = r;
      vertexBuffer[bufferIndex++] = g;
      vertexBuffer[bufferIndex++] = b;
      vertexBuffer[bufferIndex++] = a;
      vertexBuffer[bufferIndex++] = region.u2;
      vertexBuffer[bufferIndex++] = region.v2;

      vertexBuffer[bufferIndex++] = ( halfWidthCos ) - ( halfHeightSin ) + x;
      vertexBuffer[bufferIndex++] = ( halfWidthSin ) + ( halfHeightCos ) + y;
      vertexBuffer[bufferIndex++] = r;
      vertexBuffer[bufferIndex++] = g;
      vertexBuffer[bufferIndex++] = b;
      vertexBuffer[bufferIndex++] = a;
      vertexBuffer[bufferIndex++] = region.u2;
      vertexBuffer[bufferIndex++] = region.v1;

      vertexBuffer[bufferIndex++] = ( -halfWidthCos ) - ( halfHeightSin ) + x;
      vertexBuffer[bufferIndex++] = ( -halfWidthSin ) + ( halfHeightCos ) + y;
      vertexBuffer[bufferIndex++] = r;
      vertexBuffer[bufferIndex++] = g;
      vertexBuffer[bufferIndex++] = b;
      vertexBuffer[bufferIndex++] = a;
      vertexBuffer[bufferIndex++] = region.u1;
      vertexBuffer[bufferIndex++] = region.v1;

      numSprites++; // Increment Sprite Count
      }
      --------

      In the above method, "angle" should be specified in DEGREES (it is converted to radians). This only performs rotation around the z-axis (since the vertices are 2D. The rotation will also be around the origin (top/left) of the text, not the center.

      Delete
  34. Has anyone had any luck making the background of this utility transparent? I've had no success.

    ReplyDelete
    Replies
    1. What exactly are you trying to make transparent? The text in the example is rendered with transparent background. In fact the entire texture map that is generated from the font file contains only alpha (transparency) information and the rendering code fills that with a specified color at render time. The example is not a utility as such, just a quick app to illustrate the usage of the class.

      Delete
    2. The surface view stacked behind the text, I am able to apply colour values, but it will not respond to alpha values.

      Delete
    3. Oh, I see. I'm not sure why though - I suppose if you want to use one surface over another, but in the example it's just a single surface and using transparency won't really do much since there is nothing "behind" it.

      Anyways, have you tried changing the alpha value for glClearColor() in onSurfaceCreated()? It's in the TexampleRenderer.java file.

      Delete
    4. I do intend for there to be "Nothing" behind it, eventually this will be placed on top of other UI elements.

      I have modified the clear color to " 0.0f, 0.0f, 0.0f, 0.01f " (Black, almost totally transparent) but I see no change. I am using my own renderer (Based off of yours), but I experience the same issues with the renderer included with your example.

      Could it simply be an android issue?

      Delete
    5. Ok now I understand :)

      By default the GLSurfaceView has a pixel format of RGB_888 so there is no alpha channel. You will need to set it to be TRANSLUCENT. This can be achieved by calling:

      getHolder().setFormat(PixelFormat.TRANSLUCENT)

      See here:

      http://developer.android.com/reference/android/opengl/GLSurfaceView.html

      under the "Specifying the android.view.Surface" heading :)

      Delete
    6. I have the same question: i draw the line chart with your text rendering. I'm trying to overcome the problem of the black background flash in OnResume method. I use PixelFormat.TRANSLUCENT and setZOrderOnTop(true), but it breaks the alpha channel and all pixels with an alpha value less than 1 turn white. In chart i use shader antialiasing for lines and circles. And text lose alpha channel.

      Good:https://www.dropbox.com/s/xy6n0ym4fd27aqy/Good.png
      Bad:https://www.dropbox.com/s/w28b90ien9s2waf/Bad.png

      Can you help me?

      Delete
    7. Unfortunately this is not an area I have that much experience with as I have never needed to use two surfaces which overlap. However, after a quick search I found the following link on SO:

      http://stackoverflow.com/questions/12556516/cannot-place-glsurfaceview-over-surfaceview

      They mention using a TextureView instead of a SurfaceView...maybe give that a try. This works on Android 4.0+ only unfortunately.

      Delete
    8. This comment has been removed by the author.

      Delete
    9. You are gangsters? No we are russian!!! Спасибо)))

      Delete
    10. I got it, I needed to make the following changes to my implementation to make it work:

      When setting the renderer:
      setEGLConfigChooser(8,8,8,8,16,0);
      setRenderer( new tickerRenderer( context, rssTickerString, textFont, rssFontSize, width, rssBackgroundColour, rssTextColour, widgetZoneW, opacity));
      getHolder().setFormat(PixelFormat.TRANSLUCENT);
      setZOrderMediaOverlay(true);
      setZOrderOnTop(true);

      and in the renderer, I removed the glClearColor from onSurfaceCreated. This resulted in rendered text with transparent background.

      Delete
  35. This comment has been removed by the author.

    ReplyDelete
  36. Great work! It work just great on my application.
    Spaciba from Israel!

    ReplyDelete
    Replies
    1. You are most welcome :) Good luck with your app!

      Delete