Merge "Optimize interactions with glyph cache" into jb-mr1-dev

This commit is contained in:
Chet Haase
2012-08-14 10:38:45 -07:00
committed by Android (Google) Code Review
6 changed files with 403 additions and 77 deletions

View File

@@ -1699,7 +1699,9 @@ status_t DisplayListRenderer::drawTextOnPath(const char* text, int bytesCount, i
addFloat(hOffset);
addFloat(vOffset);
paint->setAntiAlias(true);
addPaint(paint);
SkPaint* addedPaint = addPaint(paint);
FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(addedPaint);
fontRenderer.precache(addedPaint, text, count);
return DrawGlInfo::kStatusDone;
}
@@ -1711,7 +1713,9 @@ status_t DisplayListRenderer::drawPosText(const char* text, int bytesCount, int
addInt(count);
addFloats(positions, count * 2);
paint->setAntiAlias(true);
addPaint(paint);
SkPaint* addedPaint = addPaint(paint);
FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(addedPaint);
fontRenderer.precache(addedPaint, text, count);
return DrawGlInfo::kStatusDone;
}
@@ -1742,7 +1746,11 @@ status_t DisplayListRenderer::drawText(const char* text, int bytesCount, int cou
addFloat(x);
addFloat(y);
addFloats(positions, count * 2);
addPaint(paint);
SkPaint* addedPaint = addPaint(paint);
if (!reject) {
FontRenderer& fontRenderer = mCaches.fontRenderer->getFontRenderer(addedPaint);
fontRenderer.precache(addedPaint, text, count);
}
addFloat(length);
addSkip(location);
return DrawGlInfo::kStatusDone;

View File

@@ -770,10 +770,10 @@ private:
addInt((int) pathCopy);
}
inline void addPaint(SkPaint* paint) {
inline SkPaint* addPaint(SkPaint* paint) {
if (!paint) {
addInt((int) NULL);
return;
return paint;
}
SkPaint* paintCopy = mPaintMap.valueFor(paint);
@@ -785,6 +785,8 @@ private:
}
addInt((int) paintCopy);
return paintCopy;
}
inline void addDisplayList(DisplayList* displayList) {

View File

@@ -37,10 +37,77 @@ namespace uirenderer {
#define DEFAULT_TEXT_CACHE_WIDTH 1024
#define DEFAULT_TEXT_CACHE_HEIGHT 256
#define MAX_TEXT_CACHE_WIDTH 2048
#define TEXTURE_BORDER_SIZE 1
#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;
}
///////////////////////////////////////////////////////////////////////////////
// CacheTextureLine
///////////////////////////////////////////////////////////////////////////////
@@ -50,14 +117,73 @@ bool CacheTextureLine::fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uin
return false;
}
if (mCurrentCol + glyph.fWidth + TEXTURE_BORDER_SIZE * 2 < mMaxWidth) {
*retOriginX = mCurrentCol + TEXTURE_BORDER_SIZE;
*retOriginY = mCurrentRow + TEXTURE_BORDER_SIZE;
mCurrentCol += glyph.fWidth + TEXTURE_BORDER_SIZE * 2;
mDirty = true;
return true;
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 = mCurrentRow + 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 (mMaxHeight - glyphH >= glyphH) {
// There's enough height left over to create a new CacheBlock
CacheBlock *newBlock = new CacheBlock(oldX, glyphH, roundedUpW,
mMaxHeight - glyphH);
#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;
}
@@ -297,6 +423,27 @@ void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t le
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);
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) {
@@ -545,9 +692,33 @@ void FontRenderer::flushAllAndInvalidate() {
mActiveFonts[i]->invalidateTextureCache();
}
uint16_t totalGlyphs = 0;
for (uint32_t i = 0; i < mCacheLines.size(); i++) {
mCacheLines[i]->mCurrentCol = 0;
totalGlyphs += mCacheLines[i]->mNumGlyphs;
mCacheLines[i]->init();
}
#if DEBUG_FONT_RENDERER
ALOGD("FontRenderer: flushAllAndInvalidatel");
// Erase caches, just as a debugging facility
if (mCacheTextureSmall && mCacheTextureSmall->mTexture) {
memset(mCacheTextureSmall->mTexture, 0,
mCacheTextureSmall->mWidth * mCacheTextureSmall->mHeight);
}
if (mCacheTexture128 && mCacheTexture128->mTexture) {
memset(mCacheTexture128->mTexture, 0,
mCacheTexture128->mWidth * mCacheTexture128->mHeight);
}
if (mCacheTexture256 && mCacheTexture256->mTexture) {
memset(mCacheTexture256->mTexture, 0,
mCacheTexture256->mWidth * mCacheTexture256->mHeight);
}
if (mCacheTexture512 && mCacheTexture512->mTexture) {
memset(mCacheTexture512->mTexture, 0,
mCacheTexture512->mWidth * mCacheTexture512->mHeight);
}
ALOGD("Flushing caches: glyphs cached = %d", totalGlyphs);
#endif
}
void FontRenderer::deallocateTextureMemory(CacheTexture *cacheTexture) {
@@ -573,7 +744,16 @@ void FontRenderer::flushLargeCaches() {
cacheLine->mCacheTexture == mCacheTexture256 ||
cacheLine->mCacheTexture == mCacheTexture512) &&
cacheLine->mCacheTexture->mTexture != NULL) {
cacheLine->mCurrentCol = 0;
#if DEBUG_FONT_RENDERER
if (cacheLine->mCacheTexture == mCacheTexture128) {
ALOGD("flushing cacheTexture128");
} else if (cacheLine->mCacheTexture == mCacheTexture256) {
ALOGD("flushing cacheTexture256");
} else {
ALOGD("flushing cacheTexture512");
}
#endif
cacheLine->init();
for (uint32_t i = 0; i < mActiveFonts.size(); i++) {
mActiveFonts[i]->invalidateTextureCache(cacheLine);
}
@@ -614,9 +794,12 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp
uint32_t* retOriginX, uint32_t* retOriginY) {
cachedGlyph->mIsValid = false;
// If the glyph is too tall, don't cache it
if (glyph.fHeight + TEXTURE_BORDER_SIZE * 2 > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
ALOGE("Font size to large to fit in cache. width, height = %i, %i",
(int) glyph.fWidth, (int) glyph.fHeight);
if (mCacheLines.size() == 0 ||
glyph.fHeight + TEXTURE_BORDER_SIZE * 2 > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) {
if (mCacheLines.size() != 0) {
ALOGE("Font size too large to fit in cache. width, height = %i, %i",
(int) glyph.fWidth, (int) glyph.fHeight);
}
return;
}
@@ -747,26 +930,26 @@ void FontRenderer::initTextTexture() {
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));
mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 18, nextLine, mCacheTextureSmall));
nextLine += mCacheLines.top()->mMaxHeight;
mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall));
mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, mCacheTextureSmall));
nextLine += mCacheLines.top()->mMaxHeight;
mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, 0, mCacheTextureSmall));
mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 26, nextLine, mCacheTextureSmall));
nextLine += mCacheLines.top()->mMaxHeight;
mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall));
mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, mCacheTextureSmall));
nextLine += mCacheLines.top()->mMaxHeight;
mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, 0, mCacheTextureSmall));
mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 34, nextLine, mCacheTextureSmall));
nextLine += mCacheLines.top()->mMaxHeight;
mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 42, nextLine, 0, mCacheTextureSmall));
mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, 42, nextLine, mCacheTextureSmall));
nextLine += mCacheLines.top()->mMaxHeight;
mCacheLines.push(new CacheTextureLine(mSmallCacheWidth, mSmallCacheHeight - nextLine,
nextLine, 0, mCacheTextureSmall));
nextLine, 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));
mCacheLines.push(new CacheTextureLine(maxWidth, 128, 0, mCacheTexture128));
mCacheLines.push(new CacheTextureLine(maxWidth, 128, 128, mCacheTexture128));
mCacheLines.push(new CacheTextureLine(maxWidth, 256, 0, mCacheTexture256));
mCacheLines.push(new CacheTextureLine(maxWidth, 512, 0, mCacheTexture512));
}
// Avoid having to reallocate memory and render quad by quad
@@ -837,6 +1020,10 @@ void FontRenderer::checkTextureUpdate() {
glBindTexture(GL_TEXTURE_2D, cacheTexture->mTextureId);
lastTextureId = cacheTexture->mTextureId;
}
#if DEBUG_FONT_RENDERER
ALOGD("glTextSubimage for cacheLine %d: xOff, yOff, width height = %d, %d, %d, %d", i,
xOffset, yOffset, width, height);
#endif
glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, width, height,
GL_ALPHA, GL_UNSIGNED_BYTE, textureData);
@@ -960,43 +1147,7 @@ void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1,
}
}
uint32_t FontRenderer::getRemainingCacheCapacity() {
uint32_t remainingCapacity = 0;
float totalPixels = 0;
//avoid divide by zero if the size is 0
if (mCacheLines.size() == 0) {
return 0;
}
for(uint32_t i = 0; i < mCacheLines.size(); i ++) {
remainingCapacity += (mCacheLines[i]->mMaxWidth - mCacheLines[i]->mCurrentCol);
totalPixels += mCacheLines[i]->mMaxWidth;
}
remainingCapacity = (remainingCapacity * 100) / totalPixels;
return remainingCapacity;
}
void FontRenderer::precacheLatin(SkPaint* paint) {
// Remaining capacity is measured in %
uint32_t remainingCapacity = getRemainingCacheCapacity();
uint32_t precacheIndex = 0;
// We store a string with letters in a rough frequency of occurrence
String16 l("eisarntolcdugpmhbyfvkwzxjq EISARNTOLCDUGPMHBYFVKWZXJQ,.?!()-+@;:'0123456789");
size_t size = l.size();
uint16_t latin[size];
paint->utfToGlyphs(l.string(), SkPaint::kUTF16_TextEncoding, size * sizeof(char16_t), latin);
while (remainingCapacity > 25 && precacheIndex < size) {
mCurrentFont->getCachedGlyph(paint, TO_GLYPH(latin[precacheIndex]));
remainingCapacity = getRemainingCacheCapacity();
precacheIndex++;
}
}
void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
uint32_t currentNumFonts = mActiveFonts.size();
int flags = 0;
if (paint->isFakeBoldText()) {
flags |= Font::kFakeBold;
@@ -1012,12 +1163,6 @@ void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) {
mCurrentFont = Font::create(this, fontId, fontSize, flags, italicStyle,
scaleX, style, strokeWidth);
const float maxPrecacheFontSize = 40.0f;
bool isNewFont = currentNumFonts != mActiveFonts.size();
if (isNewFont && fontSize <= maxPrecacheFontSize) {
precacheLatin(paint);
}
}
FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
@@ -1084,6 +1229,25 @@ void FontRenderer::finishRender() {
}
}
void FontRenderer::precache(SkPaint* paint, const char* text, int numGlyphs) {
int flags = 0;
if (paint->isFakeBoldText()) {
flags |= Font::kFakeBold;
}
const float skewX = paint->getTextSkewX();
uint32_t italicStyle = *(uint32_t*) &skewX;
const float scaleXFloat = paint->getTextScaleX();
uint32_t scaleX = *(uint32_t*) &scaleXFloat;
SkPaint::Style style = paint->getStyle();
const float strokeWidthFloat = paint->getStrokeWidth();
uint32_t strokeWidth = *(uint32_t*) &strokeWidthFloat;
float fontSize = paint->getTextSize();
Font* font = Font::create(this, SkTypeface::UniqueID(paint->getTypeface()),
fontSize, flags, italicStyle, scaleX, style, strokeWidth);
font->precache(paint, text, numGlyphs);
}
bool FontRenderer::renderText(SkPaint* paint, const Rect* clip, const char *text,
uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, Rect* bounds) {
if (!mCurrentFont) {

View File

@@ -53,6 +53,8 @@ namespace uirenderer {
#define IS_END_OF_STRING(glyph) glyph < 0
#endif
#define TEXTURE_BORDER_SIZE 1
///////////////////////////////////////////////////////////////////////////////
// Declarations
///////////////////////////////////////////////////////////////////////////////
@@ -80,16 +82,79 @@ public:
bool mLinearFiltering;
};
/**
* CacheBlock is a noce in a linked list of current free space areas in a CacheTextureLine.
* Using CacheBlocks enables us to pack the cache line 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 line, 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 CacheTextureLine {
public:
CacheTextureLine(uint16_t maxWidth, uint16_t maxHeight, uint32_t currentRow,
uint32_t currentCol, CacheTexture* cacheTexture):
CacheTexture* cacheTexture):
mMaxHeight(maxHeight),
mMaxWidth(maxWidth),
mCurrentRow(currentRow),
mCurrentCol(currentCol),
mDirty(false),
mNumGlyphs(0),
mCacheTexture(cacheTexture) {
mCacheBlocks = new CacheBlock(TEXTURE_BORDER_SIZE, TEXTURE_BORDER_SIZE,
maxWidth - TEXTURE_BORDER_SIZE, maxHeight - TEXTURE_BORDER_SIZE, true);
}
~CacheTextureLine() {
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,
mMaxWidth - TEXTURE_BORDER_SIZE, mMaxHeight - TEXTURE_BORDER_SIZE, true);
}
bool fitBitmap(const SkGlyph& glyph, uint32_t *retOriginX, uint32_t *retOriginY);
@@ -97,9 +162,10 @@ public:
uint16_t mMaxHeight;
uint16_t mMaxWidth;
uint32_t mCurrentRow;
uint32_t mCurrentCol;
bool mDirty;
uint16_t mNumGlyphs;
CacheTexture* mCacheTexture;
CacheBlock* mCacheBlocks;
};
struct CachedGlyphInfo {
@@ -179,6 +245,8 @@ protected:
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);
@@ -244,6 +312,9 @@ public:
}
void setFont(SkPaint* paint, uint32_t fontId, float fontSize);
void precache(SkPaint* paint, const char* text, int numGlyphs);
// bounds is an out parameter
bool renderText(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex,
uint32_t len, int numGlyphs, int x, int y, Rect* bounds);
@@ -327,8 +398,6 @@ protected:
void initRender(const Rect* clip, Rect* bounds);
void finishRender();
void precacheLatin(SkPaint* paint);
void issueDrawCommand();
void appendMeshQuadNoClip(float x1, float y1, float u1, float v1,
float x2, float y2, float u2, float v2,
@@ -347,7 +416,6 @@ protected:
uint32_t mSmallCacheHeight;
Vector<CacheTextureLine*> mCacheLines;
uint32_t getRemainingCacheCapacity();
Font* mCurrentFont;
Vector<Font*> mActiveFonts;

View File

@@ -176,6 +176,15 @@
</intent-filter>
</activity>
<activity
android:name="GlyphCacheActivity"
android:label="_GlyphCache">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="CanvasTextureViewActivity"
android:label="_CanvasTextureView">

View File

@@ -0,0 +1,75 @@
/*
* 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.
*/
package com.android.test.hwui;
import android.app.Activity;
import android.os.Bundle;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import static android.widget.LinearLayout.LayoutParams;
public class GlyphCacheActivity extends Activity {
private static final String mCharacterSet = "abcdefghijklmnopqrstuvwxyz" +
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789" + "~!@#$%^&*()_+-={}[]:\";'<>?,./";
private int mTotalChars = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ScrollView scrollView = new ScrollView(this);
scrollView.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
LinearLayout layout = new LinearLayout(this);
layout.setOrientation(LinearLayout.VERTICAL);
layout.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
scrollView.addView(layout);
while (mTotalChars < 10000) {
layout.addView(createTextView());
}
setContentView(scrollView);
}
private TextView createTextView() {
TextView textview = new TextView(this);
textview.setTextSize(6 + (int) (Math.random() * 5) * 10);
textview.setTextColor(0xff << 24 | (int) (Math.random() * 255) << 16 |
(int) (Math.random() * 255) << 8 | (int) (Math.random() * 255) << 16);
textview.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
int numChars = 5 + (int) (Math.random() * 10);
mTotalChars += numChars;
textview.setText(createString(numChars));
return textview;
}
private String createString(int length) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < length; i++) {
sb.append(mCharacterSet.charAt((int)(Math.random() * mCharacterSet.length())));
}
return sb.toString();
}
}