Android opengl-es fast way to render text - android

I want to show some text in opengl ES. I have a 512*512 font texture (texture atlas), all letter is 32*32 pixel here.
My text length is about 400 char.
My algorithm
opengl.setClearTransparentBGEnabled();
float y2=0;
float j =0;
for (int i=0; i<text.length(); i++) {
int ch =(int)text.charAt(i);
float x2=((float)j*16*scale/50);
j++;
if ((text.charAt(i)+"").equals("\n")) {
y2+=(16*scale*2)/50;
j=0;
x2=0;
}
opengl.saveMatrix();
Sprites.selectVertex("font"+name)
.setSprite(ch)
.translate(x-x2, y+y2, -9)
.scale(scale, scale, scale)
.rotate(90, 0, 0, 1)
.draw(true);
opengl.loadMatrix();
}
opengl.setClearTransparentBGDisabled();
My only probleme, this method is very slow: after this i get 15-20 FPS.
What is the best way, to render texts in opengl-es dynamically?

That's far too much work to be doing per-frame.
I'd use the 2D APIs to Canvas.drawText() (or drawBitmap, if you're not using a real font) the 400 chars to a private Bitmap, and use that as my texture.

You are repeating a lot of work every frame and for every character in the text. You should calculate all of the vertex and triangle data for a given string, and then submit it to opengl in one batch. Reuse the data for as long as the string stays the same.

Related

Android - Issue in rendering bulk points using Android plot

I am working on a requirement to display the luma value of raw YUV image(1280 x 720) as graph. That is, I am separating the Y data and displaying it in the form of graph, in which x axis is the width and the y axis is the respective Y value.
// Code
int count = 0;
int byteValue = 0;
for ( y = 0; y < height; y++) {
for (x = 0; x < width; x++) {
byteValue = pPictureIn[count++] & 0xff;
series.addLast(x, byteValue);
}
}
final PlotStatistics stats = new PlotStatistics(10, false);
plot.addListener(stats);
redrawer = new Redrawer(Arrays.asList(new Plot[]{plot}),
1, false);
format = new LineAndPointFormatter(this, R.xml.formatter);
plot.addSeries(series, format);
redrawer.start();
I am using AndroidPlot to plot the graph. And I am adding all the points to the series. Here my problem is, if I try to render the points, My app gets freezed. And I am using the render mode as USE_BACKGROUND_THREAD.
Someone please help me to render the points at one shot without any freeze. Thanks in advance
Im going to guess that you're using SimpleXYSeries, which is not optimized for efficiency or speed; the calls to addLast become extremely expensive as the number of points increase. Using a fixed memory XYSeries implementation will provide far better performance. If your image data is dynamic (coming from a camera or some other image stream) then a ring buffer might be a good design to consider...I'd suggest taking a look at FixedSizeEditableXYSeries in particular.
Additionally, you might consider sampling your data to reduce the size using SampledXYSeries.
The Advanced XY Series Types doc has more details about the pros and cons of the above mentioned classes and a few others.

LibGDX - ShapeRenderer is too slow / Low FPS on Android

I just wrote Conway's Game of Life in Java and i wanted to test my application on my android phone (Nexus 4).
For testing, i draw a BitmapFont on my SpriteBatch showing me FPS, gyro, cells alive and other data.
On the PC, i have ~ 500 to 4000 FPS, depending on how fast my GOL-logic works. However, if i start it on android, the FPS drop to 10-15. If I turn off my ShapeRenderer (which is my main Class to render all the GOL-Rectangles) with the GOL-logic at almost full-speed, the FPS is constantly at 60 (i guess you can't turn off VSync on a Nexus 4).
So, here is my question:
Why is the ShapeRenderer so highly inefficient? What else should i use to render my Shapes?
I will post my (fairly simple) render code and a picture:
this.setColor(new Color(1, 0, 0, 1));
for (int i = 0; i < grid.getX_length(); i++) {
for (int j = 0; j < grid.getY_length(); j++) {
if (cells[i][j].getState()){ // checks if the cell is dead or alive
this.setColor(new Color (0,0,0,1));
rect(i * size, j * size, size-gap, size-gap); // draws it.
}
(http://s14.directupload.net/images/140929/qc3uu8ew.png)
I don't know about the rectangles but I experienced the same Probelem with Circles. The Reoson that it takes so much time to draw a circle is that it draws a lot of single lines to form a circle.
See the source code for reference:
https://github.com/libgdx/libgdx/blob/master/gdx/src/com/badlogic/gdx/graphics/glutils/ShapeRenderer.java#L833

Changing colors from specific parts of a bitmap image

I am writing an Android application that must paint determined parts of a loaded bitmap image according to received events.
I need to paint (or change the current color) of a single part of a bitmap image, without changing the rest of the image.
Let's say I have a car, which is divided by many parts: door, windows, wheels, etc.
Each time an event (received from the network) arrives, I need to change the color of that particular part with the color specified by the event data.
What would be the best technique to achieve that?
I first thought on FloodFill, as suggested on many threads in SO, but given that the messages are received quite fast (several per second) I fear it would drag performance down, as it seem to be very CPU intensive algorithm.
I also thought about having multiple segments of the same image, each colored with a different color and show the right one at the right time, but the car has at least 10 different parts and each one could be painted with 4-6 colors, so I would end up with dozens of images and that would be impractical to handle, not to mention the waste of memory.
So, is there any other approach?
The fastest way to do it is with a shader. You'll need to use OpenGL ES 2 for that (some Androids only support ES 1). You'll need a temporary bitmap the same size as the image you want to change. Set it as the target. In the shader, retrieve a pixel from the sampler which is bound to the image you want to change. If it's within a small tolerance of the colour you want to change, set gl_FragColor to the new colour, otherwise just set gl_FragColor to the colour you retrieved from the sampler. You'll need to pass the desired colour and the new colour into the shader as vec4s with al_set_shader_float_vector. The fastest way to do this is to keep 2 bitmaps and swap between them as the "main one" that you're using each time a colour changes.
If you can't use a shader, then you'll have to lock the bitmap and replace the colour. Use al_lock_bitmap to lock it, then you can use al_get_pixel and al_put_pixel to change colours. Then al_unlock_bitmap when you're done. You can also avoid using al_get_pixel/al_put_pixel and access the memory manually which will be faster. If you lock the bitmap with the format ALLEGRO_PIXEL_FORMAT_ABGR_8888_LE then the memory is laid out like so:
int w = al_get_bitmap_width(bitmap);
int h = al_get_bitmap_height(bitmap);
for (int y = 0; y < h; y++) {
unsigned char *p = locked_region->data + locked_region->pitch * y;
for (int x = 0; x < w; x++) {
unsigned char r = p[0];
unsigned char g = p[1];
unsigned char b = p[2];
unsigned char a = p[3];
/* change r, g, b, a here if they match */
p[0] = r;
p[1] = g;
p[2] = b;
p[3] = a;
p += 4;
}
}
It's recommended that you lock the image in the format it was created in. That means pick an easy one like the one I mentioned, or else the inner part of the loop gets more complicated. The ABGR_8888 part of the pixel format describes the layout of the data. ABGR tells the order of the components. If you were to read a pixel into a single storage unit (an int in this case but it works the same with a short) then the bit pattern would be AAAAAAAABBBBBBBBGGGGGGGGRRRRRRRR. However, when you're reading a byte at a time, most machine are little endian so that means the small end comes first. That's why in my sample code p[0] is red. The 8888 part tells how many bits per component.

Android, using Bitmap (NDK + alpha bytes): i miss something?

I come from the Qt world and i am porting an application to Android. I am bit confused, i am banging my head for a few days now on something that must be so trivial that i cannot find why it's not working.
Some background: i have a C++ engine which i use trough NDK and JNI. This engine creates some bitmaps and passes them to the Java side, the Java side must display them on a View and let the user interact with them (drag and such).
The engine works properly, because i use it under Qt with full success. This is the workflow:
1- Java loads a big Bitmap from a custom data file (the C++ engine expects it to be in ARGB format, but it's compressed JPG data)
Bitmap.Config fmt = Bitmap.Config.ARGB_8888;
Bitmap bitmap = BitmapFactory.decodeByteArray(buffer, 0, size).copy( fmt , false);
2- initialize the C++ engine passing the bitmap. The C++ engine "breaks" the bitmap in smaller tiles. For tile it builds a rather complex alpha mask and stores it into the first byte of the bitmap (the "a" byte). This alpha mask only uses two values: 0xFF for opaque and 0x00 for transparent.
init_C_engine( this.fullImage );
3- The Java side then allocates all the tiles bitmaps, i do in two steps because before init i dont know which size will the tiles be. The engine will populate the tile_width and tile_height arrays:
Bitmap.Config fmt = Bitmap.Config.ARGB_8888;
for (int t = 0; t < this.puzzle_size; t++ ){
tile_data[ t ] = Bitmap.createBitmap( tile_width[t], tile_height[t], fmt);
4- Last step,inside the C++ engine, all the tiles bitmaps are filled:
for ( int n = 0; n < nBitmaps; n++ )
{
jobject bitmap = env->GetObjectArrayElement( bitmaps, n );
AndroidBitmap_getInfo(env, bitmap, &info);
AndroidBitmap_lockPixels(env, bitmap, reinterpret_cast<void **>(&pixels));
game->getTileBitmap( n, (unsigned char*)pixels );
AndroidBitmap_unlockPixels(env, bitmap);
env->SetObjectArrayElement( bitmaps, n, bitmap );
}
}
Now, in my custom View:
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.BLACK);
for ( int tile = 0; tile < board.nTiles; tile++ ){
canvas.drawBitmap( tile_data[tile],
tile_x[tile],
tile_y[tile], paint);
}
}
What i expect is that on my View i see my tiles with transparent areas. what i get instead is a weird behaviour so that on the black background i see the ENTIRE tile like the alpha bytes are all set to opaque, but when i move the tiles one of top of the other, the "transparent" areas get combined in some strange way, like colors are "xor"ed or multiplied in some way! When i move one tile on the other i can see the areas where the alpha bytes are set to transparent but colors gets mangled instead of being transparend!
Basically i expect that pixels having alpha set to 0 are drawn as transparent... i looked on internet but i could not find anything usefull to help me out....
Does somebody have ideas? Anything will be appreciated!
thanks.
Shouldn't you use the index t instead of tile inside the for loop inside onDraw? Like this:
canvas.drawBitmap(tile_data[t], tile_x[t], tile_y[t], paint);

Draw text in OpenGL ES

I'm currently developing a small OpenGL game for the Android platform and I wonder if there's an easy way to render text on top of the rendered frame (like a HUD with the player´s score etc). The text would need to use a custom font also.
I've seen an example using a View as an overlay, but I don't know if I want to do that since I might want to port the game to other platforms later.
Any ideas?
Rendering text to a texture is simpler than what the Sprite Text demo make it looks like, the basic idea is to use the Canvas class to render to a Bitmap and then pass the Bitmap to an OpenGL texture:
// Create an empty, mutable bitmap
Bitmap bitmap = Bitmap.createBitmap(256, 256, Bitmap.Config.ARGB_4444);
// get a canvas to paint over the bitmap
Canvas canvas = new Canvas(bitmap);
bitmap.eraseColor(0);
// get a background image from resources
// note the image format must match the bitmap format
Drawable background = context.getResources().getDrawable(R.drawable.background);
background.setBounds(0, 0, 256, 256);
background.draw(canvas); // draw the background to our bitmap
// Draw the text
Paint textPaint = new Paint();
textPaint.setTextSize(32);
textPaint.setAntiAlias(true);
textPaint.setARGB(0xff, 0x00, 0x00, 0x00);
// draw the text centered
canvas.drawText("Hello World", 16,112, textPaint);
//Generate one texture pointer...
gl.glGenTextures(1, textures, 0);
//...and bind it to our array
gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]);
//Create Nearest Filtered Texture
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);
//Different possible texture parameters, e.g. GL10.GL_CLAMP_TO_EDGE
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_REPEAT);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_REPEAT);
//Use the Android GLUtils to specify a two-dimensional texture image from our bitmap
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
//Clean up
bitmap.recycle();
The Android SDK doesn't come with any easy way to draw text on OpenGL views. Leaving you with the following options.
Place a TextView over your SurfaceView. This is slow and bad, but the most direct approach.
Render common strings to textures, and simply draw those textures. This is by far the simplest and fastest, but the least flexible.
Roll-your-own text rendering code based on a sprite. Probably second best choice if 2 isn't an option. A good way to get your feet wet but note that while it seems simple (and basic features are), it get's harder and more challenging as you add more features (texture-alignment, dealing with line-breaks, variable-width fonts etc.) - if you take this route, make it as simple as you can get away with!
Use an off-the-shelf/open-source library. There are a few around if you hunt on Google, the tricky bit is getting them integrated and running. But at least, once you do that, you'll have all the flexibility and maturity they provide.
I've written a tutorial that expands on the answer posted by JVitela. Basically, it uses the same idea, but instead of rendering each string to a texture, it renders all characters from a font file to a texture and uses that to allow for full dynamic text rendering with no further slowdowns (once the initialization is complete).
The main advantage of my method, compared to the various font atlas generators, is that you can ship small font files (.ttf .otf) with your project instead of having to ship large bitmaps for every font variation and size. It can generate perfect quality fonts at any resolution using only a font file :)
The tutorial includes full code that can be used in any project :)
According to this link:
http://code.neenbedankt.com/how-to-render-an-android-view-to-a-bitmap
You can render any View to a bitmap. It's probably worth assuming that you can layout a view as you require (including text, images etc.) and then render it to a Bitmap.
Using JVitela's code above you should be able to use that Bitmap as an OpenGL texture.
Take a look at CBFG and the Android port of the loading/rendering code. You should be able to drop the code into your project and use it straight away.
CBFG - http://www.codehead.co.uk/cbfg
Android loader - http://www.codehead.co.uk/cbfg/TexFont.java
I looked at the sprite text example and it looks awfully complicated for such a task, I considered rendering to a texture too, but I'm worried about the performance hit that might cause.
I might just have to go with a view instead and worry about porting when it's time to cross that bridge :)
IMHO there are three reasons to use OpenGL ES in a game:
Avoid differences between mobile platforms by using an open standard;
To have more control of the render process;
To benefit from GPU parallel processing;
Drawing text is always a problem in game design, because you are drawing things, so you cannot have the look and feel of a common activity, with widgets and so on.
You can use a framework to generate Bitmap fonts from TrueType fonts and render them. All the frameworks I've seen operate the same way: generate the vertex and texture coordinates for the text in draw time. This is not the most efficient use of OpenGL.
The best way is to allocate remote buffers (vertex buffer objects - VBOs) for the vertices and textures early in code, avoiding the lazy memory transfer operations in draw time.
Keep in mind that game players don't like to read text, so you won't write a long dynamically generated text. For labels, you can use static textures, leaving dynamic text for time and score, and both are numeric with a few characters long.
So, my solution is simple:
Create texture for common labels and warnings;
Create texture for numbers 0-9, ":", "+", and "-". One texture for each character;
Generate remote VBOs for all positions in the screen. I can render static or dynamic text in that positions, but the VBOs are static;
Generate just one Texture VBO, as text is always rendered one way;
In draw time, I render the static text;
For dynamic text, I can peek at the position VBO, get the character texture and draw it, a character at a time.
Draw operations are fast, if you use remote static buffers.
I create an XML file with screen positions (based on screen's diagonal percentage) and textures (static and characters), and then I load this XML before rendering.
To get a high FPS rate, you should avoid generating VBOs at draw time.
Look at the "Sprite Text" sample in the GLSurfaceView samples.
If you insist on using GL, you could render the text on to textures. Assuming that most of the HUD is relatively static, you shouldn't have to load the textures to texture memory too often.
Take a look at CBFG and the Android port of the loading/rendering
code. You should be able to drop the code into your project and use it
straight away.
CBFG
Android loader
I have problems with this implementation. It displays only one character, when I try do change size of the font's bitmap (I need special letters) whole draw fails :(
I have been looking for this for a few hours, this was the first article i came accross and although it has the best answer, the most popular answers i think are off the mark. Certainly for what i needed.
weichsel's and shakazed's answers were right on the button but a bit obscured in the articles.
To put you right to the project. Here:
Just create a new Android project based on existing sample. Choose ApiDemos:
Look under the source folder
ApiDemos/src/com/example/android/apis/graphics/spritetext
And you will find everything you need.
For static text:
Generate an image with all words used on your PC (For example with GIMP).
Load this as a texture and use it as material for a plane.
For long text that needs to be updated once in a while:
Let android draw on a bitmap canvas (JVitela's solution).
Load this as material for a plane.
Use different texture coordinates for each word.
For a number (formatted 00.0):
Generate an image with all numbers and a dot.
Load this as material for a plane.
Use below shader.
In your onDraw event only update the value variable sent to the shader.
precision highp float;
precision highp sampler2D;
uniform float uTime;
uniform float uValue;
uniform vec3 iResolution;
varying vec4 v_Color;
varying vec2 vTextureCoord;
uniform sampler2D s_texture;
void main() {
vec4 fragColor = vec4(1.0, 0.5, 0.2, 0.5);
vec2 uv = vTextureCoord;
float devisor = 10.75;
float digit;
float i;
float uCol;
float uRow;
if (uv.y < 0.45) {
if (uv.x > 0.75) {
digit = floor(uValue*10.0);
digit = digit - floor(digit/10.0)*10.0;
i = 48.0 - 32.0 + digit;
uRow = floor(i / 10.0);
uCol = i - 10.0 * uRow;
fragColor = texture2D( s_texture, uv / devisor * 2.0 + vec2((uCol-1.5) / devisor, uRow / devisor) );
} else if (uv.x > 0.5) {
uCol = 4.0;
uRow = 1.0;
fragColor = texture2D( s_texture, uv / devisor * 2.0 + vec2((uCol-1.0) / devisor, uRow / devisor) );
} else if (uv.x > 0.25) {
digit = floor(uValue);
digit = digit - floor(digit/10.0)*10.0;
i = 48.0 - 32.0 + digit;
uRow = floor(i / 10.0);
uCol = i - 10.0 * uRow;
fragColor = texture2D( s_texture, uv / devisor * 2.0 + vec2((uCol-0.5) / devisor, uRow / devisor) );
} else if (uValue >= 10.0) {
digit = floor(uValue/10.0);
digit = digit - floor(digit/10.0)*10.0;
i = 48.0 - 32.0 + digit;
uRow = floor(i / 10.0);
uCol = i - 10.0 * uRow;
fragColor = texture2D( s_texture, uv / devisor * 2.0 + vec2((uCol-0.0) / devisor, uRow / devisor) );
} else {
fragColor = vec4(0.0, 0.0, 0.0, 0.0);
}
} else {
fragColor = vec4(0.0, 0.0, 0.0, 0.0);
}
gl_FragColor = fragColor;
}
Above code works for a texture atlas where numbers start from 0 at the 7th column of the 2nd row of the font atlas (texture).
Refer to https://www.shadertoy.com/view/Xl23Dw for demonstration (with wrong texture though)
In the OpenGL ES 2.0/3.0 you can also combining OGL View and Android's UI-elements:
public class GameActivity extends AppCompatActivity {
private SurfaceView surfaceView;
#Override
protected void onCreate(Bundle state) {
setContentView(R.layout.activity_gl);
surfaceView = findViewById(R.id.oglView);
surfaceView.init(this.getApplicationContext());
...
}
}
public class SurfaceView extends GLSurfaceView {
private SceneRenderer renderer;
public SurfaceView(Context context) {
super(context);
}
public SurfaceView(Context context, AttributeSet attributes) {
super(context, attributes);
}
public void init(Context context) {
renderer = new SceneRenderer(context);
setRenderer(renderer);
...
}
}
Create layout activity_gl.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
tools:context=".activities.GameActivity">
<com.app.SurfaceView
android:id="#+id/oglView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<TextView ... />
<TextView ... />
<TextView ... />
</androidx.constraintlayout.widget.ConstraintLayout>
To update elements from the render thread, can use Handler/Looper.

Categories

Resources