Adding text metrics to renderscript.

Change-Id: Ica460525243d714a278e4ad5e436af43e1008e0c
diff --git a/java/Samples/src/com/android/samples/RsRenderStatesRS.java b/java/Samples/src/com/android/samples/RsRenderStatesRS.java
index a15c4a1..0990da3 100644
--- a/java/Samples/src/com/android/samples/RsRenderStatesRS.java
+++ b/java/Samples/src/com/android/samples/RsRenderStatesRS.java
@@ -84,8 +84,6 @@
     private Allocation mTexTransparent;
     private Allocation mTexChecker;
 
-    private Allocation mAllocPV;
-
     private Mesh mMbyNMesh;
     private Mesh mTorus;
 
@@ -95,7 +93,6 @@
     Font mFontSerifItalic;
     Font mFontSerifBoldItalic;
     Font mFontMono;
-
     private Allocation mTextAlloc;
 
     private ScriptC_rsrenderstates mScript;
@@ -267,12 +264,15 @@
         mFontSerifBoldItalic = Font.createFromFamily(mRS, mRes, "serif", Font.Style.BOLD_ITALIC, 8);
         mFontMono = Font.createFromFamily(mRS, mRes, "mono", Font.Style.NORMAL, 8);
 
+        mTextAlloc = Allocation.createFromString(mRS, "String from allocation");
+
         mScript.set_gFontSans(mFontSans);
         mScript.set_gFontSerif(mFontSerif);
         mScript.set_gFontSerifBold(mFontSerifBold);
         mScript.set_gFontSerifItalic(mFontSerifItalic);
         mScript.set_gFontSerifBoldItalic(mFontSerifBoldItalic);
         mScript.set_gFontMono(mFontMono);
+        mScript.set_gTextAlloc(mTextAlloc);
     }
 
     private void initMesh() {
diff --git a/java/Samples/src/com/android/samples/rsrenderstates.rs b/java/Samples/src/com/android/samples/rsrenderstates.rs
index b471504..77384ef 100644
--- a/java/Samples/src/com/android/samples/rsrenderstates.rs
+++ b/java/Samples/src/com/android/samples/rsrenderstates.rs
@@ -42,6 +42,7 @@
 rs_font gFontSerifItalic;
 rs_font gFontSerifBoldItalic;
 rs_font gFontMono;
+rs_allocation gTextAlloc;
 
 int gDisplayMode;
 
@@ -70,7 +71,7 @@
 #pragma rs export_var(gProgStoreBlendNoneDepth, gProgStoreBlendNone, gProgStoreBlendAlpha, gProgStoreBlendAdd)
 #pragma rs export_var(gTexOpaque, gTexTorus, gTexTransparent, gTexChecker)
 #pragma rs export_var(gMbyNMesh, gTorusMesh)
-#pragma rs export_var(gFontSans, gFontSerif, gFontSerifBold, gFontSerifItalic, gFontSerifBoldItalic, gFontMono)
+#pragma rs export_var(gFontSans, gFontSerif, gFontSerifBold, gFontSerifItalic, gFontSerifBoldItalic, gFontMono, gTextAlloc)
 #pragma rs export_var(gLinearClamp, gLinearWrap, gMipLinearWrap, gMipLinearAniso8, gMipLinearAniso15, gNearestClamp)
 #pragma rs export_var(gCullBack, gCullFront, gCullNone)
 #pragma rs export_var(gVSConstants, gFSConstants, gVSInputs, gProgVertexCustom, gProgFragmentCustom, gProgFragmentMultitex)
@@ -85,7 +86,7 @@
 
 void displayFontSamples() {
     rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f);
-    int yPos = 30;
+    int yPos = 100;
     rsgBindFont(gFontSans);
     rsgDrawText("Sans font sample", 30, yPos);
     yPos += 30;
@@ -107,6 +108,47 @@
     yPos += 30;
     rsgBindFont(gFontMono);
     rsgDrawText("Monospace font sample", 30, yPos);
+    yPos += 50;
+
+    // Now use text metrics to center the text
+    uint width = rsgGetWidth();
+    uint height = rsgGetHeight();
+    int left = 0, right = 0, top = 0, bottom = 0;
+
+    rsgFontColor(0.9f, 0.9f, 0.95f, 1.0f);
+    rsgBindFont(gFontSerifBoldItalic);
+
+    rsgMeasureText(gTextAlloc, &left, &right, &top, &bottom);
+    int centeredPos = width / 2 - (right - left) / 2;
+    rsgDrawText(gTextAlloc, centeredPos, yPos);
+    yPos += 30;
+
+    const char* text = "Centered Text Sample";
+    rsgMeasureText(text, &left, &right, &top, &bottom);
+    centeredPos = width / 2 - (right - left) / 2;
+    rsgDrawText(text, centeredPos, yPos);
+    yPos += 30;
+
+    rsgBindFont(gFontSans);
+    text = "More Centered Text Samples";
+    rsgMeasureText(text, &left, &right, &top, &bottom);
+    centeredPos = width / 2 - (right - left) / 2;
+    rsgDrawText(text, centeredPos, yPos);
+    yPos += 30;
+
+    // Now draw bottom and top right aligned text
+    text = "Top-right aligned text";
+    rsgMeasureText(text, &left, &right, &top, &bottom);
+    rsgDrawText(text, width - right, top);
+
+    text = "Top-left";
+    rsgMeasureText(text, &left, &right, &top, &bottom);
+    rsgDrawText(text, -left, top);
+
+    text = "Bottom-right aligned text";
+    rsgMeasureText(text, &left, &right, &top, &bottom);
+    rsgDrawText(text, width - right, height + bottom);
+
 }
 
 void bindProgramVertexOrtho() {
diff --git a/rsContext.cpp b/rsContext.cpp
index 6940033..493a092 100644
--- a/rsContext.cpp
+++ b/rsContext.cpp
@@ -258,14 +258,14 @@
     sprintf(buffer, "Frame %i ms, Script %i ms", mTimeMSLastFrame, mTimeMSLastScript);
     float oldR, oldG, oldB, oldA;
     mStateFont.getFontColor(&oldR, &oldG, &oldB, &oldA);
+    uint32_t bufferLen = strlen(buffer);
 
-    float shadowCol = 0.2f;
+    float shadowCol = 0.1f;
     mStateFont.setFontColor(shadowCol, shadowCol, shadowCol, 1.0f);
-    mStateFont.renderText(buffer, 5, getHeight() - 5);
+    mStateFont.renderText(buffer, bufferLen, 5, getHeight() - 5);
 
-    float textCol = 0.9f;
-    mStateFont.setFontColor(textCol, textCol, textCol, 1.0f);
-    mStateFont.renderText(buffer, 4, getHeight() - 6);
+    mStateFont.setFontColor(1.0f, 0.7f, 0.0f, 1.0f);
+    mStateFont.renderText(buffer, bufferLen, 4, getHeight() - 6);
 
     mStateFont.setFontColor(oldR, oldG, oldB, oldA);
 }
diff --git a/rsFont.cpp b/rsFont.cpp
index c516ea9..cc2b76a 100644
--- a/rsFont.cpp
+++ b/rsFont.cpp
@@ -84,34 +84,95 @@
     }
 }
 
-void Font::drawCachedGlyph(CachedGlyphInfo *glyph, int x, int y)
+void Font::drawCachedGlyph(CachedGlyphInfo *glyph, int32_t x, int32_t y)
 {
     FontState *state = &mRSC->mStateFont;
 
-    int nPenX = x + glyph->mBitmapLeft;
-    int nPenY = y - glyph->mBitmapTop + glyph->mBitmapHeight;
+    int32_t nPenX = x + glyph->mBitmapLeft;
+    int32_t nPenY = y - glyph->mBitmapTop + glyph->mBitmapHeight;
 
-    state->appendMeshQuad(nPenX, nPenY, 0,
-                            glyph->mBitmapMinU, glyph->mBitmapMaxV,
+    float u1 = glyph->mBitmapMinU;
+    float u2 = glyph->mBitmapMaxU;
+    float v1 = glyph->mBitmapMinV;
+    float v2 = glyph->mBitmapMaxV;
 
-                            nPenX + (int)glyph->mBitmapWidth, nPenY, 0,
-                            glyph->mBitmapMaxU, glyph->mBitmapMaxV,
+    int32_t width = (int32_t) glyph->mBitmapWidth;
+    int32_t height = (int32_t) glyph->mBitmapHeight;
 
-                            nPenX + (int)glyph->mBitmapWidth, nPenY - (int)glyph->mBitmapHeight, 0,
-                            glyph->mBitmapMaxU, glyph->mBitmapMinV,
-
-                            nPenX, nPenY - (int)glyph->mBitmapHeight, 0,
-                            glyph->mBitmapMinU, glyph->mBitmapMinV);
+    state->appendMeshQuad(nPenX, nPenY, 0, u1, v2,
+                          nPenX + width, nPenY, 0, u2, v2,
+                          nPenX + width, nPenY - height, 0, u2, v1,
+                          nPenX, nPenY - height, 0, u1, v1);
 }
 
-void Font::renderUTF(const char *text, uint32_t len, uint32_t start, int numGlyphs, int x, int y)
+void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int32_t x, int32_t y,
+                           uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH) {
+    int32_t nPenX = x + glyph->mBitmapLeft;
+    int32_t nPenY = y + glyph->mBitmapTop;
+
+    uint32_t endX = glyph->mBitmapMinX + glyph->mBitmapWidth;
+    uint32_t endY = glyph->mBitmapMinY + glyph->mBitmapHeight;
+
+    FontState *state = &mRSC->mStateFont;
+    uint32_t cacheWidth = state->getCacheTextureType()->getDimX();
+    const uint8_t* cacheBuffer = state->getTextTextureData();
+
+    uint32_t cacheX = 0, cacheY = 0;
+    int32_t bX = 0, bY = 0;
+    for (cacheX = glyph->mBitmapMinX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
+        for (cacheY = glyph->mBitmapMinY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
+            if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
+                LOGE("Skipping invalid index");
+                continue;
+            }
+            uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
+            bitmap[bY * bitmapW + bX] = tempCol;
+        }
+    }
+
+}
+
+void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int32_t x, int32_t y, Rect *bounds) {
+    int32_t nPenX = x + glyph->mBitmapLeft;
+    int32_t nPenY = y - glyph->mBitmapTop + glyph->mBitmapHeight;
+
+    int32_t width = (int32_t) glyph->mBitmapWidth;
+    int32_t height = (int32_t) glyph->mBitmapHeight;
+
+    if (bounds->bottom > nPenY) {
+        bounds->bottom = nPenY;
+    }
+    if (bounds->left > nPenX) {
+        bounds->left = nPenX;
+    }
+    if (bounds->right < nPenX + width) {
+        bounds->right = nPenX + width;
+    }
+    if (bounds->top < nPenY + height) {
+        bounds->top = nPenY + height;
+    }
+}
+
+void Font::renderUTF(const char *text, uint32_t len, int32_t x, int32_t y,
+                     uint32_t start, int32_t numGlyphs,
+                     RenderMode mode, Rect *bounds,
+                     uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH)
 {
     if(!mInitialized || numGlyphs == 0 || text == NULL || len == 0) {
         return;
     }
 
-    int penX = x, penY = y;
-    int glyphsLeft = 1;
+    if(mode == Font::MEASURE) {
+        if (bounds == NULL) {
+            LOGE("No return rectangle provided to measure text");
+            return;
+        }
+        // Reset min and max of the bounding box to something large
+        bounds->set(1e6, -1e6, -1e6, 1e6);
+    }
+
+    int32_t penX = x, penY = y;
+    int32_t glyphsLeft = 1;
     if(numGlyphs > 0) {
         glyphsLeft = numGlyphs;
     }
@@ -135,7 +196,17 @@
 
         // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
         if(cachedGlyph->mIsValid) {
-            drawCachedGlyph(cachedGlyph, penX, penY);
+            switch(mode) {
+            case FRAMEBUFFER:
+                drawCachedGlyph(cachedGlyph, penX, penY);
+                break;
+            case BITMAP:
+                drawCachedGlyph(cachedGlyph, penX, penY, bitmap, bitmapW, bitmapH);
+                break;
+            case MEASURE:
+                measureCachedGlyph(cachedGlyph, penX, penY, bounds);
+                break;
+            }
         }
 
         penX += (cachedGlyph->mAdvance.x >> 6);
@@ -283,7 +354,7 @@
     }
 
     // Get the black gamma threshold
-    int blackThreshold = DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD;
+    int32_t blackThreshold = DEFAULT_TEXT_BLACK_GAMMA_THRESHOLD;
     if (property_get(PROPERTY_TEXT_BLACK_GAMMA_THRESHOLD, property, NULL) > 0) {
         LOGD("  Setting text black gamma threshold to %s", property);
         blackThreshold = atoi(property);
@@ -294,7 +365,7 @@
     mBlackThreshold = (float)(blackThreshold) / 255.0f;
 
     // Get the white gamma threshold
-    int whiteThreshold = DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD;
+    int32_t whiteThreshold = DEFAULT_TEXT_WHITE_GAMMA_THRESHOLD;
     if (property_get(PROPERTY_TEXT_WHITE_GAMMA_THRESHOLD, property, NULL) > 0) {
         LOGD("  Setting text white gamma threshold to %s", property);
         whiteThreshold = atoi(property);
@@ -397,13 +468,13 @@
 
     uint32_t cacheWidth = getCacheTextureType()->getDimX();
 
-    unsigned char *cacheBuffer = (unsigned char*)mTextTexture->getPtr();
-    unsigned char *bitmapBuffer = bitmap->buffer;
+    uint8_t *cacheBuffer = (uint8_t*)mTextTexture->getPtr();
+    uint8_t *bitmapBuffer = bitmap->buffer;
 
     uint32_t cacheX = 0, bX = 0, cacheY = 0, bY = 0;
     for(cacheX = startX, bX = 0; cacheX < endX; cacheX ++, bX ++) {
         for(cacheY = startY, bY = 0; cacheY < endY; cacheY ++, bY ++) {
-            unsigned char tempCol = bitmapBuffer[bY * bitmap->width + bX];
+            uint8_t tempCol = bitmapBuffer[bY * bitmap->width + bX];
             cacheBuffer[cacheY*cacheWidth + cacheX] = tempCol;
         }
     }
@@ -487,7 +558,7 @@
     mTextTexture->deferedUploadToTexture(mRSC, false, 0);
 
     // Split up our cache texture into lines of certain widths
-    int nextLine = 0;
+    int32_t nextLine = 0;
     mCacheLines.push(new CacheTextureLine(16, texType->getDimX(), nextLine, 0));
     nextLine += mCacheLines.top()->mMaxHeight;
     mCacheLines.push(new CacheTextureLine(24, texType->getDimX(), nextLine, 0));
@@ -519,8 +590,8 @@
 
     // Four verts, two triangles , six indices per quad
     for(uint32_t i = 0; i < mMaxNumberOfQuads; i ++) {
-        int i6 = i * 6;
-        int i4 = i * 4;
+        int32_t i6 = i * 6;
+        int32_t i4 = i * 4;
 
         indexPtr[i6 + 0] = i4 + 0;
         indexPtr[i6 + 1] = i4 + 1;
@@ -713,7 +784,11 @@
 }
 
 
-void FontState::renderText(const char *text, uint32_t len, uint32_t startIndex, int numGlyphs, int x, int y)
+void FontState::renderText(const char *text, uint32_t len, int32_t x, int32_t y,
+                           uint32_t startIndex, int32_t numGlyphs,
+                           Font::RenderMode mode,
+                           Font::Rect *bounds,
+                           uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH)
 {
     checkInit();
 
@@ -730,7 +805,8 @@
         return;
     }
 
-    currentFont->renderUTF(text, len, startIndex, numGlyphs, x, y);
+    currentFont->renderUTF(text, len, x, y, startIndex, numGlyphs,
+                           mode, bounds, bitmap, bitmapW, bitmapH);
 
     if(mCurrentQuadIndex != 0) {
         issueDrawCommand();
@@ -738,32 +814,8 @@
     }
 }
 
-void FontState::renderText(const char *text, int x, int y)
-{
-    size_t textLen = strlen(text);
-    renderText(text, textLen, 0, -1, x, y);
-}
-
-void FontState::renderText(Allocation *alloc, int x, int y)
-{
-    if(!alloc) {
-        return;
-    }
-
-    const char *text = (const char *)alloc->getPtr();
-    size_t allocSize = alloc->getType()->getSizeBytes();
-    renderText(text, allocSize, 0, -1, x, y);
-}
-
-void FontState::renderText(Allocation *alloc, uint32_t start, int len, int x, int y)
-{
-    if(!alloc) {
-        return;
-    }
-
-    const char *text = (const char *)alloc->getPtr();
-    size_t allocSize = alloc->getType()->getSizeBytes();
-    renderText(text, allocSize, start, len, x, y);
+void FontState::measureText(const char *text, uint32_t len, Font::Rect *bounds) {
+    renderText(text, len, 0, 0, 0, -1, Font::MEASURE, bounds);
 }
 
 void FontState::setFontColor(float r, float g, float b, float a) {
@@ -773,7 +825,7 @@
     mConstants.mFontColor[3] = a;
 
     mConstants.mGamma = 1.0f;
-    const int luminance = (r * 2.0f + g * 5.0f + b) / 8.0f;
+    const int32_t luminance = (r * 2.0f + g * 5.0f + b) / 8.0f;
     if (luminance <= mBlackThreshold) {
         mConstants.mGamma = mBlackGamma;
     } else if (luminance >= mWhiteThreshold) {
diff --git a/rsFont.h b/rsFont.h
index 16009ef..0012b84 100644
--- a/rsFont.h
+++ b/rsFont.h
@@ -45,12 +45,26 @@
 class Font : public ObjectBase
 {
 public:
-    ~Font();
+    enum RenderMode {
+        FRAMEBUFFER,
+        BITMAP,
+        MEASURE,
+    };
 
-    // Pointer to the utf data, length of data, where to start, number of glyphs ot read
-    // (each glyph may be longer than a char because we are dealing with utf data)
-    // Last two variables are the initial pen position
-    void renderUTF(const char *text, uint32_t len, uint32_t start, int numGlyphs, int x, int y);
+    struct Rect {
+        int32_t left;
+        int32_t top;
+        int32_t right;
+        int32_t bottom;
+        void set(int32_t l, int32_t r, int32_t t, int32_t b) {
+            left = l;
+            right = r;
+            top = t;
+            bottom = b;
+        }
+    };
+
+    ~Font();
 
     // Currently files do not get serialized,
     // but we need to inherit from ObjectBase for ref tracking
@@ -66,6 +80,14 @@
 
     friend class FontState;
 
+    // Pointer to the utf data, length of data, where to start, number of glyphs ot read
+    // (each glyph may be longer than a char because we are dealing with utf data)
+    // Last two variables are the initial pen position
+    void renderUTF(const char *text, uint32_t len, int32_t x, int32_t y,
+                   uint32_t start, int32_t numGlyphs,
+                   RenderMode mode = FRAMEBUFFER, Rect *bounds = NULL,
+                   uint8_t *bitmap = NULL, uint32_t bitmapW = 0, uint32_t bitmapH = 0);
+
     void invalidateTextureCache();
     struct CachedGlyphInfo
     {
@@ -106,7 +128,10 @@
 
     CachedGlyphInfo *cacheGlyph(uint32_t glyph);
     void updateGlyphCache(CachedGlyphInfo *glyph);
-    void drawCachedGlyph(CachedGlyphInfo *glyph, int x, int y);
+    void measureCachedGlyph(CachedGlyphInfo *glyph, int32_t x, int32_t y, Rect *bounds);
+    void drawCachedGlyph(CachedGlyphInfo *glyph, int32_t x, int32_t y);
+    void drawCachedGlyph(CachedGlyphInfo *glyph, int32_t x, int32_t y,
+                         uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH);
 };
 
 class FontState
@@ -121,10 +146,13 @@
     ObjectBaseRef<Font> mDefault;
     ObjectBaseRef<Font> mLast;
 
-    void renderText(const char *text, uint32_t len, uint32_t startIndex, int numGlyphs, int x, int y);
-    void renderText(const char *text, int x, int y);
-    void renderText(Allocation *alloc, int x, int y);
-    void renderText(Allocation *alloc, uint32_t start, int len, int x, int y);
+    void renderText(const char *text, uint32_t len, int32_t x, int32_t y,
+                    uint32_t startIndex = 0, int numGlyphs = -1,
+                    Font::RenderMode mode = Font::FRAMEBUFFER,
+                    Font::Rect *bounds = NULL,
+                    uint8_t *bitmap = NULL, uint32_t bitmapW = 0, uint32_t bitmapH = 0);
+
+    void measureText(const char *text, uint32_t len, Font::Rect *bounds);
 
     void setFontColor(float r, float g, float b, float a);
     void getFontColor(float *r, float *g, float *b, float *a) const;
@@ -198,6 +226,9 @@
     // Texture to cache glyph bitmaps
     ObjectBaseRef<Allocation> mTextTexture;
     void initTextTexture();
+    const uint8_t* getTextTextureData() const {
+        return (uint8_t*)mTextTexture->getPtr();
+    }
 
     bool cacheBitmap(FT_Bitmap *bitmap, uint32_t *retOriginX, uint32_t *retOriginY);
     const Type* getCacheTextureType() {
diff --git a/rsScriptC_LibGL.cpp b/rsScriptC_LibGL.cpp
index 88db761..b991cab 100644
--- a/rsScriptC_LibGL.cpp
+++ b/rsScriptC_LibGL.cpp
@@ -351,13 +351,59 @@
     CHECK_OBJ(va);
     GET_TLS();
     Allocation *alloc = static_cast<Allocation *>(va);
-    rsc->mStateFont.renderText(alloc, x, y);
+    const char *text = (const char *)alloc->getPtr();
+    size_t allocSize = alloc->getType()->getSizeBytes();
+    rsc->mStateFont.renderText(text, allocSize, x, y);
 }
 
 static void SC_DrawText(const char *text, int x, int y)
 {
     GET_TLS();
-    rsc->mStateFont.renderText(text, x, y);
+    size_t textLen = strlen(text);
+    rsc->mStateFont.renderText(text, textLen, x, y);
+}
+
+static void SC_setMetrics(Font::Rect *metrics,
+                          int32_t *left, int32_t *right,
+                          int32_t *top, int32_t *bottom)
+{
+    if(left) {
+        *left = metrics->left;
+    }
+    if(right) {
+        *right = metrics->right;
+    }
+    if(top) {
+        *top = metrics->top;
+    }
+    if(bottom) {
+        *bottom = metrics->bottom;
+    }
+}
+
+static void SC_MeasureTextAlloc(RsAllocation va,
+                                int32_t *left, int32_t *right,
+                                int32_t *top, int32_t *bottom)
+{
+    CHECK_OBJ(va);
+    GET_TLS();
+    Allocation *alloc = static_cast<Allocation *>(va);
+    const char *text = (const char *)alloc->getPtr();
+    size_t textLen = alloc->getType()->getSizeBytes();
+    Font::Rect metrics;
+    rsc->mStateFont.measureText(text, textLen, &metrics);
+    SC_setMetrics(&metrics, left, right, top, bottom);
+}
+
+static void SC_MeasureText(const char *text,
+                           int32_t *left, int32_t *right,
+                           int32_t *top, int32_t *bottom)
+{
+    GET_TLS();
+    size_t textLen = strlen(text);
+    Font::Rect metrics;
+    rsc->mStateFont.measureText(text, textLen, &metrics);
+    SC_setMetrics(&metrics, left, right, top, bottom);
 }
 
 static void SC_BindFont(RsFont font)
@@ -432,6 +478,8 @@
 
     { "_Z11rsgDrawTextPKcii", (void *)&SC_DrawText },
     { "_Z11rsgDrawText13rs_allocationii", (void *)&SC_DrawTextAlloc },
+    { "_Z14rsgMeasureTextPKcPiS1_S1_S1_", (void *)&SC_MeasureText },
+    { "_Z14rsgMeasureText13rs_allocationPiS0_S0_S0_", (void *)&SC_MeasureTextAlloc },
 
     { "_Z11rsgBindFont7rs_font", (void *)&SC_BindFont },
     { "_Z12rsgFontColorffff", (void *)&SC_FontColor },
diff --git a/scriptc/rs_graphics.rsh b/scriptc/rs_graphics.rsh
index c0b2d2d..ac6f8cc 100644
--- a/scriptc/rs_graphics.rsh
+++ b/scriptc/rs_graphics.rsh
@@ -79,6 +79,12 @@
     rsgBindFont(rs_font);
 extern void __attribute__((overloadable))
     rsgFontColor(float, float, float, float);
+// Returns the bounding box of the text relative to (0, 0)
+// Any of left, right, top, bottom could be NULL
+extern void __attribute__((overloadable))
+    rsgMeasureText(const char *, int *left, int *right, int *top, int *bottom);
+extern void __attribute__((overloadable))
+    rsgMeasureText(rs_allocation, int *left, int *right, int *top, int *bottom);
 
 extern void __attribute__((overloadable))
     rsgMeshComputeBoundingBox(rs_mesh mesh, float *minX, float *minY, float *minZ,