Loading libs/hwui/DisplayListRenderer.cpp +11 −3 Original line number Diff line number Diff line Loading @@ -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; } Loading @@ -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; } Loading Loading @@ -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; Loading libs/hwui/DisplayListRenderer.h +4 −2 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -785,6 +785,8 @@ private: } addInt((int) paintCopy); return paintCopy; } inline void addDisplayList(DisplayList* displayList) { Loading libs/hwui/FontRenderer.cpp +230 −66 Original line number Diff line number Diff line Loading @@ -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 /////////////////////////////////////////////////////////////////////////////// Loading @@ -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; 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; } Loading Loading @@ -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) { Loading Loading @@ -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) { Loading @@ -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); } Loading Loading @@ -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", 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; } Loading Loading @@ -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 Loading Loading @@ -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); Loading Loading @@ -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; Loading @@ -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, Loading Loading @@ -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) { Loading libs/hwui/FontRenderer.h +74 −6 Original line number Diff line number Diff line Loading @@ -53,6 +53,8 @@ namespace uirenderer { #define IS_END_OF_STRING(glyph) glyph < 0 #endif #define TEXTURE_BORDER_SIZE 1 /////////////////////////////////////////////////////////////////////////////// // Declarations /////////////////////////////////////////////////////////////////////////////// Loading Loading @@ -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); Loading @@ -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 { Loading Loading @@ -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); Loading Loading @@ -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); Loading Loading @@ -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, Loading @@ -347,7 +416,6 @@ protected: uint32_t mSmallCacheHeight; Vector<CacheTextureLine*> mCacheLines; uint32_t getRemainingCacheCapacity(); Font* mCurrentFont; Vector<Font*> mActiveFonts; Loading tests/HwAccelerationTest/AndroidManifest.xml +9 −0 Original line number Diff line number Diff line Loading @@ -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"> Loading Loading
libs/hwui/DisplayListRenderer.cpp +11 −3 Original line number Diff line number Diff line Loading @@ -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; } Loading @@ -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; } Loading Loading @@ -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; Loading
libs/hwui/DisplayListRenderer.h +4 −2 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -785,6 +785,8 @@ private: } addInt((int) paintCopy); return paintCopy; } inline void addDisplayList(DisplayList* displayList) { Loading
libs/hwui/FontRenderer.cpp +230 −66 Original line number Diff line number Diff line Loading @@ -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 /////////////////////////////////////////////////////////////////////////////// Loading @@ -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; 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; } Loading Loading @@ -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) { Loading Loading @@ -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) { Loading @@ -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); } Loading Loading @@ -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", 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; } Loading Loading @@ -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 Loading Loading @@ -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); Loading Loading @@ -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; Loading @@ -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, Loading Loading @@ -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) { Loading
libs/hwui/FontRenderer.h +74 −6 Original line number Diff line number Diff line Loading @@ -53,6 +53,8 @@ namespace uirenderer { #define IS_END_OF_STRING(glyph) glyph < 0 #endif #define TEXTURE_BORDER_SIZE 1 /////////////////////////////////////////////////////////////////////////////// // Declarations /////////////////////////////////////////////////////////////////////////////// Loading Loading @@ -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); Loading @@ -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 { Loading Loading @@ -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); Loading Loading @@ -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); Loading Loading @@ -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, Loading @@ -347,7 +416,6 @@ protected: uint32_t mSmallCacheHeight; Vector<CacheTextureLine*> mCacheLines; uint32_t getRemainingCacheCapacity(); Font* mCurrentFont; Vector<Font*> mActiveFonts; Loading
tests/HwAccelerationTest/AndroidManifest.xml +9 −0 Original line number Diff line number Diff line Loading @@ -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"> Loading