LabelMakerpublic class LabelMaker extends Object An OpenGL text label maker.
OpenGL labels are implemented by creating a Bitmap, drawing all the labels
into the Bitmap, converting the Bitmap into an Alpha texture, and creating a
mesh for each label
The benefits of this approach are that the labels are drawn using the high
quality anti-aliased font rasterizer, full character set support, and all the
text labels are stored on a single texture, which makes it faster to use.
The drawbacks are that you can only have as many labels as will fit onto one
texture, and you have to recreate the whole texture if any label text
changes. |
Fields Summary |
---|
private int | mStrikeWidth | private int | mStrikeHeight | private boolean | mFullColor | private android.graphics.Bitmap | mBitmap | private android.graphics.Canvas | mCanvas | private android.graphics.Paint | mClearPaint | private int | mTextureID | private float | mTexelWidth | private float | mTexelHeight | private int | mU | private int | mV | private int | mLineHeight | private ArrayList | mLabels | private static final int | STATE_NEW | private static final int | STATE_INITIALIZED | private static final int | STATE_ADDING | private static final int | STATE_DRAWING | private int | mState |
Constructors Summary |
---|
public LabelMaker(boolean fullColor, int strikeWidth, int strikeHeight)Create a label maker
or maximum compatibility with various OpenGL ES implementations,
the strike width and height must be powers of two,
We want the strike width to be at least as wide as the widest window.
mFullColor = fullColor;
mStrikeWidth = strikeWidth;
mStrikeHeight = strikeHeight;
mTexelWidth = (float) (1.0 / mStrikeWidth);
mTexelHeight = (float) (1.0 / mStrikeHeight);
mClearPaint = new Paint();
mClearPaint.setARGB(0, 0, 0, 0);
mClearPaint.setStyle(Style.FILL);
mState = STATE_NEW;
|
Methods Summary |
---|
public int | add(javax.microedition.khronos.opengles.GL10 gl, java.lang.String text, android.graphics.Paint textPaint)Call to add a label
return add(gl, null, text, textPaint);
| public int | add(javax.microedition.khronos.opengles.GL10 gl, android.graphics.drawable.Drawable background, java.lang.String text, android.graphics.Paint textPaint)Call to add a label
return add(gl, background, text, textPaint, 0, 0);
| public int | add(javax.microedition.khronos.opengles.GL10 gl, android.graphics.drawable.Drawable drawable, int minWidth, int minHeight)Call to add a label
return add(gl, drawable, null, null, minWidth, minHeight);
| public int | add(javax.microedition.khronos.opengles.GL10 gl, android.graphics.drawable.Drawable background, java.lang.String text, android.graphics.Paint textPaint, int minWidth, int minHeight)Call to add a label
checkState(STATE_ADDING, STATE_ADDING);
boolean drawBackground = background != null;
boolean drawText = (text != null) && (textPaint != null);
Rect padding = new Rect();
if (drawBackground) {
background.getPadding(padding);
minWidth = Math.max(minWidth, background.getMinimumWidth());
minHeight = Math.max(minHeight, background.getMinimumHeight());
}
int ascent = 0;
int descent = 0;
int measuredTextWidth = 0;
if (drawText) {
// Paint.ascent is negative, so negate it.
ascent = (int) Math.ceil(-textPaint.ascent());
descent = (int) Math.ceil(textPaint.descent());
measuredTextWidth = (int) Math.ceil(textPaint.measureText(text));
}
int textHeight = ascent + descent;
int textWidth = Math.min(mStrikeWidth,measuredTextWidth);
int padHeight = padding.top + padding.bottom;
int padWidth = padding.left + padding.right;
int height = Math.max(minHeight, textHeight + padHeight);
int width = Math.max(minWidth, textWidth + padWidth);
int effectiveTextHeight = height - padHeight;
int effectiveTextWidth = width - padWidth;
int centerOffsetHeight = (effectiveTextHeight - textHeight) / 2;
int centerOffsetWidth = (effectiveTextWidth - textWidth) / 2;
// Make changes to the local variables, only commit them
// to the member variables after we've decided not to throw
// any exceptions.
int u = mU;
int v = mV;
int lineHeight = mLineHeight;
if (width > mStrikeWidth) {
width = mStrikeWidth;
}
// Is there room for this string on the current line?
if (u + width > mStrikeWidth) {
// No room, go to the next line:
u = 0;
v += lineHeight;
lineHeight = 0;
}
lineHeight = Math.max(lineHeight, height);
if (v + lineHeight > mStrikeHeight) {
throw new IllegalArgumentException("Out of texture space.");
}
int u2 = u + width;
int vBase = v + ascent;
int v2 = v + height;
if (drawBackground) {
background.setBounds(u, v, u + width, v + height);
background.draw(mCanvas);
}
if (drawText) {
mCanvas.drawText(text,
u + padding.left + centerOffsetWidth,
vBase + padding.top + centerOffsetHeight,
textPaint);
}
Grid grid = new Grid(2, 2);
// Grid.set arguments: i, j, x, y, z, u, v
float texU = u * mTexelWidth;
float texU2 = u2 * mTexelWidth;
float texV = 1.0f - v * mTexelHeight;
float texV2 = 1.0f - v2 * mTexelHeight;
grid.set(0, 0, 0.0f, 0.0f, 0.0f, texU , texV2);
grid.set(1, 0, width, 0.0f, 0.0f, texU2, texV2);
grid.set(0, 1, 0.0f, height, 0.0f, texU , texV );
grid.set(1, 1, width, height, 0.0f, texU2, texV );
// We know there's enough space, so update the member variables
mU = u + width;
mV = v;
mLineHeight = lineHeight;
mLabels.add(new Label(grid, width, height, ascent,
u, v + height, width, -height));
return mLabels.size() - 1;
| public void | beginAdding(javax.microedition.khronos.opengles.GL10 gl)Call before adding labels. Clears out any existing labels.
checkState(STATE_INITIALIZED, STATE_ADDING);
mLabels.clear();
mU = 0;
mV = 0;
mLineHeight = 0;
Bitmap.Config config = mFullColor ?
Bitmap.Config.ARGB_4444 : Bitmap.Config.ALPHA_8;
mBitmap = Bitmap.createBitmap(mStrikeWidth, mStrikeHeight, config);
mCanvas = new Canvas(mBitmap);
mBitmap.eraseColor(0);
| public void | beginDrawing(javax.microedition.khronos.opengles.GL10 gl, float viewWidth, float viewHeight)Begin drawing labels. Sets the OpenGL state for rapid drawing.
checkState(STATE_INITIALIZED, STATE_DRAWING);
gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureID);
gl.glShadeModel(GL10.GL_FLAT);
gl.glEnable(GL10.GL_BLEND);
gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
gl.glColor4x(0x10000, 0x10000, 0x10000, 0x10000);
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glPushMatrix();
gl.glLoadIdentity();
gl.glOrthof(0.0f, viewWidth, 0.0f, viewHeight, 0.0f, 1.0f);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glPushMatrix();
gl.glLoadIdentity();
// Magic offsets to promote consistent rasterization.
gl.glTranslatef(0.375f, 0.375f, 0.0f);
| private void | checkState(int oldState, int newState)
if (mState != oldState) {
throw new IllegalArgumentException("Can't call this method now.");
}
mState = newState;
| public void | draw(javax.microedition.khronos.opengles.GL10 gl, float x, float y, int labelID)Draw a given label at a given x,y position, expressed in pixels, with the
lower-left-hand-corner of the view being (0,0).
checkState(STATE_DRAWING, STATE_DRAWING);
gl.glPushMatrix();
float snappedX = (float) Math.floor(x);
float snappedY = (float) Math.floor(y);
gl.glTranslatef(snappedX, snappedY, 0.0f);
Label label = mLabels.get(labelID);
gl.glEnable(GL10.GL_TEXTURE_2D);
((GL11)gl).glTexParameteriv(GL10.GL_TEXTURE_2D,
GL11Ext.GL_TEXTURE_CROP_RECT_OES, label.mCrop, 0);
((GL11Ext)gl).glDrawTexiOES((int) snappedX, (int) snappedY, 0,
(int) label.width, (int) label.height);
gl.glPopMatrix();
| public void | endAdding(javax.microedition.khronos.opengles.GL10 gl)Call to end adding labels. Must be called before drawing starts.
checkState(STATE_ADDING, STATE_INITIALIZED);
gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureID);
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, mBitmap, 0);
// Reclaim storage used by bitmap and canvas.
mBitmap.recycle();
mBitmap = null;
mCanvas = null;
| public void | endDrawing(javax.microedition.khronos.opengles.GL10 gl)Ends the drawing and restores the OpenGL state.
checkState(STATE_DRAWING, STATE_INITIALIZED);
gl.glDisable(GL10.GL_BLEND);
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glPopMatrix();
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glPopMatrix();
| public float | getBaseline(int labelID)Get the baseline of a given label. That's how many pixels from the top of
the label to the text baseline. (This is equivalent to the negative of
the label's paint's ascent.)
return mLabels.get(labelID).baseline;
| public float | getHeight(int labelID)Get the height in pixels of a given label.
return mLabels.get(labelID).height;
| public float | getWidth(int labelID)Get the width in pixels of a given label.
return mLabels.get(labelID).width;
| public void | initialize(javax.microedition.khronos.opengles.GL10 gl)Call to initialize the class.
Call whenever the surface has been created.
mState = STATE_INITIALIZED;
int[] textures = new int[1];
gl.glGenTextures(1, textures, 0);
mTextureID = textures[0];
gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureID);
// Use Nearest for performance.
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_NEAREST);
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);
gl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE,
GL10.GL_REPLACE);
| public void | shutdown(javax.microedition.khronos.opengles.GL10 gl)Call when the surface has been destroyed
if ( gl != null) {
if (mState > STATE_NEW) {
int[] textures = new int[1];
textures[0] = mTextureID;
gl.glDeleteTextures(1, textures, 0);
mState = STATE_NEW;
}
}
|
|