summaryrefslogtreecommitdiffstats
path: root/libs/hwui/FontRenderer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libs/hwui/FontRenderer.cpp')
-rw-r--r--libs/hwui/FontRenderer.cpp519
1 files changed, 347 insertions, 172 deletions
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 42e672b..3df105b 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -22,8 +22,10 @@
#include <utils/Log.h>
+#include "Caches.h"
#include "Debug.h"
#include "FontRenderer.h"
+#include "Caches.h"
namespace android {
namespace uirenderer {
@@ -34,10 +36,28 @@ namespace uirenderer {
#define DEFAULT_TEXT_CACHE_WIDTH 1024
#define DEFAULT_TEXT_CACHE_HEIGHT 256
-
-// We should query these values from the GL context
#define MAX_TEXT_CACHE_WIDTH 2048
-#define MAX_TEXT_CACHE_HEIGHT 2048
+#define TEXTURE_BORDER_SIZE 2
+
+///////////////////////////////////////////////////////////////////////////////
+// CacheTextureLine
+///////////////////////////////////////////////////////////////////////////////
+
+bool CacheTextureLine::fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) {
+ if (glyph.fHeight + TEXTURE_BORDER_SIZE > mMaxHeight) {
+ return false;
+ }
+
+ if (mCurrentCol + glyph.fWidth + TEXTURE_BORDER_SIZE < mMaxWidth) {
+ *retOriginX = mCurrentCol + 1;
+ *retOriginY = mCurrentRow + 1;
+ mCurrentCol += glyph.fWidth + TEXTURE_BORDER_SIZE;
+ mDirty = true;
+ return true;
+ }
+
+ return false;
+}
///////////////////////////////////////////////////////////////////////////////
// Font
@@ -65,13 +85,17 @@ Font::~Font() {
}
}
-void Font::invalidateTextureCache() {
+void Font::invalidateTextureCache(CacheTextureLine *cacheLine) {
for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
- mCachedGlyphs.valueAt(i)->mIsValid = false;
+ CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueAt(i);
+ if (cacheLine == NULL || cachedGlyph->mCachedTextureLine == cacheLine) {
+ cachedGlyph->mIsValid = false;
+ }
}
}
-void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, Rect *bounds) {
+void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y,
+ uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
int nPenX = x + glyph->mBitmapLeft;
int nPenY = y + glyph->mBitmapTop;
@@ -92,7 +116,8 @@ void Font::measureCachedGlyph(CachedGlyphInfo *glyph, int x, int y, Rect *bounds
}
}
-void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) {
+void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
+ uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
int nPenX = x + glyph->mBitmapLeft;
int nPenY = y + glyph->mBitmapTop + glyph->mBitmapHeight;
@@ -104,39 +129,41 @@ void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) {
int width = (int) glyph->mBitmapWidth;
int height = (int) glyph->mBitmapHeight;
- mState->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);
+ mState->appendMeshQuad(nPenX, nPenY, u1, v2,
+ nPenX + width, nPenY, u2, v2,
+ nPenX + width, nPenY - height, u2, v1,
+ nPenX, nPenY - height, u1, v1, glyph->mCachedTextureLine->mCacheTexture);
}
-void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
- uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH) {
+void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
+ uint8_t* bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* pos) {
int nPenX = x + glyph->mBitmapLeft;
int nPenY = y + glyph->mBitmapTop;
uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
- uint32_t cacheWidth = mState->getCacheWidth();
- const uint8_t* cacheBuffer = mState->getTextTextureData();
+ CacheTexture *cacheTexture = glyph->mCachedTextureLine->mCacheTexture;
+ uint32_t cacheWidth = cacheTexture->mWidth;
+ const uint8_t* cacheBuffer = cacheTexture->mTexture;
uint32_t cacheX = 0, cacheY = 0;
int32_t bX = 0, bY = 0;
for (cacheX = glyph->mStartX, bX = nPenX; cacheX < endX; cacheX++, bX++) {
for (cacheY = glyph->mStartY, bY = nPenY; cacheY < endY; cacheY++, bY++) {
+#if DEBUG_FONT_RENDERER
if (bX < 0 || bY < 0 || bX >= (int32_t) bitmapW || bY >= (int32_t) bitmapH) {
ALOGE("Skipping invalid index");
continue;
}
+#endif
uint8_t tempCol = cacheBuffer[cacheY * cacheWidth + cacheX];
bitmap[bY * bitmapW + bX] = tempCol;
}
}
-
}
-Font::CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit) {
+CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit) {
CachedGlyphInfo* cachedGlyph = NULL;
ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
if (index >= 0) {
@@ -158,13 +185,19 @@ void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len
int numGlyphs, int x, int y, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH) {
if (bitmap != NULL && bitmapW > 0 && bitmapH > 0) {
render(paint, text, start, len, numGlyphs, x, y, BITMAP, bitmap,
- bitmapW, bitmapH, NULL);
+ bitmapW, bitmapH, NULL, NULL);
} else {
render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
- 0, 0, NULL);
+ 0, 0, NULL, NULL);
}
}
+void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+ int numGlyphs, int x, int y, const float* positions) {
+ render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, NULL,
+ 0, 0, NULL, positions);
+}
+
void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
int numGlyphs, Rect *bounds) {
if (bounds == NULL) {
@@ -172,62 +205,95 @@ void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t le
return;
}
bounds->set(1e6, -1e6, -1e6, 1e6);
- render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds);
+ render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, NULL);
}
#define SkAutoKern_AdjustF(prev, next) (((next) - (prev) + 32) >> 6 << 16)
void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap,
- uint32_t bitmapW, uint32_t bitmapH,Rect *bounds) {
+ uint32_t bitmapW, uint32_t bitmapH, Rect *bounds, const float* positions) {
if (numGlyphs == 0 || text == NULL || len == 0) {
return;
}
- float penX = x;
- int penY = y;
- int glyphsLeft = 1;
- if (numGlyphs > 0) {
- glyphsLeft = numGlyphs;
- }
-
- SkFixed prevRsbDelta = 0;
- penX += 0.5f;
+ int glyphsCount = 0;
text += start;
- while (glyphsLeft > 0) {
- glyph_t glyph = GET_GLYPH(text);
+ static RenderGlyph gRenderGlyph[] = {
+ &android::uirenderer::Font::drawCachedGlyph,
+ &android::uirenderer::Font::drawCachedGlyphBitmap,
+ &android::uirenderer::Font::measureCachedGlyph
+ };
+ RenderGlyph render = gRenderGlyph[mode];
- // Reached the end of the string
- if (IS_END_OF_STRING(glyph)) {
- break;
- }
+ if (CC_LIKELY(positions == NULL)) {
+ SkFixed prevRsbDelta = 0;
- CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
- penX += SkFixedToFloat(SkAutoKern_AdjustF(prevRsbDelta, cachedGlyph->mLsbDelta));
- prevRsbDelta = cachedGlyph->mRsbDelta;
+ float penX = x;
+ int penY = y;
- // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
- if (cachedGlyph->mIsValid) {
- switch(mode) {
- case FRAMEBUFFER:
- drawCachedGlyph(cachedGlyph, (int) floorf(penX), penY);
- break;
- case BITMAP:
- drawCachedGlyph(cachedGlyph, (int) floorf(penX), penY, bitmap, bitmapW, bitmapH);
- break;
- case MEASURE:
- measureCachedGlyph(cachedGlyph, (int) floorf(penX), penY, bounds);
+ penX += 0.5f;
+
+ while (glyphsCount < numGlyphs) {
+ glyph_t glyph = GET_GLYPH(text);
+
+ // Reached the end of the string
+ if (IS_END_OF_STRING(glyph)) {
break;
}
+
+ CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
+ penX += SkFixedToFloat(SkAutoKern_AdjustF(prevRsbDelta, cachedGlyph->mLsbDelta));
+ prevRsbDelta = cachedGlyph->mRsbDelta;
+
+ // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
+ if (cachedGlyph->mIsValid) {
+ (*this.*render)(cachedGlyph, (int) floorf(penX), penY,
+ bitmap, bitmapW, bitmapH, bounds, positions);
+ }
+
+ penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
+
+ glyphsCount++;
}
+ } else {
+ const SkPaint::Align align = paint->getTextAlign();
+
+ // This is for renderPosText()
+ while (glyphsCount < numGlyphs) {
+ glyph_t glyph = GET_GLYPH(text);
- penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
+ // Reached the end of the string
+ if (IS_END_OF_STRING(glyph)) {
+ break;
+ }
- // If we were given a specific number of glyphs, decrement
- if (numGlyphs > 0) {
- glyphsLeft--;
+ CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
+
+ // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage
+ if (cachedGlyph->mIsValid) {
+ int penX = x + positions[(glyphsCount << 1)];
+ int penY = y + positions[(glyphsCount << 1) + 1];
+
+ switch (align) {
+ case SkPaint::kRight_Align:
+ penX -= SkFixedToFloat(cachedGlyph->mAdvanceX);
+ penY -= SkFixedToFloat(cachedGlyph->mAdvanceY);
+ break;
+ case SkPaint::kCenter_Align:
+ penX -= SkFixedToFloat(cachedGlyph->mAdvanceX >> 1);
+ penY -= SkFixedToFloat(cachedGlyph->mAdvanceY >> 1);
+ default:
+ break;
+ }
+
+ (*this.*render)(cachedGlyph, penX, penY,
+ bitmap, bitmapW, bitmapH, bounds, positions);
+ }
+
+ glyphsCount++;
}
}
}
@@ -245,7 +311,7 @@ void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyp
// Get the bitmap for the glyph
paint->findImage(skiaGlyph);
- glyph->mIsValid = mState->cacheBitmap(skiaGlyph, &startX, &startY);
+ mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY);
if (!glyph->mIsValid) {
return;
@@ -259,8 +325,8 @@ void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyp
glyph->mBitmapWidth = skiaGlyph.fWidth;
glyph->mBitmapHeight = skiaGlyph.fHeight;
- uint32_t cacheWidth = mState->getCacheWidth();
- uint32_t cacheHeight = mState->getCacheHeight();
+ uint32_t cacheWidth = glyph->mCachedTextureLine->mCacheTexture->mWidth;
+ uint32_t cacheHeight = glyph->mCachedTextureLine->mCacheTexture->mHeight;
glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
@@ -270,7 +336,7 @@ void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyp
mState->mUploadTexture = true;
}
-Font::CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph) {
+CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph) {
CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
mCachedGlyphs.add(glyph, newGlyph);
@@ -319,27 +385,31 @@ FontRenderer::FontRenderer() {
mInitialized = false;
mMaxNumberOfQuads = 1024;
mCurrentQuadIndex = 0;
- mTextureId = 0;
mTextMeshPtr = NULL;
- mTextTexture = NULL;
+ mCurrentCacheTexture = NULL;
+ mLastCacheTexture = NULL;
+ mCacheTextureSmall = NULL;
+ mCacheTexture128 = NULL;
+ mCacheTexture256 = NULL;
+ mCacheTexture512 = NULL;
+
+ mLinearFiltering = false;
mIndexBufferID = 0;
- mPositionAttrSlot = -1;
- mTexcoordAttrSlot = -1;
- mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
- mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
+ mSmallCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
+ mSmallCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
char property[PROPERTY_VALUE_MAX];
if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
if (sLogFontRendererCreate) {
INIT_LOGD(" Setting text cache width to %s pixels", property);
}
- mCacheWidth = atoi(property);
+ mSmallCacheWidth = atoi(property);
} else {
if (sLogFontRendererCreate) {
- INIT_LOGD(" Using default text cache width of %i pixels", mCacheWidth);
+ INIT_LOGD(" Using default text cache width of %i pixels", mSmallCacheWidth);
}
}
@@ -347,10 +417,10 @@ FontRenderer::FontRenderer() {
if (sLogFontRendererCreate) {
INIT_LOGD(" Setting text cache width to %s pixels", property);
}
- mCacheHeight = atoi(property);
+ mSmallCacheHeight = atoi(property);
} else {
if (sLogFontRendererCreate) {
- INIT_LOGD(" Using default text cache height of %i pixels", mCacheHeight);
+ INIT_LOGD(" Using default text cache height of %i pixels", mSmallCacheHeight);
}
}
@@ -365,11 +435,10 @@ FontRenderer::~FontRenderer() {
if (mInitialized) {
delete[] mTextMeshPtr;
- delete[] mTextTexture;
- }
-
- if (mTextureId) {
- glDeleteTextures(1, &mTextureId);
+ delete mCacheTextureSmall;
+ delete mCacheTexture128;
+ delete mCacheTexture256;
+ delete mCacheTexture512;
}
Vector<Font*> fontsToDereference = mActiveFonts;
@@ -391,30 +460,79 @@ void FontRenderer::flushAllAndInvalidate() {
}
}
-bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
- // If the glyph is too tall, don't cache it
- if (glyph.fHeight + 2 > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
- if (mCacheHeight < MAX_TEXT_CACHE_HEIGHT) {
- // Default cache not large enough for large glyphs - resize cache to
- // max size and try again
- flushAllAndInvalidate();
- initTextTexture(true);
- }
- if (glyph.fHeight + 2 > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
- ALOGE("Font size to large to fit in cache. width, height = %i, %i",
- (int) glyph.fWidth, (int) glyph.fHeight);
- return false;
+void FontRenderer::deallocateTextureMemory(CacheTexture *cacheTexture) {
+ if (cacheTexture && cacheTexture->mTexture) {
+ glDeleteTextures(1, &cacheTexture->mTextureId);
+ delete cacheTexture->mTexture;
+ cacheTexture->mTexture = NULL;
+ }
+}
+
+void FontRenderer::flushLargeCaches() {
+ if ((!mCacheTexture128 || !mCacheTexture128->mTexture) &&
+ (!mCacheTexture256 || !mCacheTexture256->mTexture) &&
+ (!mCacheTexture512 || !mCacheTexture512->mTexture)) {
+ // Typical case; no large glyph caches allocated
+ return;
+ }
+
+ for (uint32_t i = 0; i < mCacheLines.size(); i++) {
+ CacheTextureLine* cacheLine = mCacheLines[i];
+ if ((cacheLine->mCacheTexture == mCacheTexture128 ||
+ cacheLine->mCacheTexture == mCacheTexture256 ||
+ cacheLine->mCacheTexture == mCacheTexture512) &&
+ cacheLine->mCacheTexture->mTexture != NULL) {
+ cacheLine->mCurrentCol = 0;
+ for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
+ mActiveFonts[i]->invalidateTextureCache(cacheLine);
+ }
}
}
+ deallocateTextureMemory(mCacheTexture128);
+ deallocateTextureMemory(mCacheTexture256);
+ deallocateTextureMemory(mCacheTexture512);
+}
+
+void FontRenderer::allocateTextureMemory(CacheTexture *cacheTexture) {
+ int width = cacheTexture->mWidth;
+ int height = cacheTexture->mHeight;
+ cacheTexture->mTexture = new uint8_t[width * height];
+ memset(cacheTexture->mTexture, 0, width * height * sizeof(uint8_t));
+ glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ // Initialize texture dimensions
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
+ GL_ALPHA, GL_UNSIGNED_BYTE, 0);
+
+ const GLenum filtering = cacheTexture->mLinearFiltering ? GL_LINEAR : GL_NEAREST;
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+}
+
+void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
+ uint32_t* retOriginX, uint32_t* retOriginY) {
+ cachedGlyph->mIsValid = false;
+ // If the glyph is too tall, don't cache it
+ if (glyph.fHeight + TEXTURE_BORDER_SIZE > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
+ ALOGE("Font size to large to fit in cache. width, height = %i, %i",
+ (int) glyph.fWidth, (int) glyph.fHeight);
+ return;
+ }
+
// Now copy the bitmap into the cache texture
uint32_t startX = 0;
uint32_t startY = 0;
bool bitmapFit = false;
+ CacheTextureLine *cacheLine;
for (uint32_t i = 0; i < mCacheLines.size(); i++) {
bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
if (bitmapFit) {
+ cacheLine = mCacheLines[i];
break;
}
}
@@ -427,27 +545,33 @@ bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint3
for (uint32_t i = 0; i < mCacheLines.size(); i++) {
bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
if (bitmapFit) {
+ cacheLine = mCacheLines[i];
break;
}
}
// if we still don't fit, something is wrong and we shouldn't draw
if (!bitmapFit) {
- ALOGE("Bitmap doesn't fit in cache. width, height = %i, %i",
- (int) glyph.fWidth, (int) glyph.fHeight);
- return false;
+ return;
}
}
+ cachedGlyph->mCachedTextureLine = cacheLine;
+
*retOriginX = startX;
*retOriginY = startY;
uint32_t endX = startX + glyph.fWidth;
uint32_t endY = startY + glyph.fHeight;
- uint32_t cacheWidth = mCacheWidth;
+ uint32_t cacheWidth = cacheLine->mMaxWidth;
- uint8_t* cacheBuffer = mTextTexture;
+ CacheTexture *cacheTexture = cacheLine->mCacheTexture;
+ if (cacheTexture->mTexture == NULL) {
+ // Large-glyph texture memory is allocated only as needed
+ allocateTextureMemory(cacheTexture);
+ }
+ uint8_t* cacheBuffer = cacheTexture->mTexture;
uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
unsigned int stride = glyph.rowBytes();
@@ -458,69 +582,73 @@ bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint3
cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
}
}
-
- return true;
+ cachedGlyph->mIsValid = true;
}
-void FontRenderer::initTextTexture(bool largeFonts) {
- mCacheLines.clear();
- if (largeFonts) {
- mCacheWidth = MAX_TEXT_CACHE_WIDTH;
- mCacheHeight = MAX_TEXT_CACHE_HEIGHT;
- }
+CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
+ GLuint textureId;
+ glGenTextures(1, &textureId);
+ uint8_t* textureMemory = NULL;
- mTextTexture = new uint8_t[mCacheWidth * mCacheHeight];
- memset(mTextTexture, 0, mCacheWidth * mCacheHeight * sizeof(uint8_t));
-
- mUploadTexture = false;
-
- if (mTextureId != 0) {
- glDeleteTextures(1, &mTextureId);
+ CacheTexture* cacheTexture = new CacheTexture(textureMemory, textureId, width, height);
+ if (allocate) {
+ allocateTextureMemory(cacheTexture);
}
- glGenTextures(1, &mTextureId);
- glBindTexture(GL_TEXTURE_2D, mTextureId);
- glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
- // Initialize texture dimensions
- glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
- GL_ALPHA, GL_UNSIGNED_BYTE, 0);
+ return cacheTexture;
+}
- mLinearFiltering = false;
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+void FontRenderer::initTextTexture() {
+ mCacheLines.clear();
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ // Next, use other, separate caches for large glyphs.
+ uint16_t maxWidth = 0;
+ if (Caches::hasInstance()) {
+ maxWidth = Caches::getInstance().maxTextureSize;
+ }
+ if (maxWidth > MAX_TEXT_CACHE_WIDTH || maxWidth == 0) {
+ maxWidth = MAX_TEXT_CACHE_WIDTH;
+ }
+ if (mCacheTextureSmall != NULL) {
+ delete mCacheTextureSmall;
+ delete mCacheTexture128;
+ delete mCacheTexture256;
+ delete mCacheTexture512;
+ }
+ mCacheTextureSmall = createCacheTexture(mSmallCacheWidth, mSmallCacheHeight, true);
+ mCacheTexture128 = createCacheTexture(maxWidth, 256, false);
+ mCacheTexture256 = createCacheTexture(maxWidth, 256, false);
+ mCacheTexture512 = createCacheTexture(maxWidth, 512, false);
+ mCurrentCacheTexture = mCacheTextureSmall;
- // Split up our cache texture into lines of certain widths
+ mUploadTexture = false;
+ // Split up our default cache texture into lines of certain widths
int nextLine = 0;
- mCacheLines.push(new CacheTextureLine(mCacheWidth, 18, nextLine, 0));
+ mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 18, nextLine, 0, mCacheTextureSmall));
nextLine += mCacheLines.top()->mMaxHeight;
- mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
+ mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall));
nextLine += mCacheLines.top()->mMaxHeight;
- mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
+ mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall));
nextLine += mCacheLines.top()->mMaxHeight;
- mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
+ mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall));
nextLine += mCacheLines.top()->mMaxHeight;
- mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
+ mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall));
nextLine += mCacheLines.top()->mMaxHeight;
- mCacheLines.push(new CacheTextureLine(mCacheWidth, 42, nextLine, 0));
+ mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 42, nextLine, 0, mCacheTextureSmall));
nextLine += mCacheLines.top()->mMaxHeight;
- if (largeFonts) {
- int nextSize = 76;
- // Make several new lines with increasing font sizes
- while (nextSize < (int)(mCacheHeight - nextLine - (2 * nextSize))) {
- mCacheLines.push(new CacheTextureLine(mCacheWidth, nextSize, nextLine, 0));
- nextLine += mCacheLines.top()->mMaxHeight;
- nextSize += 50;
- }
- }
- mCacheLines.push(new CacheTextureLine(mCacheWidth, mCacheHeight - nextLine, nextLine, 0));
+ mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, mSmallCacheHeight - nextLine,
+ nextLine, 0, mCacheTextureSmall));
+
+ // The first cache is split into 2 lines of height 128, the rest have just one cache line.
+ mCacheLines.push(new CacheTextureLine(maxWidth, 128, 0, 0, mCacheTexture128));
+ mCacheLines.push(new CacheTextureLine(maxWidth, 128, 128, 0, mCacheTexture128));
+ mCacheLines.push(new CacheTextureLine(maxWidth, 256, 0, 0, mCacheTexture256));
+ mCacheLines.push(new CacheTextureLine(maxWidth, 512, 0, 0, mCacheTexture512));
}
// Avoid having to reallocate memory and render quad by quad
void FontRenderer::initVertexArrayBuffers() {
- uint32_t numIndicies = mMaxNumberOfQuads * 6;
- uint32_t indexBufferSizeBytes = numIndicies * sizeof(uint16_t);
+ uint32_t numIndices = mMaxNumberOfQuads * 6;
+ uint32_t indexBufferSizeBytes = numIndices * sizeof(uint16_t);
uint16_t* indexBufferData = (uint16_t*) malloc(indexBufferSizeBytes);
// Four verts, two triangles , six indices per quad
@@ -538,13 +666,12 @@ void FontRenderer::initVertexArrayBuffers() {
}
glGenBuffers(1, &mIndexBufferID);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
+ Caches::getInstance().bindIndicesBuffer(mIndexBufferID);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexBufferSizeBytes, indexBufferData, GL_STATIC_DRAW);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
free(indexBufferData);
- uint32_t coordSize = 3;
+ uint32_t coordSize = 2;
uint32_t uvSize = 2;
uint32_t vertsPerQuad = 4;
uint32_t vertexBufferSize = mMaxNumberOfQuads * vertsPerQuad * coordSize * uvSize;
@@ -570,22 +697,28 @@ void FontRenderer::checkInit() {
}
void FontRenderer::checkTextureUpdate() {
- if (!mUploadTexture) {
+ if (!mUploadTexture && mLastCacheTexture == mCurrentCacheTexture) {
return;
}
- glBindTexture(GL_TEXTURE_2D, mTextureId);
-
+ Caches& caches = Caches::getInstance();
+ GLuint lastTextureId = 0;
// Iterate over all the cache lines and see which ones need to be updated
for (uint32_t i = 0; i < mCacheLines.size(); i++) {
CacheTextureLine* cl = mCacheLines[i];
- if(cl->mDirty) {
+ if (cl->mDirty && cl->mCacheTexture->mTexture != NULL) {
+ CacheTexture* cacheTexture = cl->mCacheTexture;
uint32_t xOffset = 0;
uint32_t yOffset = cl->mCurrentRow;
- uint32_t width = mCacheWidth;
+ uint32_t width = cl->mMaxWidth;
uint32_t height = cl->mMaxHeight;
- void* textureData = mTextTexture + yOffset*width;
+ void* textureData = cacheTexture->mTexture + (yOffset * width);
+ if (cacheTexture->mTextureId != lastTextureId) {
+ caches.activeTexture(0);
+ glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
+ lastTextureId = cacheTexture->mTextureId;
+ }
glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
@@ -593,57 +726,78 @@ void FontRenderer::checkTextureUpdate() {
}
}
+ glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId);
+ if (mLinearFiltering != mCurrentCacheTexture->mLinearFiltering) {
+ const GLenum filtering = mLinearFiltering ? GL_LINEAR : GL_NEAREST;
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
+ mCurrentCacheTexture->mLinearFiltering = mLinearFiltering;
+ }
+ mLastCacheTexture = mCurrentCacheTexture;
+
mUploadTexture = false;
}
void FontRenderer::issueDrawCommand() {
checkTextureUpdate();
- float* vtx = mTextMeshPtr;
- float* tex = vtx + 3;
+ Caches& caches = Caches::getInstance();
+ caches.bindIndicesBuffer(mIndexBufferID);
+ if (!mDrawn) {
+ float* buffer = mTextMeshPtr;
+ int offset = 2;
- glVertexAttribPointer(mPositionAttrSlot, 3, GL_FLOAT, false, 20, vtx);
- glVertexAttribPointer(mTexcoordAttrSlot, 2, GL_FLOAT, false, 20, tex);
+ bool force = caches.unbindMeshBuffer();
+ caches.bindPositionVertexPointer(force, caches.currentProgram->position, buffer);
+ caches.bindTexCoordsVertexPointer(force, caches.currentProgram->texCoords,
+ buffer + offset);
+ }
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndexBufferID);
glDrawElements(GL_TRIANGLES, mCurrentQuadIndex * 6, GL_UNSIGNED_SHORT, NULL);
mDrawn = true;
}
-void FontRenderer::appendMeshQuad(float x1, float y1, float z1, float u1, float v1, float x2,
- float y2, float z2, float u2, float v2, float x3, float y3, float z3, float u3, float v3,
- float x4, float y4, float z4, float u4, float v4) {
+void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
+ float x2, float y2, float u2, float v2,
+ float x3, float y3, float u3, float v3,
+ float x4, float y4, float u4, float v4, CacheTexture* texture) {
+
if (mClip &&
(x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
return;
}
+ if (texture != mCurrentCacheTexture) {
+ if (mCurrentQuadIndex != 0) {
+ // First, draw everything stored already which uses the previous texture
+ issueDrawCommand();
+ mCurrentQuadIndex = 0;
+ }
+ // Now use the new texture id
+ mCurrentCacheTexture = texture;
+ }
const uint32_t vertsPerQuad = 4;
- const uint32_t floatsPerVert = 5;
+ const uint32_t floatsPerVert = 4;
float* currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert;
(*currentPos++) = x1;
(*currentPos++) = y1;
- (*currentPos++) = z1;
(*currentPos++) = u1;
(*currentPos++) = v1;
(*currentPos++) = x2;
(*currentPos++) = y2;
- (*currentPos++) = z2;
(*currentPos++) = u2;
(*currentPos++) = v2;
(*currentPos++) = x3;
(*currentPos++) = y3;
- (*currentPos++) = z3;
(*currentPos++) = u3;
(*currentPos++) = v3;
(*currentPos++) = x4;
(*currentPos++) = y4;
- (*currentPos++) = z4;
(*currentPos++) = u4;
(*currentPos++) = v4;
@@ -723,6 +877,7 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const ch
return image;
}
+ mDrawn = false;
mClip = NULL;
mBounds = NULL;
@@ -750,29 +905,19 @@ FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const ch
image.image = dataBuffer;
image.penX = penX;
image.penY = penY;
+
return image;
}
-bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
- uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
+void FontRenderer::initRender(const Rect* clip, Rect* bounds) {
checkInit();
- if (!mCurrentFont) {
- ALOGE("No font set");
- return false;
- }
-
- if (mPositionAttrSlot < 0 || mTexcoordAttrSlot < 0) {
- ALOGE("Font renderer unable to draw, attribute slots undefined");
- return false;
- }
-
mDrawn = false;
mBounds = bounds;
mClip = clip;
+}
- mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
-
+void FontRenderer::finishRender() {
mBounds = NULL;
mClip = NULL;
@@ -780,6 +925,33 @@ bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text
issueDrawCommand();
mCurrentQuadIndex = 0;
}
+}
+
+bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
+ uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
+ if (!mCurrentFont) {
+ ALOGE("No font set");
+ return false;
+ }
+
+ initRender(clip, bounds);
+ mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y);
+ finishRender();
+
+ return mDrawn;
+}
+
+bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *text,
+ uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y,
+ const float* positions, Rect* bounds) {
+ if (!mCurrentFont) {
+ ALOGE("No font set");
+ return false;
+ }
+
+ initRender(clip, bounds);
+ mCurrentFont->render(paint, text, startIndex, len, numGlyphs, x, y, positions);
+ finishRender();
return mDrawn;
}
@@ -914,9 +1086,12 @@ void FontRenderer::verticalBlur(float* weights, int32_t radius,
void FontRenderer::blurImage(uint8_t *image, int32_t width, int32_t height, int32_t radius) {
float *gaussian = new float[2 * radius + 1];
computeGaussianWeights(gaussian, radius);
+
uint8_t* scratch = new uint8_t[width * height];
+
horizontalBlur(gaussian, radius, image, scratch, width, height);
verticalBlur(gaussian, radius, scratch, image, width, height);
+
delete[] gaussian;
delete[] scratch;
}