Merge "Adding text metrics to renderscript."

This commit is contained in:
Alex Sakhartchouk
2010-10-05 13:13:40 -07:00
committed by Android (Google) Code Review
7 changed files with 254 additions and 75 deletions

View File

@@ -84,8 +84,6 @@ public class RsRenderStatesRS {
private Allocation mTexTransparent;
private Allocation mTexChecker;
private Allocation mAllocPV;
private Mesh mMbyNMesh;
private Mesh mTorus;
@@ -95,7 +93,6 @@ public class RsRenderStatesRS {
Font mFontSerifItalic;
Font mFontSerifBoldItalic;
Font mFontMono;
private Allocation mTextAlloc;
private ScriptC_rsrenderstates mScript;
@@ -267,12 +264,15 @@ public class RsRenderStatesRS {
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() {

View File

@@ -42,6 +42,7 @@ rs_font gFontSerifBold;
rs_font gFontSerifItalic;
rs_font gFontSerifBoldItalic;
rs_font gFontMono;
rs_allocation gTextAlloc;
int gDisplayMode;
@@ -70,7 +71,7 @@ rs_program_fragment gProgFragmentMultitex;
#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 init() {
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 @@ void displayFontSamples() {
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() {

View File

@@ -258,14 +258,14 @@ void Context::displayDebugStats()
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);
}

View File

@@ -84,34 +84,95 @@ void Font::invalidateTextureCache()
}
}
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 @@ void Font::renderUTF(const char *text, uint32_t len, uint32_t start, int numGlyp
// 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 @@ FontState::FontState()
}
// 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 @@ FontState::FontState()
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 @@ bool FontState::cacheBitmap(FT_Bitmap *bitmap, uint32_t *retOriginX, uint32_t *r
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 @@ void FontState::initTextTexture()
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 @@ void FontState::initVertexArrayBuffers()
// 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::precacheLatin(Font *font) {
}
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 @@ void FontState::renderText(const char *text, uint32_t len, uint32_t startIndex,
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, uint32_t len, uint32_t startIndex,
}
}
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 @@ void FontState::setFontColor(float r, float g, float b, float a) {
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) {

View File

@@ -45,12 +45,26 @@ class FontState;
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 @@ protected:
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 @@ protected:
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 @@ public:
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 @@ protected:
// 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() {

View File

@@ -351,13 +351,59 @@ static void SC_DrawTextAlloc(RsAllocation va, int x, int y)
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 @@ static ScriptCState::SymbolTable_t gSyms[] = {
{ "_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 },

View File

@@ -79,6 +79,12 @@ extern void __attribute__((overloadable))
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,