FileDocCategorySizeDatePackage
LabelMaker.javaAPI DocAndroid 1.5 API14264Wed May 06 22:41:08 BST 2009com.example.android.apis.graphics.spritetext

LabelMaker

public 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.

param
fullColor true if we want a full color backing store (4444), otherwise we generate a grey L8 backing store.
param
strikeWidth width of strike
param
strikeHeight height of strike

        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 intadd(javax.microedition.khronos.opengles.GL10 gl, java.lang.String text, android.graphics.Paint textPaint)
Call to add a label

param
gl
param
text the text of the label
param
textPaint the paint of the label
return
the id of the label, used to measure and draw the label

        return add(gl, null, text, textPaint);
    
public intadd(javax.microedition.khronos.opengles.GL10 gl, android.graphics.drawable.Drawable background, java.lang.String text, android.graphics.Paint textPaint)
Call to add a label

param
gl
param
text the text of the label
param
textPaint the paint of the label
return
the id of the label, used to measure and draw the label

        return add(gl, background, text, textPaint, 0, 0);
    
public intadd(javax.microedition.khronos.opengles.GL10 gl, android.graphics.drawable.Drawable drawable, int minWidth, int minHeight)
Call to add a label

return
the id of the label, used to measure and draw the label

        return add(gl, drawable, null, null, minWidth, minHeight);
    
public intadd(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

param
gl
param
text the text of the label
param
textPaint the paint of the label
return
the id of the label, used to measure and draw the 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 voidbeginAdding(javax.microedition.khronos.opengles.GL10 gl)
Call before adding labels. Clears out any existing labels.

param
gl

        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 voidbeginDrawing(javax.microedition.khronos.opengles.GL10 gl, float viewWidth, float viewHeight)
Begin drawing labels. Sets the OpenGL state for rapid drawing.

param
gl
param
viewWidth
param
viewHeight

        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 voidcheckState(int oldState, int newState)

        if (mState != oldState) {
            throw new IllegalArgumentException("Can't call this method now.");
        }
        mState = newState;
    
public voiddraw(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).

param
gl
param
x
param
y
param
labelID

        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 voidendAdding(javax.microedition.khronos.opengles.GL10 gl)
Call to end adding labels. Must be called before drawing starts.

param
gl

        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 voidendDrawing(javax.microedition.khronos.opengles.GL10 gl)
Ends the drawing and restores the OpenGL state.

param
gl

        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 floatgetBaseline(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.)

param
labelID
return
the baseline in pixels.

        return mLabels.get(labelID).baseline;
    
public floatgetHeight(int labelID)
Get the height in pixels of a given label.

param
labelID
return
the height in pixels

        return mLabels.get(labelID).height;
    
public floatgetWidth(int labelID)
Get the width in pixels of a given label.

param
labelID
return
the width in pixels

        return mLabels.get(labelID).width;
    
public voidinitialize(javax.microedition.khronos.opengles.GL10 gl)
Call to initialize the class. Call whenever the surface has been created.

param
gl

        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 voidshutdown(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;
            }
        }