Merge "Make glyph cache more flexible"
This commit is contained in:
@@ -25,6 +25,7 @@
|
|||||||
#include "Caches.h"
|
#include "Caches.h"
|
||||||
#include "Debug.h"
|
#include "Debug.h"
|
||||||
#include "FontRenderer.h"
|
#include "FontRenderer.h"
|
||||||
|
#include "Caches.h"
|
||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
namespace uirenderer {
|
namespace uirenderer {
|
||||||
@@ -35,10 +36,28 @@ namespace uirenderer {
|
|||||||
|
|
||||||
#define DEFAULT_TEXT_CACHE_WIDTH 1024
|
#define DEFAULT_TEXT_CACHE_WIDTH 1024
|
||||||
#define DEFAULT_TEXT_CACHE_HEIGHT 256
|
#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_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
|
// Font
|
||||||
@@ -108,7 +127,7 @@ void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y) {
|
|||||||
mState->appendMeshQuad(nPenX, nPenY, u1, v2,
|
mState->appendMeshQuad(nPenX, nPenY, u1, v2,
|
||||||
nPenX + width, nPenY, u2, v2,
|
nPenX + width, nPenY, u2, v2,
|
||||||
nPenX + width, nPenY - height, u2, v1,
|
nPenX + width, nPenY - height, u2, v1,
|
||||||
nPenX, nPenY - height, u1, v1);
|
nPenX, nPenY - height, u1, v1, glyph->mCachedTextureLine->mCacheTexture);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
|
void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
|
||||||
@@ -119,8 +138,9 @@ void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
|
|||||||
uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
|
uint32_t endX = glyph->mStartX + glyph->mBitmapWidth;
|
||||||
uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
|
uint32_t endY = glyph->mStartY + glyph->mBitmapHeight;
|
||||||
|
|
||||||
uint32_t cacheWidth = mState->getCacheWidth();
|
CacheTexture *cacheTexture = glyph->mCachedTextureLine->mCacheTexture;
|
||||||
const uint8_t* cacheBuffer = mState->getTextTextureData();
|
uint32_t cacheWidth = cacheTexture->mWidth;
|
||||||
|
const uint8_t* cacheBuffer = cacheTexture->mTexture;
|
||||||
|
|
||||||
uint32_t cacheX = 0, cacheY = 0;
|
uint32_t cacheX = 0, cacheY = 0;
|
||||||
int32_t bX = 0, bY = 0;
|
int32_t bX = 0, bY = 0;
|
||||||
@@ -134,10 +154,9 @@ void Font::drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
|
|||||||
bitmap[bY * bitmapW + bX] = tempCol;
|
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;
|
CachedGlyphInfo* cachedGlyph = NULL;
|
||||||
ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
|
ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
@@ -246,7 +265,7 @@ void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyp
|
|||||||
|
|
||||||
// Get the bitmap for the glyph
|
// Get the bitmap for the glyph
|
||||||
paint->findImage(skiaGlyph);
|
paint->findImage(skiaGlyph);
|
||||||
glyph->mIsValid = mState->cacheBitmap(skiaGlyph, &startX, &startY);
|
mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY);
|
||||||
|
|
||||||
if (!glyph->mIsValid) {
|
if (!glyph->mIsValid) {
|
||||||
return;
|
return;
|
||||||
@@ -260,8 +279,8 @@ void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyp
|
|||||||
glyph->mBitmapWidth = skiaGlyph.fWidth;
|
glyph->mBitmapWidth = skiaGlyph.fWidth;
|
||||||
glyph->mBitmapHeight = skiaGlyph.fHeight;
|
glyph->mBitmapHeight = skiaGlyph.fHeight;
|
||||||
|
|
||||||
uint32_t cacheWidth = mState->getCacheWidth();
|
uint32_t cacheWidth = glyph->mCachedTextureLine->mCacheTexture->mWidth;
|
||||||
uint32_t cacheHeight = mState->getCacheHeight();
|
uint32_t cacheHeight = glyph->mCachedTextureLine->mCacheTexture->mHeight;
|
||||||
|
|
||||||
glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
|
glyph->mBitmapMinU = (float) startX / (float) cacheWidth;
|
||||||
glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
|
glyph->mBitmapMinV = (float) startY / (float) cacheHeight;
|
||||||
@@ -271,7 +290,7 @@ void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyp
|
|||||||
mState->mUploadTexture = true;
|
mState->mUploadTexture = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Font::CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph) {
|
CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph) {
|
||||||
CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
|
CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
|
||||||
mCachedGlyphs.add(glyph, newGlyph);
|
mCachedGlyphs.add(glyph, newGlyph);
|
||||||
|
|
||||||
@@ -320,25 +339,29 @@ FontRenderer::FontRenderer() {
|
|||||||
mInitialized = false;
|
mInitialized = false;
|
||||||
mMaxNumberOfQuads = 1024;
|
mMaxNumberOfQuads = 1024;
|
||||||
mCurrentQuadIndex = 0;
|
mCurrentQuadIndex = 0;
|
||||||
mTextureId = 0;
|
|
||||||
|
|
||||||
mTextMeshPtr = NULL;
|
mTextMeshPtr = NULL;
|
||||||
mTextTexture = NULL;
|
mCurrentCacheTexture = NULL;
|
||||||
|
mLastCacheTexture = NULL;
|
||||||
|
mCacheTextureSmall = NULL;
|
||||||
|
mCacheTexture128 = NULL;
|
||||||
|
mCacheTexture256 = NULL;
|
||||||
|
mCacheTexture512 = NULL;
|
||||||
|
|
||||||
mIndexBufferID = 0;
|
mIndexBufferID = 0;
|
||||||
|
|
||||||
mCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
|
mSmallCacheWidth = DEFAULT_TEXT_CACHE_WIDTH;
|
||||||
mCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
|
mSmallCacheHeight = DEFAULT_TEXT_CACHE_HEIGHT;
|
||||||
|
|
||||||
char property[PROPERTY_VALUE_MAX];
|
char property[PROPERTY_VALUE_MAX];
|
||||||
if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
|
if (property_get(PROPERTY_TEXT_CACHE_WIDTH, property, NULL) > 0) {
|
||||||
if (sLogFontRendererCreate) {
|
if (sLogFontRendererCreate) {
|
||||||
INIT_LOGD(" Setting text cache width to %s pixels", property);
|
INIT_LOGD(" Setting text cache width to %s pixels", property);
|
||||||
}
|
}
|
||||||
mCacheWidth = atoi(property);
|
mSmallCacheWidth = atoi(property);
|
||||||
} else {
|
} else {
|
||||||
if (sLogFontRendererCreate) {
|
if (sLogFontRendererCreate) {
|
||||||
INIT_LOGD(" Using default text cache width of %i pixels", mCacheWidth);
|
INIT_LOGD(" Using default text cache width of %i pixels", mSmallCacheWidth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -346,10 +369,10 @@ FontRenderer::FontRenderer() {
|
|||||||
if (sLogFontRendererCreate) {
|
if (sLogFontRendererCreate) {
|
||||||
INIT_LOGD(" Setting text cache width to %s pixels", property);
|
INIT_LOGD(" Setting text cache width to %s pixels", property);
|
||||||
}
|
}
|
||||||
mCacheHeight = atoi(property);
|
mSmallCacheHeight = atoi(property);
|
||||||
} else {
|
} else {
|
||||||
if (sLogFontRendererCreate) {
|
if (sLogFontRendererCreate) {
|
||||||
INIT_LOGD(" Using default text cache height of %i pixels", mCacheHeight);
|
INIT_LOGD(" Using default text cache height of %i pixels", mSmallCacheHeight);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -364,11 +387,10 @@ FontRenderer::~FontRenderer() {
|
|||||||
|
|
||||||
if (mInitialized) {
|
if (mInitialized) {
|
||||||
delete[] mTextMeshPtr;
|
delete[] mTextMeshPtr;
|
||||||
delete[] mTextTexture;
|
delete mCacheTextureSmall;
|
||||||
}
|
delete mCacheTexture128;
|
||||||
|
delete mCacheTexture256;
|
||||||
if (mTextureId) {
|
delete mCacheTexture512;
|
||||||
glDeleteTextures(1, &mTextureId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector<Font*> fontsToDereference = mActiveFonts;
|
Vector<Font*> fontsToDereference = mActiveFonts;
|
||||||
@@ -390,20 +412,21 @@ void FontRenderer::flushAllAndInvalidate() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) {
|
uint8_t* FontRenderer::allocateTextureMemory(int width, int height) {
|
||||||
|
uint8_t* textureMemory = new uint8_t[width * height];
|
||||||
|
memset(textureMemory, 0, width * height * sizeof(uint8_t));
|
||||||
|
|
||||||
|
return textureMemory;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 the glyph is too tall, don't cache it
|
||||||
if (glyph.fHeight + 2 > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
|
if (glyph.fHeight + TEXTURE_BORDER_SIZE > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
|
||||||
if (mCacheHeight < MAX_TEXT_CACHE_HEIGHT) {
|
LOGE("Font size to large to fit in cache. width, height = %i, %i",
|
||||||
// Default cache not large enough for large glyphs - resize cache to
|
(int) glyph.fWidth, (int) glyph.fHeight);
|
||||||
// max size and try again
|
return;
|
||||||
flushAllAndInvalidate();
|
|
||||||
initTextTexture(true);
|
|
||||||
}
|
|
||||||
if (glyph.fHeight + 2 > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
|
|
||||||
LOGE("Font size to large to fit in cache. width, height = %i, %i",
|
|
||||||
(int) glyph.fWidth, (int) glyph.fHeight);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now copy the bitmap into the cache texture
|
// Now copy the bitmap into the cache texture
|
||||||
@@ -411,9 +434,11 @@ bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint3
|
|||||||
uint32_t startY = 0;
|
uint32_t startY = 0;
|
||||||
|
|
||||||
bool bitmapFit = false;
|
bool bitmapFit = false;
|
||||||
|
CacheTextureLine *cacheLine;
|
||||||
for (uint32_t i = 0; i < mCacheLines.size(); i++) {
|
for (uint32_t i = 0; i < mCacheLines.size(); i++) {
|
||||||
bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
|
bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
|
||||||
if (bitmapFit) {
|
if (bitmapFit) {
|
||||||
|
cacheLine = mCacheLines[i];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -426,27 +451,33 @@ bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint3
|
|||||||
for (uint32_t i = 0; i < mCacheLines.size(); i++) {
|
for (uint32_t i = 0; i < mCacheLines.size(); i++) {
|
||||||
bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
|
bitmapFit = mCacheLines[i]->fitBitmap(glyph, &startX, &startY);
|
||||||
if (bitmapFit) {
|
if (bitmapFit) {
|
||||||
|
cacheLine = mCacheLines[i];
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if we still don't fit, something is wrong and we shouldn't draw
|
// if we still don't fit, something is wrong and we shouldn't draw
|
||||||
if (!bitmapFit) {
|
if (!bitmapFit) {
|
||||||
LOGE("Bitmap doesn't fit in cache. width, height = %i, %i",
|
return;
|
||||||
(int) glyph.fWidth, (int) glyph.fHeight);
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cachedGlyph->mCachedTextureLine = cacheLine;
|
||||||
|
|
||||||
*retOriginX = startX;
|
*retOriginX = startX;
|
||||||
*retOriginY = startY;
|
*retOriginY = startY;
|
||||||
|
|
||||||
uint32_t endX = startX + glyph.fWidth;
|
uint32_t endX = startX + glyph.fWidth;
|
||||||
uint32_t endY = startY + glyph.fHeight;
|
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
|
||||||
|
cacheTexture->mTexture = allocateTextureMemory(cacheTexture->mWidth, cacheTexture->mHeight);
|
||||||
|
}
|
||||||
|
uint8_t* cacheBuffer = cacheTexture->mTexture;
|
||||||
uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
|
uint8_t* bitmapBuffer = (uint8_t*) glyph.fImage;
|
||||||
unsigned int stride = glyph.rowBytes();
|
unsigned int stride = glyph.rowBytes();
|
||||||
|
|
||||||
@@ -457,30 +488,17 @@ bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint3
|
|||||||
cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
|
cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
cachedGlyph->mIsValid = true;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FontRenderer::initTextTexture(bool largeFonts) {
|
CacheTexture* FontRenderer::createCacheTexture(int width, int height, bool allocate) {
|
||||||
mCacheLines.clear();
|
uint8_t* textureMemory = allocate ? allocateTextureMemory(width, height) : NULL;
|
||||||
if (largeFonts) {
|
GLuint textureId;
|
||||||
mCacheWidth = MAX_TEXT_CACHE_WIDTH;
|
glGenTextures(1, &textureId);
|
||||||
mCacheHeight = MAX_TEXT_CACHE_HEIGHT;
|
glBindTexture(GL_TEXTURE_2D, textureId);
|
||||||
}
|
|
||||||
|
|
||||||
mTextTexture = new uint8_t[mCacheWidth * mCacheHeight];
|
|
||||||
memset(mTextTexture, 0, mCacheWidth * mCacheHeight * sizeof(uint8_t));
|
|
||||||
|
|
||||||
mUploadTexture = false;
|
|
||||||
|
|
||||||
if (mTextureId != 0) {
|
|
||||||
glDeleteTextures(1, &mTextureId);
|
|
||||||
}
|
|
||||||
glGenTextures(1, &mTextureId);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, mTextureId);
|
|
||||||
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||||
// Initialize texture dimensions
|
// Initialize texture dimensions
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, mCacheWidth, mCacheHeight, 0,
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0,
|
||||||
GL_ALPHA, GL_UNSIGNED_BYTE, 0);
|
GL_ALPHA, GL_UNSIGNED_BYTE, 0);
|
||||||
|
|
||||||
mLinearFiltering = false;
|
mLinearFiltering = false;
|
||||||
@@ -490,30 +508,55 @@ void FontRenderer::initTextTexture(bool largeFonts) {
|
|||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
|
|
||||||
// Split up our cache texture into lines of certain widths
|
return new CacheTexture(textureMemory, textureId, width, height);
|
||||||
int nextLine = 0;
|
}
|
||||||
mCacheLines.push(new CacheTextureLine(mCacheWidth, 18, nextLine, 0));
|
|
||||||
nextLine += mCacheLines.top()->mMaxHeight;
|
void FontRenderer::initTextTexture() {
|
||||||
mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
|
mCacheLines.clear();
|
||||||
nextLine += mCacheLines.top()->mMaxHeight;
|
|
||||||
mCacheLines.push(new CacheTextureLine(mCacheWidth, 26, nextLine, 0));
|
// Next, use other, separate caches for large glyphs.
|
||||||
nextLine += mCacheLines.top()->mMaxHeight;
|
uint16_t maxWidth = 0;
|
||||||
mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
|
if (Caches::hasInstance()) {
|
||||||
nextLine += mCacheLines.top()->mMaxHeight;
|
maxWidth = Caches::getInstance().maxTextureSize;
|
||||||
mCacheLines.push(new CacheTextureLine(mCacheWidth, 34, nextLine, 0));
|
|
||||||
nextLine += mCacheLines.top()->mMaxHeight;
|
|
||||||
mCacheLines.push(new CacheTextureLine(mCacheWidth, 42, nextLine, 0));
|
|
||||||
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));
|
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;
|
||||||
|
|
||||||
|
mUploadTexture = false;
|
||||||
|
// Split up our default cache texture into lines of certain widths
|
||||||
|
int nextLine = 0;
|
||||||
|
mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 18, nextLine, 0, mCacheTextureSmall));
|
||||||
|
nextLine += mCacheLines.top()->mMaxHeight;
|
||||||
|
mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall));
|
||||||
|
nextLine += mCacheLines.top()->mMaxHeight;
|
||||||
|
mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall));
|
||||||
|
nextLine += mCacheLines.top()->mMaxHeight;
|
||||||
|
mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall));
|
||||||
|
nextLine += mCacheLines.top()->mMaxHeight;
|
||||||
|
mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall));
|
||||||
|
nextLine += mCacheLines.top()->mMaxHeight;
|
||||||
|
mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 42, nextLine, 0, mCacheTextureSmall));
|
||||||
|
nextLine += mCacheLines.top()->mMaxHeight;
|
||||||
|
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
|
// Avoid having to reallocate memory and render quad by quad
|
||||||
@@ -568,22 +611,22 @@ void FontRenderer::checkInit() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void FontRenderer::checkTextureUpdate() {
|
void FontRenderer::checkTextureUpdate() {
|
||||||
if (!mUploadTexture) {
|
if (!mUploadTexture && mLastCacheTexture == mCurrentCacheTexture) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, mTextureId);
|
|
||||||
|
|
||||||
// Iterate over all the cache lines and see which ones need to be updated
|
// Iterate over all the cache lines and see which ones need to be updated
|
||||||
for (uint32_t i = 0; i < mCacheLines.size(); i++) {
|
for (uint32_t i = 0; i < mCacheLines.size(); i++) {
|
||||||
CacheTextureLine* cl = mCacheLines[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 xOffset = 0;
|
||||||
uint32_t yOffset = cl->mCurrentRow;
|
uint32_t yOffset = cl->mCurrentRow;
|
||||||
uint32_t width = mCacheWidth;
|
uint32_t width = cl->mMaxWidth;
|
||||||
uint32_t height = cl->mMaxHeight;
|
uint32_t height = cl->mMaxHeight;
|
||||||
void* textureData = mTextTexture + yOffset * width;
|
void* textureData = cacheTexture->mTexture + (yOffset * width);
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
|
||||||
glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
|
glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
|
||||||
GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
|
GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
|
||||||
|
|
||||||
@@ -591,6 +634,9 @@ void FontRenderer::checkTextureUpdate() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId);
|
||||||
|
mLastCacheTexture = mCurrentCacheTexture;
|
||||||
|
|
||||||
mUploadTexture = false;
|
mUploadTexture = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -617,12 +663,21 @@ void FontRenderer::issueDrawCommand() {
|
|||||||
void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
|
void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1,
|
||||||
float x2, float y2, float u2, float v2,
|
float x2, float y2, float u2, float v2,
|
||||||
float x3, float y3, float u3, float v3,
|
float x3, float y3, float u3, float v3,
|
||||||
float x4, float y4, float u4, float v4) {
|
float x4, float y4, float u4, float v4, CacheTexture* texture) {
|
||||||
|
|
||||||
if (mClip &&
|
if (mClip &&
|
||||||
(x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
|
(x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) {
|
||||||
return;
|
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 vertsPerQuad = 4;
|
||||||
const uint32_t floatsPerVert = 4;
|
const uint32_t floatsPerVert = 4;
|
||||||
|
|||||||
@@ -55,6 +55,77 @@ namespace uirenderer {
|
|||||||
|
|
||||||
class FontRenderer;
|
class FontRenderer;
|
||||||
|
|
||||||
|
class CacheTexture {
|
||||||
|
public:
|
||||||
|
CacheTexture(){}
|
||||||
|
CacheTexture(uint8_t* texture, GLuint textureId, uint16_t width, uint16_t height) :
|
||||||
|
mTexture(texture), mTextureId(textureId), mWidth(width), mHeight(height) {}
|
||||||
|
~CacheTexture() {
|
||||||
|
if (mTexture != NULL) {
|
||||||
|
delete[] mTexture;
|
||||||
|
}
|
||||||
|
if (mTextureId != 0) {
|
||||||
|
glDeleteTextures(1, &mTextureId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t* mTexture;
|
||||||
|
GLuint mTextureId;
|
||||||
|
uint16_t mWidth;
|
||||||
|
uint16_t mHeight;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CacheTextureLine {
|
||||||
|
public:
|
||||||
|
CacheTextureLine(uint16_t maxWidth, uint16_t maxHeight, uint32_t currentRow,
|
||||||
|
uint32_t currentCol, CacheTexture* cacheTexture):
|
||||||
|
mMaxHeight(maxHeight),
|
||||||
|
mMaxWidth(maxWidth),
|
||||||
|
mCurrentRow(currentRow),
|
||||||
|
mCurrentCol(currentCol),
|
||||||
|
mDirty(false),
|
||||||
|
mCacheTexture(cacheTexture){
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY);
|
||||||
|
|
||||||
|
uint16_t mMaxHeight;
|
||||||
|
uint16_t mMaxWidth;
|
||||||
|
uint32_t mCurrentRow;
|
||||||
|
uint32_t mCurrentCol;
|
||||||
|
bool mDirty;
|
||||||
|
CacheTexture *mCacheTexture;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CachedGlyphInfo {
|
||||||
|
// Has the cache been invalidated?
|
||||||
|
bool mIsValid;
|
||||||
|
// Location of the cached glyph in the bitmap
|
||||||
|
// in case we need to resize the texture or
|
||||||
|
// render to bitmap
|
||||||
|
uint32_t mStartX;
|
||||||
|
uint32_t mStartY;
|
||||||
|
uint32_t mBitmapWidth;
|
||||||
|
uint32_t mBitmapHeight;
|
||||||
|
// Also cache texture coords for the quad
|
||||||
|
float mBitmapMinU;
|
||||||
|
float mBitmapMinV;
|
||||||
|
float mBitmapMaxU;
|
||||||
|
float mBitmapMaxV;
|
||||||
|
// Minimize how much we call freetype
|
||||||
|
uint32_t mGlyphIndex;
|
||||||
|
uint32_t mAdvanceX;
|
||||||
|
uint32_t mAdvanceY;
|
||||||
|
// Values below contain a glyph's origin in the bitmap
|
||||||
|
int32_t mBitmapLeft;
|
||||||
|
int32_t mBitmapTop;
|
||||||
|
// Auto-kerning
|
||||||
|
SkFixed mLsbDelta;
|
||||||
|
SkFixed mRsbDelta;
|
||||||
|
CacheTextureLine* mCachedTextureLine;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
// Font
|
// Font
|
||||||
///////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -101,33 +172,6 @@ protected:
|
|||||||
void measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
|
void measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
|
||||||
int numGlyphs, Rect *bounds);
|
int numGlyphs, Rect *bounds);
|
||||||
|
|
||||||
struct CachedGlyphInfo {
|
|
||||||
// Has the cache been invalidated?
|
|
||||||
bool mIsValid;
|
|
||||||
// Location of the cached glyph in the bitmap
|
|
||||||
// in case we need to resize the texture or
|
|
||||||
// render to bitmap
|
|
||||||
uint32_t mStartX;
|
|
||||||
uint32_t mStartY;
|
|
||||||
uint32_t mBitmapWidth;
|
|
||||||
uint32_t mBitmapHeight;
|
|
||||||
// Also cache texture coords for the quad
|
|
||||||
float mBitmapMinU;
|
|
||||||
float mBitmapMinV;
|
|
||||||
float mBitmapMaxU;
|
|
||||||
float mBitmapMaxV;
|
|
||||||
// Minimize how much we call freetype
|
|
||||||
uint32_t mGlyphIndex;
|
|
||||||
uint32_t mAdvanceX;
|
|
||||||
uint32_t mAdvanceY;
|
|
||||||
// Values below contain a glyph's origin in the bitmap
|
|
||||||
int32_t mBitmapLeft;
|
|
||||||
int32_t mBitmapTop;
|
|
||||||
// Auto-kerning
|
|
||||||
SkFixed mLsbDelta;
|
|
||||||
SkFixed mRsbDelta;
|
|
||||||
};
|
|
||||||
|
|
||||||
Font(FontRenderer* state, uint32_t fontId, float fontSize, int flags, uint32_t italicStyle,
|
Font(FontRenderer* state, uint32_t fontId, float fontSize, int flags, uint32_t italicStyle,
|
||||||
uint32_t scaleX, SkPaint::Style style, uint32_t strokeWidth);
|
uint32_t scaleX, SkPaint::Style style, uint32_t strokeWidth);
|
||||||
|
|
||||||
@@ -209,19 +253,28 @@ public:
|
|||||||
mLinearFiltering = linearFiltering;
|
mLinearFiltering = linearFiltering;
|
||||||
const GLenum filtering = linearFiltering ? GL_LINEAR : GL_NEAREST;
|
const GLenum filtering = linearFiltering ? GL_LINEAR : GL_NEAREST;
|
||||||
|
|
||||||
glBindTexture(GL_TEXTURE_2D, mTextureId);
|
glBindTexture(GL_TEXTURE_2D, mCurrentCacheTexture->mTextureId);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
|
||||||
}
|
}
|
||||||
return mTextureId;
|
return mCurrentCacheTexture->mTextureId;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t getCacheWidth() const {
|
uint32_t getCacheSize() const {
|
||||||
return mCacheWidth;
|
uint32_t size = 0;
|
||||||
}
|
if (mCacheTextureSmall != NULL && mCacheTextureSmall->mTexture != NULL) {
|
||||||
|
size += mCacheTextureSmall->mWidth * mCacheTextureSmall->mHeight;
|
||||||
uint32_t getCacheHeight() const {
|
}
|
||||||
return mCacheHeight;
|
if (mCacheTexture128 != NULL && mCacheTexture128->mTexture != NULL) {
|
||||||
|
size += mCacheTexture128->mWidth * mCacheTexture128->mHeight;
|
||||||
|
}
|
||||||
|
if (mCacheTexture256 != NULL && mCacheTexture256->mTexture != NULL) {
|
||||||
|
size += mCacheTexture256->mWidth * mCacheTexture256->mHeight;
|
||||||
|
}
|
||||||
|
if (mCacheTexture512 != NULL && mCacheTexture512->mTexture != NULL) {
|
||||||
|
size += mCacheTexture512->mWidth * mCacheTexture512->mHeight;
|
||||||
|
}
|
||||||
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
@@ -229,41 +282,11 @@ protected:
|
|||||||
|
|
||||||
const uint8_t* mGammaTable;
|
const uint8_t* mGammaTable;
|
||||||
|
|
||||||
struct CacheTextureLine {
|
uint8_t* allocateTextureMemory(int width, int height);
|
||||||
uint16_t mMaxHeight;
|
void initTextTexture();
|
||||||
uint16_t mMaxWidth;
|
CacheTexture *createCacheTexture(int width, int height, bool allocate);
|
||||||
uint32_t mCurrentRow;
|
void cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph,
|
||||||
uint32_t mCurrentCol;
|
uint32_t *retOriginX, uint32_t *retOriginY);
|
||||||
bool mDirty;
|
|
||||||
|
|
||||||
CacheTextureLine(uint16_t maxWidth, uint16_t maxHeight, uint32_t currentRow,
|
|
||||||
uint32_t currentCol):
|
|
||||||
mMaxHeight(maxHeight),
|
|
||||||
mMaxWidth(maxWidth),
|
|
||||||
mCurrentRow(currentRow),
|
|
||||||
mCurrentCol(currentCol),
|
|
||||||
mDirty(false) {
|
|
||||||
}
|
|
||||||
|
|
||||||
bool fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) {
|
|
||||||
if (glyph.fHeight + 2 > mMaxHeight) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mCurrentCol + glyph.fWidth + 2 < mMaxWidth) {
|
|
||||||
*retOriginX = mCurrentCol + 1;
|
|
||||||
*retOriginY = mCurrentRow + 1;
|
|
||||||
mCurrentCol += glyph.fWidth + 2;
|
|
||||||
mDirty = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
void initTextTexture(bool largeFonts = false);
|
|
||||||
bool cacheBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY);
|
|
||||||
|
|
||||||
void flushAllAndInvalidate();
|
void flushAllAndInvalidate();
|
||||||
void initVertexArrayBuffers();
|
void initVertexArrayBuffers();
|
||||||
@@ -277,10 +300,10 @@ protected:
|
|||||||
void appendMeshQuad(float x1, float y1, float u1, float v1,
|
void appendMeshQuad(float x1, float y1, float u1, float v1,
|
||||||
float x2, float y2, float u2, float v2,
|
float x2, float y2, float u2, float v2,
|
||||||
float x3, float y3, float u3, float v3,
|
float x3, float y3, float u3, float v3,
|
||||||
float x4, float y4, float u4, float v4);
|
float x4, float y4, float u4, float v4, CacheTexture* texture);
|
||||||
|
|
||||||
uint32_t mCacheWidth;
|
uint32_t mSmallCacheWidth;
|
||||||
uint32_t mCacheHeight;
|
uint32_t mSmallCacheHeight;
|
||||||
|
|
||||||
Vector<CacheTextureLine*> mCacheLines;
|
Vector<CacheTextureLine*> mCacheLines;
|
||||||
uint32_t getRemainingCacheCapacity();
|
uint32_t getRemainingCacheCapacity();
|
||||||
@@ -288,12 +311,14 @@ protected:
|
|||||||
Font* mCurrentFont;
|
Font* mCurrentFont;
|
||||||
Vector<Font*> mActiveFonts;
|
Vector<Font*> mActiveFonts;
|
||||||
|
|
||||||
// Texture to cache glyph bitmaps
|
CacheTexture* mCurrentCacheTexture;
|
||||||
uint8_t* mTextTexture;
|
CacheTexture* mLastCacheTexture;
|
||||||
const uint8_t* getTextTextureData() const {
|
CacheTexture* mCacheTextureSmall;
|
||||||
return mTextTexture;
|
CacheTexture* mCacheTexture128;
|
||||||
}
|
CacheTexture* mCacheTexture256;
|
||||||
GLuint mTextureId;
|
CacheTexture* mCacheTexture512;
|
||||||
|
|
||||||
|
|
||||||
void checkTextureUpdate();
|
void checkTextureUpdate();
|
||||||
bool mUploadTexture;
|
bool mUploadTexture;
|
||||||
|
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ struct GammaFontRenderer {
|
|||||||
FontRenderer* renderer = mRenderers[fontRenderer];
|
FontRenderer* renderer = mRenderers[fontRenderer];
|
||||||
if (!renderer) return 0;
|
if (!renderer) return 0;
|
||||||
|
|
||||||
return renderer->getCacheHeight() * renderer->getCacheWidth();
|
return renderer->getCacheSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
Reference in New Issue
Block a user