summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--libs/hwui/Android.mk2
-rw-r--r--libs/hwui/FontRenderer.cpp590
-rw-r--r--libs/hwui/FontRenderer.h254
-rw-r--r--libs/hwui/font/CacheTexture.cpp172
-rw-r--r--libs/hwui/font/CacheTexture.h122
-rw-r--r--libs/hwui/font/CachedGlyphInfo.h58
-rw-r--r--libs/hwui/font/Font.cpp446
-rw-r--r--libs/hwui/font/Font.h130
-rw-r--r--libs/hwui/font/FontUtil.h60
9 files changed, 1005 insertions, 829 deletions
diff --git a/libs/hwui/Android.mk b/libs/hwui/Android.mk
index c3a07a1..c0f79df 100644
--- a/libs/hwui/Android.mk
+++ b/libs/hwui/Android.mk
@@ -6,6 +6,8 @@ include $(CLEAR_VARS)
ifeq ($(USE_OPENGL_RENDERER),true)
LOCAL_SRC_FILES:= \
utils/SortedListImpl.cpp \
+ font/CacheTexture.cpp \
+ font/Font.cpp \
FontRenderer.cpp \
GammaFontRenderer.cpp \
Caches.cpp \
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 95ccdfc..e66ab54 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -25,585 +25,12 @@
#include "Caches.h"
#include "Debug.h"
#include "FontRenderer.h"
-#include "Caches.h"
+#include "Rect.h"
namespace android {
namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
-
-#define DEFAULT_TEXT_SMALL_CACHE_WIDTH 1024
-#define DEFAULT_TEXT_SMALL_CACHE_HEIGHT 256
-#define DEFAULT_TEXT_LARGE_CACHE_WIDTH 2048
-#define DEFAULT_TEXT_LARGE_CACHE_HEIGHT 512
-#define CACHE_BLOCK_ROUNDING_SIZE 4
-
-#define AUTO_KERN(prev, next) (((next) - (prev) + 32) >> 6 << 16)
-
-///////////////////////////////////////////////////////////////////////////////
-// CacheBlock
-///////////////////////////////////////////////////////////////////////////////
-
-/**
- * Insert new block into existing linked list of blocks. Blocks are sorted in increasing-width
- * order, except for the final block (the remainder space at the right, since we fill from the
- * left).
- */
-CacheBlock* CacheBlock::insertBlock(CacheBlock* head, CacheBlock *newBlock) {
-#if DEBUG_FONT_RENDERER
- ALOGD("insertBlock: this, x, y, w, h = %p, %d, %d, %d, %d",
- newBlock, newBlock->mX, newBlock->mY,
- newBlock->mWidth, newBlock->mHeight);
-#endif
- CacheBlock *currBlock = head;
- CacheBlock *prevBlock = NULL;
- while (currBlock && currBlock->mY != TEXTURE_BORDER_SIZE) {
- if (newBlock->mWidth < currBlock->mWidth) {
- newBlock->mNext = currBlock;
- newBlock->mPrev = prevBlock;
- currBlock->mPrev = newBlock;
- if (prevBlock) {
- prevBlock->mNext = newBlock;
- return head;
- } else {
- return newBlock;
- }
- }
- prevBlock = currBlock;
- currBlock = currBlock->mNext;
- }
- // new block larger than all others - insert at end (but before the remainder space, if there)
- newBlock->mNext = currBlock;
- newBlock->mPrev = prevBlock;
- if (currBlock) {
- currBlock->mPrev = newBlock;
- }
- if (prevBlock) {
- prevBlock->mNext = newBlock;
- return head;
- } else {
- return newBlock;
- }
-}
-
-CacheBlock* CacheBlock::removeBlock(CacheBlock* head, CacheBlock *blockToRemove) {
-#if DEBUG_FONT_RENDERER
- ALOGD("removeBlock: this, x, y, w, h = %p, %d, %d, %d, %d",
- blockToRemove, blockToRemove->mX, blockToRemove->mY,
- blockToRemove->mWidth, blockToRemove->mHeight);
-#endif
- CacheBlock* newHead = head;
- CacheBlock* nextBlock = blockToRemove->mNext;
- CacheBlock* prevBlock = blockToRemove->mPrev;
- if (prevBlock) {
- prevBlock->mNext = nextBlock;
- } else {
- newHead = nextBlock;
- }
- if (nextBlock) {
- nextBlock->mPrev = prevBlock;
- }
- delete blockToRemove;
- return newHead;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// CacheTexture
-///////////////////////////////////////////////////////////////////////////////
-
-bool CacheTexture::fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) {
- if (glyph.fHeight + TEXTURE_BORDER_SIZE > mHeight) {
- return false;
- }
-
- uint16_t glyphW = glyph.fWidth + TEXTURE_BORDER_SIZE;
- uint16_t glyphH = glyph.fHeight + TEXTURE_BORDER_SIZE;
- // roundedUpW equals glyphW to the next multiple of CACHE_BLOCK_ROUNDING_SIZE.
- // This columns for glyphs that are close but not necessarily exactly the same size. It trades
- // off the loss of a few pixels for some glyphs against the ability to store more glyphs
- // of varying sizes in one block.
- uint16_t roundedUpW =
- (glyphW + CACHE_BLOCK_ROUNDING_SIZE - 1) & -CACHE_BLOCK_ROUNDING_SIZE;
- CacheBlock *cacheBlock = mCacheBlocks;
- while (cacheBlock) {
- // Store glyph in this block iff: it fits the block's remaining space and:
- // it's the remainder space (mY == 0) or there's only enough height for this one glyph
- // or it's within ROUNDING_SIZE of the block width
- if (roundedUpW <= cacheBlock->mWidth && glyphH <= cacheBlock->mHeight &&
- (cacheBlock->mY == TEXTURE_BORDER_SIZE ||
- (cacheBlock->mWidth - roundedUpW < CACHE_BLOCK_ROUNDING_SIZE))) {
- if (cacheBlock->mHeight - glyphH < glyphH) {
- // Only enough space for this glyph - don't bother rounding up the width
- roundedUpW = glyphW;
- }
- *retOriginX = cacheBlock->mX;
- *retOriginY = cacheBlock->mY;
- // If this is the remainder space, create a new cache block for this column. Otherwise,
- // adjust the info about this column.
- if (cacheBlock->mY == TEXTURE_BORDER_SIZE) {
- uint16_t oldX = cacheBlock->mX;
- // Adjust remainder space dimensions
- cacheBlock->mWidth -= roundedUpW;
- cacheBlock->mX += roundedUpW;
- if (mHeight - glyphH >= glyphH) {
- // There's enough height left over to create a new CacheBlock
- CacheBlock *newBlock = new CacheBlock(oldX, glyphH + TEXTURE_BORDER_SIZE,
- roundedUpW, mHeight - glyphH - TEXTURE_BORDER_SIZE);
-#if DEBUG_FONT_RENDERER
- ALOGD("fitBitmap: Created new block: this, x, y, w, h = %p, %d, %d, %d, %d",
- newBlock, newBlock->mX, newBlock->mY,
- newBlock->mWidth, newBlock->mHeight);
-#endif
- mCacheBlocks = CacheBlock::insertBlock(mCacheBlocks, newBlock);
- }
- } else {
- // Insert into current column and adjust column dimensions
- cacheBlock->mY += glyphH;
- cacheBlock->mHeight -= glyphH;
-#if DEBUG_FONT_RENDERER
- ALOGD("fitBitmap: Added to existing block: this, x, y, w, h = %p, %d, %d, %d, %d",
- cacheBlock, cacheBlock->mX, cacheBlock->mY,
- cacheBlock->mWidth, cacheBlock->mHeight);
-#endif
- }
- if (cacheBlock->mHeight < fmin(glyphH, glyphW)) {
- // If remaining space in this block is too small to be useful, remove it
- mCacheBlocks = CacheBlock::removeBlock(mCacheBlocks, cacheBlock);
- }
- mDirty = true;
-#if DEBUG_FONT_RENDERER
- ALOGD("fitBitmap: current block list:");
- mCacheBlocks->output();
-#endif
- ++mNumGlyphs;
- return true;
- }
- cacheBlock = cacheBlock->mNext;
- }
-#if DEBUG_FONT_RENDERER
- ALOGD("fitBitmap: returning false for glyph of size %d, %d", glyphW, glyphH);
-#endif
- return false;
-}
-
-///////////////////////////////////////////////////////////////////////////////
-// Font
-///////////////////////////////////////////////////////////////////////////////
-
-Font::Font(FontRenderer* state, uint32_t fontId, float fontSize,
- int flags, uint32_t italicStyle, uint32_t scaleX,
- SkPaint::Style style, uint32_t strokeWidth) :
- mState(state), mFontId(fontId), mFontSize(fontSize),
- mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX),
- mStyle(style), mStrokeWidth(mStrokeWidth) {
-}
-
-
-Font::~Font() {
- for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
- if (mState->mActiveFonts[ct] == this) {
- mState->mActiveFonts.removeAt(ct);
- break;
- }
- }
-
- for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
- delete mCachedGlyphs.valueAt(i);
- }
-}
-
-void Font::invalidateTextureCache(CacheTexture *cacheTexture) {
- for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
- CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueAt(i);
- if (cacheTexture == NULL || cachedGlyph->mCacheTexture == cacheTexture) {
- cachedGlyph->mIsValid = false;
- }
- }
-}
-
-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;
-
- int width = (int) glyph->mBitmapWidth;
- int height = (int) 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::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;
-
- float u1 = glyph->mBitmapMinU;
- float u2 = glyph->mBitmapMaxU;
- float v1 = glyph->mBitmapMinV;
- float v2 = glyph->mBitmapMaxV;
-
- int width = (int) glyph->mBitmapWidth;
- int height = (int) glyph->mBitmapHeight;
-
- mState->appendMeshQuad(nPenX, nPenY, u1, v2,
- nPenX + width, nPenY, u2, v2,
- nPenX + width, nPenY - height, u2, v1,
- nPenX, nPenY - height, u1, v1, glyph->mCacheTexture);
-}
-
-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;
-
- CacheTexture *cacheTexture = glyph->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;
- }
- }
-}
-
-void Font::drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
- SkPathMeasure& measure, SkPoint* position, SkVector* tangent) {
- const float halfWidth = glyph->mBitmapWidth * 0.5f;
- const float height = glyph->mBitmapHeight;
-
- vOffset += glyph->mBitmapTop + height;
-
- SkPoint destination[4];
- measure.getPosTan(x + hOffset + glyph->mBitmapLeft + halfWidth, position, tangent);
-
- // Move along the tangent and offset by the normal
- destination[0].set(-tangent->fX * halfWidth - tangent->fY * vOffset,
- -tangent->fY * halfWidth + tangent->fX * vOffset);
- destination[1].set(tangent->fX * halfWidth - tangent->fY * vOffset,
- tangent->fY * halfWidth + tangent->fX * vOffset);
- destination[2].set(destination[1].fX + tangent->fY * height,
- destination[1].fY - tangent->fX * height);
- destination[3].set(destination[0].fX + tangent->fY * height,
- destination[0].fY - tangent->fX * height);
-
- const float u1 = glyph->mBitmapMinU;
- const float u2 = glyph->mBitmapMaxU;
- const float v1 = glyph->mBitmapMinV;
- const float v2 = glyph->mBitmapMaxV;
-
- mState->appendRotatedMeshQuad(
- position->fX + destination[0].fX,
- position->fY + destination[0].fY, u1, v2,
- position->fX + destination[1].fX,
- position->fY + destination[1].fY, u2, v2,
- position->fX + destination[2].fX,
- position->fY + destination[2].fY, u2, v1,
- position->fX + destination[3].fX,
- position->fY + destination[3].fY, u1, v1,
- glyph->mCacheTexture);
-}
-
-CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool precaching) {
- CachedGlyphInfo* cachedGlyph = NULL;
- ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
- if (index >= 0) {
- cachedGlyph = mCachedGlyphs.valueAt(index);
- } else {
- cachedGlyph = cacheGlyph(paint, textUnit, precaching);
- }
-
- // Is the glyph still in texture cache?
- if (!cachedGlyph->mIsValid) {
- const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit);
- updateGlyphCache(paint, skiaGlyph, cachedGlyph, precaching);
- }
-
- return cachedGlyph;
-}
-
-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, NULL);
- } else {
- render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, 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::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
- int numGlyphs, SkPath* path, float hOffset, float vOffset) {
- if (numGlyphs == 0 || text == NULL || len == 0) {
- return;
- }
-
- text += start;
-
- int glyphsCount = 0;
- SkFixed prevRsbDelta = 0;
-
- float penX = 0.0f;
-
- SkPoint position;
- SkVector tangent;
-
- SkPathMeasure measure(*path, false);
- float pathLength = SkScalarToFloat(measure.getLength());
-
- if (paint->getTextAlign() != SkPaint::kLeft_Align) {
- float textWidth = SkScalarToFloat(paint->measureText(text, len));
- float pathOffset = pathLength;
- if (paint->getTextAlign() == SkPaint::kCenter_Align) {
- textWidth *= 0.5f;
- pathOffset *= 0.5f;
- }
- penX += pathOffset - textWidth;
- }
-
- while (glyphsCount < numGlyphs && penX < pathLength) {
- glyph_t glyph = GET_GLYPH(text);
-
- if (IS_END_OF_STRING(glyph)) {
- break;
- }
-
- CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
- penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
- prevRsbDelta = cachedGlyph->mRsbDelta;
-
- if (cachedGlyph->mIsValid) {
- drawCachedGlyph(cachedGlyph, penX, hOffset, vOffset, measure, &position, &tangent);
- }
-
- penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
-
- glyphsCount++;
- }
-}
-
-void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
- int numGlyphs, Rect *bounds, const float* positions) {
- if (bounds == NULL) {
- ALOGE("No return rectangle provided to measure text");
- return;
- }
- bounds->set(1e6, -1e6, -1e6, 1e6);
- render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, positions);
-}
-
-void Font::precache(SkPaint* paint, const char* text, int numGlyphs) {
-
- if (numGlyphs == 0 || text == NULL) {
- return;
- }
- int glyphsCount = 0;
-
- 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, true);
-
- glyphsCount++;
- }
-}
-
-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, const float* positions) {
- if (numGlyphs == 0 || text == NULL || len == 0) {
- return;
- }
-
- static RenderGlyph gRenderGlyph[] = {
- &android::uirenderer::Font::drawCachedGlyph,
- &android::uirenderer::Font::drawCachedGlyphBitmap,
- &android::uirenderer::Font::measureCachedGlyph
- };
- RenderGlyph render = gRenderGlyph[mode];
-
- text += start;
- int glyphsCount = 0;
-
- if (CC_LIKELY(positions == NULL)) {
- SkFixed prevRsbDelta = 0;
-
- float penX = x + 0.5f;
- int penY = y;
-
- 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(AUTO_KERN(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);
-
- // Reached the end of the string
- if (IS_END_OF_STRING(glyph)) {
- break;
- }
-
- 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++;
- }
- }
-}
-
-void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph,
- bool precaching) {
- glyph->mAdvanceX = skiaGlyph.fAdvanceX;
- glyph->mAdvanceY = skiaGlyph.fAdvanceY;
- glyph->mBitmapLeft = skiaGlyph.fLeft;
- glyph->mBitmapTop = skiaGlyph.fTop;
- glyph->mLsbDelta = skiaGlyph.fLsbDelta;
- glyph->mRsbDelta = skiaGlyph.fRsbDelta;
-
- uint32_t startX = 0;
- uint32_t startY = 0;
-
- // Get the bitmap for the glyph
- paint->findImage(skiaGlyph);
- mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY, precaching);
-
- if (!glyph->mIsValid) {
- return;
- }
-
- uint32_t endX = startX + skiaGlyph.fWidth;
- uint32_t endY = startY + skiaGlyph.fHeight;
-
- glyph->mStartX = startX;
- glyph->mStartY = startY;
- glyph->mBitmapWidth = skiaGlyph.fWidth;
- glyph->mBitmapHeight = skiaGlyph.fHeight;
-
- uint32_t cacheWidth = glyph->mCacheTexture->mWidth;
- uint32_t cacheHeight = glyph->mCacheTexture->mHeight;
-
- glyph->mBitmapMinU = startX / (float) cacheWidth;
- glyph->mBitmapMinV = startY / (float) cacheHeight;
- glyph->mBitmapMaxU = endX / (float) cacheWidth;
- glyph->mBitmapMaxV = endY / (float) cacheHeight;
-
- mState->mUploadTexture = true;
-}
-
-CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching) {
- CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
- mCachedGlyphs.add(glyph, newGlyph);
-
- const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
- newGlyph->mGlyphIndex = skiaGlyph.fID;
- newGlyph->mIsValid = false;
-
- updateGlyphCache(paint, skiaGlyph, newGlyph, precaching);
-
- return newGlyph;
-}
-
-Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
- int flags, uint32_t italicStyle, uint32_t scaleX,
- SkPaint::Style style, uint32_t strokeWidth) {
- Vector<Font*> &activeFonts = state->mActiveFonts;
-
- for (uint32_t i = 0; i < activeFonts.size(); i++) {
- Font* font = activeFonts[i];
- if (font->mFontId == fontId && font->mFontSize == fontSize &&
- font->mFlags == flags && font->mItalicStyle == italicStyle &&
- font->mScaleX == scaleX && font->mStyle == style &&
- (style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) {
- return font;
- }
- }
-
- Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle,
- scaleX, style, strokeWidth);
- activeFonts.push(newFont);
- return newFont;
-}
-
-///////////////////////////////////////////////////////////////////////////////
// FontRenderer
///////////////////////////////////////////////////////////////////////////////
@@ -636,20 +63,25 @@ FontRenderer::FontRenderer() {
if (property_get(PROPERTY_TEXT_SMALL_CACHE_WIDTH, property, NULL) > 0) {
mSmallCacheWidth = atoi(property);
}
+
if (property_get(PROPERTY_TEXT_SMALL_CACHE_HEIGHT, property, NULL) > 0) {
mSmallCacheHeight = atoi(property);
}
+
if (property_get(PROPERTY_TEXT_LARGE_CACHE_WIDTH, property, NULL) > 0) {
mLargeCacheWidth = atoi(property);
}
+
if (property_get(PROPERTY_TEXT_LARGE_CACHE_HEIGHT, property, NULL) > 0) {
mLargeCacheHeight = atoi(property);
}
- GLint maxTextureSize = Caches::getInstance().maxTextureSize;
- mSmallCacheWidth = (mSmallCacheWidth > maxTextureSize) ? maxTextureSize : mSmallCacheWidth;
- mSmallCacheHeight = (mSmallCacheHeight > maxTextureSize) ? maxTextureSize : mSmallCacheHeight;
- mLargeCacheWidth = (mLargeCacheWidth > maxTextureSize) ? maxTextureSize : mLargeCacheWidth;
- mLargeCacheHeight = (mLargeCacheHeight > maxTextureSize) ? maxTextureSize : mLargeCacheHeight;
+
+ uint32_t maxTextureSize = (uint32_t) Caches::getInstance().maxTextureSize;
+ mSmallCacheWidth = mSmallCacheWidth > maxTextureSize ? maxTextureSize : mSmallCacheWidth;
+ mSmallCacheHeight = mSmallCacheHeight > maxTextureSize ? maxTextureSize : mSmallCacheHeight;
+ mLargeCacheWidth = mLargeCacheWidth > maxTextureSize ? maxTextureSize : mLargeCacheWidth;
+ mLargeCacheHeight = mLargeCacheHeight > maxTextureSize ? maxTextureSize : mLargeCacheHeight;
+
if (sLogFontRendererCreate) {
INIT_LOGD(" Text cache sizes, in pixels: %i x %i, %i x %i, %i x %i, %i x %i",
mSmallCacheWidth, mSmallCacheHeight,
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 241b73e..58aa9c3 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -17,268 +17,22 @@
#ifndef ANDROID_HWUI_FONT_RENDERER_H
#define ANDROID_HWUI_FONT_RENDERER_H
-#include <utils/String8.h>
-#include <utils/String16.h>
#include <utils/Vector.h>
-#include <utils/KeyedVector.h>
-#include <SkScalerContext.h>
#include <SkPaint.h>
-#include <SkPathMeasure.h>
-#include <SkPoint.h>
#include <GLES2/gl2.h>
-#include "Rect.h"
+#include "font/FontUtil.h"
+#include "font/CacheTexture.h"
+#include "font/CachedGlyphInfo.h"
+#include "font/Font.h"
#include "Properties.h"
namespace android {
namespace uirenderer {
///////////////////////////////////////////////////////////////////////////////
-// Defines
-///////////////////////////////////////////////////////////////////////////////
-
-#if RENDER_TEXT_AS_GLYPHS
- typedef uint16_t glyph_t;
- #define TO_GLYPH(g) g
- #define GET_METRICS(paint, glyph) paint->getGlyphMetrics(glyph)
- #define GET_GLYPH(text) nextGlyph((const uint16_t**) &text)
- #define IS_END_OF_STRING(glyph) false
-#else
- typedef SkUnichar glyph_t;
- #define TO_GLYPH(g) ((SkUnichar) g)
- #define GET_METRICS(paint, glyph) paint->getUnicharMetrics(glyph)
- #define GET_GLYPH(text) SkUTF16_NextUnichar((const uint16_t**) &text)
- #define IS_END_OF_STRING(glyph) glyph < 0
-#endif
-
-#define TEXTURE_BORDER_SIZE 1
-
-///////////////////////////////////////////////////////////////////////////////
-// Declarations
-///////////////////////////////////////////////////////////////////////////////
-
-class FontRenderer;
-
-/**
- * CacheBlock is a node in a linked list of current free space areas in a CacheTexture.
- * Using CacheBlocks enables us to pack the cache from top to bottom as well as left to right.
- * When we add a glyph to the cache, we see if it fits within one of the existing columns that
- * have already been started (this is the case if the glyph fits vertically as well as
- * horizontally, and if its width is sufficiently close to the column width to avoid
- * sub-optimal packing of small glyphs into wide columns). If there is no column in which the
- * glyph fits, we check the final node, which is the remaining space in the cache, creating
- * a new column as appropriate.
- *
- * As columns fill up, we remove their CacheBlock from the list to avoid having to check
- * small blocks in the future.
- */
-struct CacheBlock {
- uint16_t mX;
- uint16_t mY;
- uint16_t mWidth;
- uint16_t mHeight;
- CacheBlock* mNext;
- CacheBlock* mPrev;
-
- CacheBlock(uint16_t x, uint16_t y, uint16_t width, uint16_t height, bool empty = false):
- mX(x), mY(y), mWidth(width), mHeight(height), mNext(NULL), mPrev(NULL)
- {
- }
-
- static CacheBlock* insertBlock(CacheBlock* head, CacheBlock *newBlock);
-
- static CacheBlock* removeBlock(CacheBlock* head, CacheBlock *blockToRemove);
-
- void output() {
- CacheBlock *currBlock = this;
- while (currBlock) {
- ALOGD("Block: this, x, y, w, h = %p, %d, %d, %d, %d",
- currBlock, currBlock->mX, currBlock->mY, currBlock->mWidth, currBlock->mHeight);
- currBlock = currBlock->mNext;
- }
- }
-};
-
-class CacheTexture {
-public:
- CacheTexture(uint16_t width, uint16_t height) :
- mTexture(NULL), mTextureId(0), mWidth(width), mHeight(height),
- mLinearFiltering(false), mDirty(false), mNumGlyphs(0) {
- mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
- mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true);
- }
-
- ~CacheTexture() {
- if (mTexture) {
- delete[] mTexture;
- }
- if (mTextureId) {
- glDeleteTextures(1, &mTextureId);
- }
- reset();
- }
-
- void reset() {
- // Delete existing cache blocks
- while (mCacheBlocks != NULL) {
- CacheBlock* tmpBlock = mCacheBlocks;
- mCacheBlocks = mCacheBlocks->mNext;
- delete tmpBlock;
- }
- mNumGlyphs = 0;
- }
-
- void init() {
- // reset, then create a new remainder space to start again
- reset();
- mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
- mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true);
- }
-
- bool fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY);
-
- uint8_t* mTexture;
- GLuint mTextureId;
- uint16_t mWidth;
- uint16_t mHeight;
- bool mLinearFiltering;
- bool mDirty;
- uint16_t mNumGlyphs;
- CacheBlock* mCacheBlocks;
-};
-
-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;
- CacheTexture* mCacheTexture;
-};
-
-
-///////////////////////////////////////////////////////////////////////////////
-// Font
-///////////////////////////////////////////////////////////////////////////////
-
-/**
- * Represents a font, defined by a Skia font id and a font size. A font is used
- * to generate glyphs and cache them in the FontState.
- */
-class Font {
-public:
- enum Style {
- kFakeBold = 1
- };
-
- ~Font();
-
- /**
- * Renders the specified string of text.
- * If bitmap is specified, it will be used as the render target
- */
- void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
- int numGlyphs, int x, int y, uint8_t *bitmap = NULL,
- uint32_t bitmapW = 0, uint32_t bitmapH = 0);
-
- void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
- int numGlyphs, int x, int y, const float* positions);
-
- void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
- int numGlyphs, SkPath* path, float hOffset, float vOffset);
-
- /**
- * Creates a new font associated with the specified font state.
- */
- static Font* create(FontRenderer* state, uint32_t fontId, float fontSize,
- int flags, uint32_t italicStyle, uint32_t scaleX, SkPaint::Style style,
- uint32_t strokeWidth);
-
-protected:
- friend class FontRenderer;
- typedef void (Font::*RenderGlyph)(CachedGlyphInfo*, int, int, uint8_t*,
- uint32_t, uint32_t, Rect*, const float*);
-
- enum RenderMode {
- FRAMEBUFFER,
- BITMAP,
- MEASURE,
- };
-
- void precache(SkPaint* paint, const char* text, int numGlyphs);
-
- void 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, const float* positions);
-
- void measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
- int numGlyphs, Rect *bounds, const float* positions);
-
- Font(FontRenderer* state, uint32_t fontId, float fontSize, int flags, uint32_t italicStyle,
- uint32_t scaleX, SkPaint::Style style, uint32_t strokeWidth);
-
- // Cache of glyphs
- DefaultKeyedVector<glyph_t, CachedGlyphInfo*> mCachedGlyphs;
-
- void invalidateTextureCache(CacheTexture *cacheTexture = NULL);
-
- CachedGlyphInfo* cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching);
- void updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph,
- bool precaching);
-
- void measureCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
- uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
- Rect* bounds, const float* pos);
- void drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
- uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
- Rect* bounds, const float* pos);
- void drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
- uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
- Rect* bounds, const float* pos);
- void drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
- SkPathMeasure& measure, SkPoint* position, SkVector* tangent);
-
- CachedGlyphInfo* getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool precaching = false);
-
- static glyph_t nextGlyph(const uint16_t** srcPtr) {
- const uint16_t* src = *srcPtr;
- glyph_t g = *src++;
- *srcPtr = src;
- return g;
- }
-
- FontRenderer* mState;
- uint32_t mFontId;
- float mFontSize;
- int mFlags;
- uint32_t mItalicStyle;
- uint32_t mScaleX;
- SkPaint::Style mStyle;
- uint32_t mStrokeWidth;
-};
-
-///////////////////////////////////////////////////////////////////////////////
// Renderer
///////////////////////////////////////////////////////////////////////////////
diff --git a/libs/hwui/font/CacheTexture.cpp b/libs/hwui/font/CacheTexture.cpp
new file mode 100644
index 0000000..5fb2636
--- /dev/null
+++ b/libs/hwui/font/CacheTexture.cpp
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <utils/Log.h>
+
+#include "Debug.h"
+#include "CacheTexture.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// CacheBlock
+///////////////////////////////////////////////////////////////////////////////
+
+/**
+ * Insert new block into existing linked list of blocks. Blocks are sorted in increasing-width
+ * order, except for the final block (the remainder space at the right, since we fill from the
+ * left).
+ */
+CacheBlock* CacheBlock::insertBlock(CacheBlock* head, CacheBlock *newBlock) {
+#if DEBUG_FONT_RENDERER
+ ALOGD("insertBlock: this, x, y, w, h = %p, %d, %d, %d, %d",
+ newBlock, newBlock->mX, newBlock->mY,
+ newBlock->mWidth, newBlock->mHeight);
+#endif
+ CacheBlock *currBlock = head;
+ CacheBlock *prevBlock = NULL;
+ while (currBlock && currBlock->mY != TEXTURE_BORDER_SIZE) {
+ if (newBlock->mWidth < currBlock->mWidth) {
+ newBlock->mNext = currBlock;
+ newBlock->mPrev = prevBlock;
+ currBlock->mPrev = newBlock;
+ if (prevBlock) {
+ prevBlock->mNext = newBlock;
+ return head;
+ } else {
+ return newBlock;
+ }
+ }
+ prevBlock = currBlock;
+ currBlock = currBlock->mNext;
+ }
+ // new block larger than all others - insert at end (but before the remainder space, if there)
+ newBlock->mNext = currBlock;
+ newBlock->mPrev = prevBlock;
+ if (currBlock) {
+ currBlock->mPrev = newBlock;
+ }
+ if (prevBlock) {
+ prevBlock->mNext = newBlock;
+ return head;
+ } else {
+ return newBlock;
+ }
+}
+
+CacheBlock* CacheBlock::removeBlock(CacheBlock* head, CacheBlock *blockToRemove) {
+#if DEBUG_FONT_RENDERER
+ ALOGD("removeBlock: this, x, y, w, h = %p, %d, %d, %d, %d",
+ blockToRemove, blockToRemove->mX, blockToRemove->mY,
+ blockToRemove->mWidth, blockToRemove->mHeight);
+#endif
+ CacheBlock* newHead = head;
+ CacheBlock* nextBlock = blockToRemove->mNext;
+ CacheBlock* prevBlock = blockToRemove->mPrev;
+ if (prevBlock) {
+ prevBlock->mNext = nextBlock;
+ } else {
+ newHead = nextBlock;
+ }
+ if (nextBlock) {
+ nextBlock->mPrev = prevBlock;
+ }
+ delete blockToRemove;
+ return newHead;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// CacheTexture
+///////////////////////////////////////////////////////////////////////////////
+
+bool CacheTexture::fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY) {
+ if (glyph.fHeight + TEXTURE_BORDER_SIZE > mHeight) {
+ return false;
+ }
+
+ uint16_t glyphW = glyph.fWidth + TEXTURE_BORDER_SIZE;
+ uint16_t glyphH = glyph.fHeight + TEXTURE_BORDER_SIZE;
+ // roundedUpW equals glyphW to the next multiple of CACHE_BLOCK_ROUNDING_SIZE.
+ // This columns for glyphs that are close but not necessarily exactly the same size. It trades
+ // off the loss of a few pixels for some glyphs against the ability to store more glyphs
+ // of varying sizes in one block.
+ uint16_t roundedUpW =
+ (glyphW + CACHE_BLOCK_ROUNDING_SIZE - 1) & -CACHE_BLOCK_ROUNDING_SIZE;
+ CacheBlock *cacheBlock = mCacheBlocks;
+ while (cacheBlock) {
+ // Store glyph in this block iff: it fits the block's remaining space and:
+ // it's the remainder space (mY == 0) or there's only enough height for this one glyph
+ // or it's within ROUNDING_SIZE of the block width
+ if (roundedUpW <= cacheBlock->mWidth && glyphH <= cacheBlock->mHeight &&
+ (cacheBlock->mY == TEXTURE_BORDER_SIZE ||
+ (cacheBlock->mWidth - roundedUpW < CACHE_BLOCK_ROUNDING_SIZE))) {
+ if (cacheBlock->mHeight - glyphH < glyphH) {
+ // Only enough space for this glyph - don't bother rounding up the width
+ roundedUpW = glyphW;
+ }
+ *retOriginX = cacheBlock->mX;
+ *retOriginY = cacheBlock->mY;
+ // If this is the remainder space, create a new cache block for this column. Otherwise,
+ // adjust the info about this column.
+ if (cacheBlock->mY == TEXTURE_BORDER_SIZE) {
+ uint16_t oldX = cacheBlock->mX;
+ // Adjust remainder space dimensions
+ cacheBlock->mWidth -= roundedUpW;
+ cacheBlock->mX += roundedUpW;
+ if (mHeight - glyphH >= glyphH) {
+ // There's enough height left over to create a new CacheBlock
+ CacheBlock *newBlock = new CacheBlock(oldX, glyphH + TEXTURE_BORDER_SIZE,
+ roundedUpW, mHeight - glyphH - TEXTURE_BORDER_SIZE);
+#if DEBUG_FONT_RENDERER
+ ALOGD("fitBitmap: Created new block: this, x, y, w, h = %p, %d, %d, %d, %d",
+ newBlock, newBlock->mX, newBlock->mY,
+ newBlock->mWidth, newBlock->mHeight);
+#endif
+ mCacheBlocks = CacheBlock::insertBlock(mCacheBlocks, newBlock);
+ }
+ } else {
+ // Insert into current column and adjust column dimensions
+ cacheBlock->mY += glyphH;
+ cacheBlock->mHeight -= glyphH;
+#if DEBUG_FONT_RENDERER
+ ALOGD("fitBitmap: Added to existing block: this, x, y, w, h = %p, %d, %d, %d, %d",
+ cacheBlock, cacheBlock->mX, cacheBlock->mY,
+ cacheBlock->mWidth, cacheBlock->mHeight);
+#endif
+ }
+ if (cacheBlock->mHeight < fmin(glyphH, glyphW)) {
+ // If remaining space in this block is too small to be useful, remove it
+ mCacheBlocks = CacheBlock::removeBlock(mCacheBlocks, cacheBlock);
+ }
+ mDirty = true;
+#if DEBUG_FONT_RENDERER
+ ALOGD("fitBitmap: current block list:");
+ mCacheBlocks->output();
+#endif
+ ++mNumGlyphs;
+ return true;
+ }
+ cacheBlock = cacheBlock->mNext;
+ }
+#if DEBUG_FONT_RENDERER
+ ALOGD("fitBitmap: returning false for glyph of size %d, %d", glyphW, glyphH);
+#endif
+ return false;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/font/CacheTexture.h b/libs/hwui/font/CacheTexture.h
new file mode 100644
index 0000000..7840d74
--- /dev/null
+++ b/libs/hwui/font/CacheTexture.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_CACHE_TEXTURE_H
+#define ANDROID_HWUI_CACHE_TEXTURE_H
+
+#include <GLES2/gl2.h>
+
+#include <SkScalerContext.h>
+
+#include <utils/Log.h>
+
+#include "FontUtil.h"
+
+namespace android {
+namespace uirenderer {
+
+/**
+ * CacheBlock is a node in a linked list of current free space areas in a CacheTexture.
+ * Using CacheBlocks enables us to pack the cache from top to bottom as well as left to right.
+ * When we add a glyph to the cache, we see if it fits within one of the existing columns that
+ * have already been started (this is the case if the glyph fits vertically as well as
+ * horizontally, and if its width is sufficiently close to the column width to avoid
+ * sub-optimal packing of small glyphs into wide columns). If there is no column in which the
+ * glyph fits, we check the final node, which is the remaining space in the cache, creating
+ * a new column as appropriate.
+ *
+ * As columns fill up, we remove their CacheBlock from the list to avoid having to check
+ * small blocks in the future.
+ */
+struct CacheBlock {
+ uint16_t mX;
+ uint16_t mY;
+ uint16_t mWidth;
+ uint16_t mHeight;
+ CacheBlock* mNext;
+ CacheBlock* mPrev;
+
+ CacheBlock(uint16_t x, uint16_t y, uint16_t width, uint16_t height, bool empty = false):
+ mX(x), mY(y), mWidth(width), mHeight(height), mNext(NULL), mPrev(NULL)
+ {
+ }
+
+ static CacheBlock* insertBlock(CacheBlock* head, CacheBlock *newBlock);
+
+ static CacheBlock* removeBlock(CacheBlock* head, CacheBlock *blockToRemove);
+
+ void output() {
+ CacheBlock *currBlock = this;
+ while (currBlock) {
+ ALOGD("Block: this, x, y, w, h = %p, %d, %d, %d, %d",
+ currBlock, currBlock->mX, currBlock->mY, currBlock->mWidth, currBlock->mHeight);
+ currBlock = currBlock->mNext;
+ }
+ }
+};
+
+class CacheTexture {
+public:
+ CacheTexture(uint16_t width, uint16_t height) :
+ mTexture(NULL), mTextureId(0), mWidth(width), mHeight(height),
+ mLinearFiltering(false), mDirty(false), mNumGlyphs(0) {
+ mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
+ mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true);
+ }
+
+ ~CacheTexture() {
+ if (mTexture) {
+ delete[] mTexture;
+ }
+ if (mTextureId) {
+ glDeleteTextures(1, &mTextureId);
+ }
+ reset();
+ }
+
+ void reset() {
+ // Delete existing cache blocks
+ while (mCacheBlocks != NULL) {
+ CacheBlock* tmpBlock = mCacheBlocks;
+ mCacheBlocks = mCacheBlocks->mNext;
+ delete tmpBlock;
+ }
+ mNumGlyphs = 0;
+ }
+
+ void init() {
+ // reset, then create a new remainder space to start again
+ reset();
+ mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
+ mWidth - TEXTURE_BORDER_SIZE, mHeight - TEXTURE_BORDER_SIZE, true);
+ }
+
+ bool fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY);
+
+ uint8_t* mTexture;
+ GLuint mTextureId;
+ uint16_t mWidth;
+ uint16_t mHeight;
+ bool mLinearFiltering;
+ bool mDirty;
+ uint16_t mNumGlyphs;
+ CacheBlock* mCacheBlocks;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_CACHE_TEXTURE_H
diff --git a/libs/hwui/font/CachedGlyphInfo.h b/libs/hwui/font/CachedGlyphInfo.h
new file mode 100644
index 0000000..6680a00
--- /dev/null
+++ b/libs/hwui/font/CachedGlyphInfo.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_CACHED_GLYPH_INFO_H
+#define ANDROID_HWUI_CACHED_GLYPH_INFO_H
+
+#include <SkFixed.h>
+
+#include "CacheTexture.h"
+
+namespace android {
+namespace uirenderer {
+
+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;
+ CacheTexture* mCacheTexture;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_CACHED_GLYPH_INFO_H
diff --git a/libs/hwui/font/Font.cpp b/libs/hwui/font/Font.cpp
new file mode 100644
index 0000000..0439875
--- /dev/null
+++ b/libs/hwui/font/Font.cpp
@@ -0,0 +1,446 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <cutils/compiler.h>
+
+#include <SkUtils.h>
+
+#include "Debug.h"
+#include "FontUtil.h"
+#include "Font.h"
+#include "FontRenderer.h"
+#include "Properties.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Font
+///////////////////////////////////////////////////////////////////////////////
+
+Font::Font(FontRenderer* state, uint32_t fontId, float fontSize,
+ int flags, uint32_t italicStyle, uint32_t scaleX,
+ SkPaint::Style style, uint32_t strokeWidth) :
+ mState(state), mFontId(fontId), mFontSize(fontSize),
+ mFlags(flags), mItalicStyle(italicStyle), mScaleX(scaleX),
+ mStyle(style), mStrokeWidth(mStrokeWidth) {
+}
+
+
+Font::~Font() {
+ for (uint32_t ct = 0; ct < mState->mActiveFonts.size(); ct++) {
+ if (mState->mActiveFonts[ct] == this) {
+ mState->mActiveFonts.removeAt(ct);
+ break;
+ }
+ }
+
+ for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
+ delete mCachedGlyphs.valueAt(i);
+ }
+}
+
+void Font::invalidateTextureCache(CacheTexture *cacheTexture) {
+ for (uint32_t i = 0; i < mCachedGlyphs.size(); i++) {
+ CachedGlyphInfo* cachedGlyph = mCachedGlyphs.valueAt(i);
+ if (cacheTexture == NULL || cachedGlyph->mCacheTexture == cacheTexture) {
+ cachedGlyph->mIsValid = false;
+ }
+ }
+}
+
+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;
+
+ int width = (int) glyph->mBitmapWidth;
+ int height = (int) 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::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;
+
+ float u1 = glyph->mBitmapMinU;
+ float u2 = glyph->mBitmapMaxU;
+ float v1 = glyph->mBitmapMinV;
+ float v2 = glyph->mBitmapMaxV;
+
+ int width = (int) glyph->mBitmapWidth;
+ int height = (int) glyph->mBitmapHeight;
+
+ mState->appendMeshQuad(nPenX, nPenY, u1, v2,
+ nPenX + width, nPenY, u2, v2,
+ nPenX + width, nPenY - height, u2, v1,
+ nPenX, nPenY - height, u1, v1, glyph->mCacheTexture);
+}
+
+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;
+
+ CacheTexture *cacheTexture = glyph->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;
+ }
+ }
+}
+
+void Font::drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
+ SkPathMeasure& measure, SkPoint* position, SkVector* tangent) {
+ const float halfWidth = glyph->mBitmapWidth * 0.5f;
+ const float height = glyph->mBitmapHeight;
+
+ vOffset += glyph->mBitmapTop + height;
+
+ SkPoint destination[4];
+ measure.getPosTan(x + hOffset + glyph->mBitmapLeft + halfWidth, position, tangent);
+
+ // Move along the tangent and offset by the normal
+ destination[0].set(-tangent->fX * halfWidth - tangent->fY * vOffset,
+ -tangent->fY * halfWidth + tangent->fX * vOffset);
+ destination[1].set(tangent->fX * halfWidth - tangent->fY * vOffset,
+ tangent->fY * halfWidth + tangent->fX * vOffset);
+ destination[2].set(destination[1].fX + tangent->fY * height,
+ destination[1].fY - tangent->fX * height);
+ destination[3].set(destination[0].fX + tangent->fY * height,
+ destination[0].fY - tangent->fX * height);
+
+ const float u1 = glyph->mBitmapMinU;
+ const float u2 = glyph->mBitmapMaxU;
+ const float v1 = glyph->mBitmapMinV;
+ const float v2 = glyph->mBitmapMaxV;
+
+ mState->appendRotatedMeshQuad(
+ position->fX + destination[0].fX,
+ position->fY + destination[0].fY, u1, v2,
+ position->fX + destination[1].fX,
+ position->fY + destination[1].fY, u2, v2,
+ position->fX + destination[2].fX,
+ position->fY + destination[2].fY, u2, v1,
+ position->fX + destination[3].fX,
+ position->fY + destination[3].fY, u1, v1,
+ glyph->mCacheTexture);
+}
+
+CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool precaching) {
+ CachedGlyphInfo* cachedGlyph = NULL;
+ ssize_t index = mCachedGlyphs.indexOfKey(textUnit);
+ if (index >= 0) {
+ cachedGlyph = mCachedGlyphs.valueAt(index);
+ } else {
+ cachedGlyph = cacheGlyph(paint, textUnit, precaching);
+ }
+
+ // Is the glyph still in texture cache?
+ if (!cachedGlyph->mIsValid) {
+ const SkGlyph& skiaGlyph = GET_METRICS(paint, textUnit);
+ updateGlyphCache(paint, skiaGlyph, cachedGlyph, precaching);
+ }
+
+ return cachedGlyph;
+}
+
+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, NULL);
+ } else {
+ render(paint, text, start, len, numGlyphs, x, y, FRAMEBUFFER, 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::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+ int numGlyphs, SkPath* path, float hOffset, float vOffset) {
+ if (numGlyphs == 0 || text == NULL || len == 0) {
+ return;
+ }
+
+ text += start;
+
+ int glyphsCount = 0;
+ SkFixed prevRsbDelta = 0;
+
+ float penX = 0.0f;
+
+ SkPoint position;
+ SkVector tangent;
+
+ SkPathMeasure measure(*path, false);
+ float pathLength = SkScalarToFloat(measure.getLength());
+
+ if (paint->getTextAlign() != SkPaint::kLeft_Align) {
+ float textWidth = SkScalarToFloat(paint->measureText(text, len));
+ float pathOffset = pathLength;
+ if (paint->getTextAlign() == SkPaint::kCenter_Align) {
+ textWidth *= 0.5f;
+ pathOffset *= 0.5f;
+ }
+ penX += pathOffset - textWidth;
+ }
+
+ while (glyphsCount < numGlyphs && penX < pathLength) {
+ glyph_t glyph = GET_GLYPH(text);
+
+ if (IS_END_OF_STRING(glyph)) {
+ break;
+ }
+
+ CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph);
+ penX += SkFixedToFloat(AUTO_KERN(prevRsbDelta, cachedGlyph->mLsbDelta));
+ prevRsbDelta = cachedGlyph->mRsbDelta;
+
+ if (cachedGlyph->mIsValid) {
+ drawCachedGlyph(cachedGlyph, penX, hOffset, vOffset, measure, &position, &tangent);
+ }
+
+ penX += SkFixedToFloat(cachedGlyph->mAdvanceX);
+
+ glyphsCount++;
+ }
+}
+
+void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+ int numGlyphs, Rect *bounds, const float* positions) {
+ if (bounds == NULL) {
+ ALOGE("No return rectangle provided to measure text");
+ return;
+ }
+ bounds->set(1e6, -1e6, -1e6, 1e6);
+ render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, positions);
+}
+
+void Font::precache(SkPaint* paint, const char* text, int numGlyphs) {
+
+ if (numGlyphs == 0 || text == NULL) {
+ return;
+ }
+ int glyphsCount = 0;
+
+ 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, true);
+
+ glyphsCount++;
+ }
+}
+
+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, const float* positions) {
+ if (numGlyphs == 0 || text == NULL || len == 0) {
+ return;
+ }
+
+ static RenderGlyph gRenderGlyph[] = {
+ &android::uirenderer::Font::drawCachedGlyph,
+ &android::uirenderer::Font::drawCachedGlyphBitmap,
+ &android::uirenderer::Font::measureCachedGlyph
+ };
+ RenderGlyph render = gRenderGlyph[mode];
+
+ text += start;
+ int glyphsCount = 0;
+
+ if (CC_LIKELY(positions == NULL)) {
+ SkFixed prevRsbDelta = 0;
+
+ float penX = x + 0.5f;
+ int penY = y;
+
+ 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(AUTO_KERN(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);
+
+ // Reached the end of the string
+ if (IS_END_OF_STRING(glyph)) {
+ break;
+ }
+
+ 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++;
+ }
+ }
+}
+
+void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph,
+ bool precaching) {
+ glyph->mAdvanceX = skiaGlyph.fAdvanceX;
+ glyph->mAdvanceY = skiaGlyph.fAdvanceY;
+ glyph->mBitmapLeft = skiaGlyph.fLeft;
+ glyph->mBitmapTop = skiaGlyph.fTop;
+ glyph->mLsbDelta = skiaGlyph.fLsbDelta;
+ glyph->mRsbDelta = skiaGlyph.fRsbDelta;
+
+ uint32_t startX = 0;
+ uint32_t startY = 0;
+
+ // Get the bitmap for the glyph
+ paint->findImage(skiaGlyph);
+ mState->cacheBitmap(skiaGlyph, glyph, &startX, &startY, precaching);
+
+ if (!glyph->mIsValid) {
+ return;
+ }
+
+ uint32_t endX = startX + skiaGlyph.fWidth;
+ uint32_t endY = startY + skiaGlyph.fHeight;
+
+ glyph->mStartX = startX;
+ glyph->mStartY = startY;
+ glyph->mBitmapWidth = skiaGlyph.fWidth;
+ glyph->mBitmapHeight = skiaGlyph.fHeight;
+
+ uint32_t cacheWidth = glyph->mCacheTexture->mWidth;
+ uint32_t cacheHeight = glyph->mCacheTexture->mHeight;
+
+ glyph->mBitmapMinU = startX / (float) cacheWidth;
+ glyph->mBitmapMinV = startY / (float) cacheHeight;
+ glyph->mBitmapMaxU = endX / (float) cacheWidth;
+ glyph->mBitmapMaxV = endY / (float) cacheHeight;
+
+ mState->mUploadTexture = true;
+}
+
+CachedGlyphInfo* Font::cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching) {
+ CachedGlyphInfo* newGlyph = new CachedGlyphInfo();
+ mCachedGlyphs.add(glyph, newGlyph);
+
+ const SkGlyph& skiaGlyph = GET_METRICS(paint, glyph);
+ newGlyph->mGlyphIndex = skiaGlyph.fID;
+ newGlyph->mIsValid = false;
+
+ updateGlyphCache(paint, skiaGlyph, newGlyph, precaching);
+
+ return newGlyph;
+}
+
+Font* Font::create(FontRenderer* state, uint32_t fontId, float fontSize,
+ int flags, uint32_t italicStyle, uint32_t scaleX,
+ SkPaint::Style style, uint32_t strokeWidth) {
+ Vector<Font*> &activeFonts = state->mActiveFonts;
+
+ for (uint32_t i = 0; i < activeFonts.size(); i++) {
+ Font* font = activeFonts[i];
+ if (font->mFontId == fontId && font->mFontSize == fontSize &&
+ font->mFlags == flags && font->mItalicStyle == italicStyle &&
+ font->mScaleX == scaleX && font->mStyle == style &&
+ (style == SkPaint::kFill_Style || font->mStrokeWidth == strokeWidth)) {
+ return font;
+ }
+ }
+
+ Font* newFont = new Font(state, fontId, fontSize, flags, italicStyle,
+ scaleX, style, strokeWidth);
+ activeFonts.push(newFont);
+ return newFont;
+}
+
+}; // namespace uirenderer
+}; // namespace android
diff --git a/libs/hwui/font/Font.h b/libs/hwui/font/Font.h
new file mode 100644
index 0000000..d1b75bd
--- /dev/null
+++ b/libs/hwui/font/Font.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_FONT_H
+#define ANDROID_HWUI_FONT_H
+
+#include <utils/KeyedVector.h>
+
+#include <SkScalerContext.h>
+#include <SkPaint.h>
+#include <SkPathMeasure.h>
+
+#include "CachedGlyphInfo.h"
+#include "../Rect.h"
+
+namespace android {
+namespace uirenderer {
+
+///////////////////////////////////////////////////////////////////////////////
+// Font
+///////////////////////////////////////////////////////////////////////////////
+
+class FontRenderer;
+
+/**
+ * Represents a font, defined by a Skia font id and a font size. A font is used
+ * to generate glyphs and cache them in the FontState.
+ */
+class Font {
+public:
+ enum Style {
+ kFakeBold = 1
+ };
+
+ ~Font();
+
+ /**
+ * Renders the specified string of text.
+ * If bitmap is specified, it will be used as the render target
+ */
+ void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+ int numGlyphs, int x, int y, uint8_t *bitmap = NULL,
+ uint32_t bitmapW = 0, uint32_t bitmapH = 0);
+
+ void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+ int numGlyphs, int x, int y, const float* positions);
+
+ void render(SkPaint* paint, const char *text, uint32_t start, uint32_t len,
+ int numGlyphs, SkPath* path, float hOffset, float vOffset);
+
+ /**
+ * Creates a new font associated with the specified font state.
+ */
+ static Font* create(FontRenderer* state, uint32_t fontId, float fontSize,
+ int flags, uint32_t italicStyle, uint32_t scaleX, SkPaint::Style style,
+ uint32_t strokeWidth);
+
+private:
+ friend class FontRenderer;
+ typedef void (Font::*RenderGlyph)(CachedGlyphInfo*, int, int, uint8_t*,
+ uint32_t, uint32_t, Rect*, const float*);
+
+ enum RenderMode {
+ FRAMEBUFFER,
+ BITMAP,
+ MEASURE,
+ };
+
+ void precache(SkPaint* paint, const char* text, int numGlyphs);
+
+ void 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, const float* positions);
+
+ void measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
+ int numGlyphs, Rect *bounds, const float* positions);
+
+ Font(FontRenderer* state, uint32_t fontId, float fontSize, int flags, uint32_t italicStyle,
+ uint32_t scaleX, SkPaint::Style style, uint32_t strokeWidth);
+
+ // Cache of glyphs
+ DefaultKeyedVector<glyph_t, CachedGlyphInfo*> mCachedGlyphs;
+
+ void invalidateTextureCache(CacheTexture *cacheTexture = NULL);
+
+ CachedGlyphInfo* cacheGlyph(SkPaint* paint, glyph_t glyph, bool precaching);
+ void updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyphInfo* glyph,
+ bool precaching);
+
+ void measureCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
+ uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
+ Rect* bounds, const float* pos);
+ void drawCachedGlyph(CachedGlyphInfo* glyph, int x, int y,
+ uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
+ Rect* bounds, const float* pos);
+ void drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y,
+ uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,
+ Rect* bounds, const float* pos);
+ void drawCachedGlyph(CachedGlyphInfo* glyph, float x, float hOffset, float vOffset,
+ SkPathMeasure& measure, SkPoint* position, SkVector* tangent);
+
+ CachedGlyphInfo* getCachedGlyph(SkPaint* paint, glyph_t textUnit, bool precaching = false);
+
+ FontRenderer* mState;
+ uint32_t mFontId;
+ float mFontSize;
+ int mFlags;
+ uint32_t mItalicStyle;
+ uint32_t mScaleX;
+ SkPaint::Style mStyle;
+ uint32_t mStrokeWidth;
+};
+
+}; // namespace uirenderer
+}; // namespace android
+
+#endif // ANDROID_HWUI_FONT_H
diff --git a/libs/hwui/font/FontUtil.h b/libs/hwui/font/FontUtil.h
new file mode 100644
index 0000000..12247ba
--- /dev/null
+++ b/libs/hwui/font/FontUtil.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_HWUI_FONT_UTIL_H
+#define ANDROID_HWUI_FONT_UTIL_H
+
+#include <SkUtils.h>
+
+#include "Properties.h"
+
+///////////////////////////////////////////////////////////////////////////////
+// Defines
+///////////////////////////////////////////////////////////////////////////////
+
+#define DEFAULT_TEXT_SMALL_CACHE_WIDTH 1024
+#define DEFAULT_TEXT_SMALL_CACHE_HEIGHT 256
+#define DEFAULT_TEXT_LARGE_CACHE_WIDTH 2048
+#define DEFAULT_TEXT_LARGE_CACHE_HEIGHT 512
+
+#define TEXTURE_BORDER_SIZE 1
+
+#define CACHE_BLOCK_ROUNDING_SIZE 4
+
+#if RENDER_TEXT_AS_GLYPHS
+ typedef uint16_t glyph_t;
+ #define TO_GLYPH(g) g
+ #define GET_METRICS(paint, glyph) paint->getGlyphMetrics(glyph)
+ #define GET_GLYPH(text) nextGlyph((const uint16_t**) &text)
+ #define IS_END_OF_STRING(glyph) false
+
+ static glyph_t nextGlyph(const uint16_t** srcPtr) {
+ const uint16_t* src = *srcPtr;
+ glyph_t g = *src++;
+ *srcPtr = src;
+ return g;
+ }
+#else
+ typedef SkUnichar glyph_t;
+ #define TO_GLYPH(g) ((SkUnichar) g)
+ #define GET_METRICS(paint, glyph) paint->getUnicharMetrics(glyph)
+ #define GET_GLYPH(text) SkUTF16_NextUnichar((const uint16_t**) &text)
+ #define IS_END_OF_STRING(glyph) glyph < 0
+#endif
+
+#define AUTO_KERN(prev, next) (((next) - (prev) + 32) >> 6 << 16)
+
+#endif // ANDROID_HWUI_FONT_UTIL_H