Loading libs/hwui/FontRenderer.cpp +145 −19 Original line number Diff line number Diff line Loading @@ -39,6 +39,8 @@ namespace uirenderer { #define MAX_TEXT_CACHE_WIDTH 2048 #define TEXTURE_BORDER_SIZE 2 #define AUTO_KERN(prev, next) (((next) - (prev) + 32) >> 6 << 16) /////////////////////////////////////////////////////////////////////////////// // CacheTextureLine /////////////////////////////////////////////////////////////////////////////// Loading Loading @@ -163,6 +165,44 @@ void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y, } } 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; float nPenX = glyph->mBitmapLeft; vOffset += glyph->mBitmapTop + height; const float u1 = glyph->mBitmapMinU; const float u2 = glyph->mBitmapMaxU; const float v1 = glyph->mBitmapMinV; const float v2 = glyph->mBitmapMaxV; SkPoint destination[4]; measure.getPosTan(x + hOffset + nPenX + 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); 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->mCachedTextureLine->mCacheTexture); } CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit) { CachedGlyphInfo* cachedGlyph = NULL; ssize_t index = mCachedGlyphs.indexOfKey(textUnit); Loading Loading @@ -198,6 +238,56 @@ void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len 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, roundf(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) { if (bounds == NULL) { Loading @@ -208,8 +298,6 @@ 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, NULL); } #define SkAutoKern_AdjustF(prev, next) (((next) - (prev) + 32) >> 6 << 16) void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len, int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) { Loading @@ -217,10 +305,6 @@ void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len return; } int glyphsCount = 0; text += start; static RenderGlyph gRenderGlyph[] = { &android::uirenderer::Font::drawCachedGlyph, &android::uirenderer::Font::drawCachedGlyphBitmap, Loading @@ -228,14 +312,15 @@ void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len }; RenderGlyph render = gRenderGlyph[mode]; text += start; int glyphsCount = 0; if (CC_LIKELY(positions == NULL)) { SkFixed prevRsbDelta = 0; float penX = x; float penX = x + 0.5f; int penY = y; penX += 0.5f; while (glyphsCount < numGlyphs) { glyph_t glyph = GET_GLYPH(text); Loading @@ -245,7 +330,7 @@ void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len } CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph); penX += SkFixedToFloat(SkAutoKern_AdjustF(prevRsbDelta, cachedGlyph->mLsbDelta)); 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 Loading Loading @@ -582,6 +667,7 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol]; } } cachedGlyph->mIsValid = true; } Loading Loading @@ -758,15 +844,9 @@ void FontRenderer::issueDrawCommand() { mDrawn = true; } void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1, float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1, float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, float x4, float y4, float u4, float v4, CacheTexture* texture) { if (mClip && (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) { return; } if (texture != mCurrentCacheTexture) { if (mCurrentQuadIndex != 0) { // First, draw everything stored already which uses the previous texture Loading Loading @@ -802,6 +882,18 @@ void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1, (*currentPos++) = v4; mCurrentQuadIndex++; } void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1, float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, float x4, float y4, float u4, float v4, CacheTexture* texture) { if (mClip && (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) { return; } appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture); if (mBounds) { mBounds->left = fmin(mBounds->left, x1); Loading @@ -816,6 +908,25 @@ void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1, } } void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1, float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, float x4, float y4, float u4, float v4, CacheTexture* texture) { appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture); if (mBounds) { mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4)))); mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4)))); mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4)))); mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4)))); } if (mCurrentQuadIndex == mMaxNumberOfQuads) { issueDrawCommand(); mCurrentQuadIndex = 0; } } uint32_t FontRenderer::getRemainingCacheCapacity() { uint32_t remainingCapacity = 0; float totalPixels = 0; Loading Loading @@ -956,6 +1067,21 @@ bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *t return mDrawn; } bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path, float hOffset, float vOffset, Rect* bounds) { if (!mCurrentFont) { ALOGE("No font set"); return false; } initRender(clip, bounds); mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset); finishRender(); return mDrawn; } void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) { // Compute gaussian weights for the blur // e is the euler's number Loading libs/hwui/FontRenderer.h +19 −1 Original line number Diff line number Diff line Loading @@ -24,6 +24,8 @@ #include <SkScalerContext.h> #include <SkPaint.h> #include <SkPathMeasure.h> #include <SkPoint.h> #include <GLES2/gl2.h> Loading Loading @@ -155,6 +157,9 @@ public: 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. */ Loading Loading @@ -200,6 +205,8 @@ protected: 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); Loading Loading @@ -244,6 +251,9 @@ public: // bounds is an out parameter bool renderPosText(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, const float* positions, Rect* bounds); // bounds is an out parameter bool renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path, float hOffset, float vOffset, Rect* bounds); struct DropShadow { DropShadow() { }; Loading Loading @@ -320,10 +330,18 @@ protected: void precacheLatin(SkPaint* paint); void issueDrawCommand(); void appendMeshQuadNoClip(float x1, float y1, float u1, float v1, float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, float x4, float y4, float u4, float v4, CacheTexture* texture); void appendMeshQuad(float x1, float y1, float u1, float v1, float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, float x4, float y4, float u4, float v4, CacheTexture* texture); void appendRotatedMeshQuad(float x1, float y1, float u1, float v1, float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, float x4, float y4, float u4, float v4, CacheTexture* texture); uint32_t mSmallCacheWidth; uint32_t mSmallCacheHeight; Loading libs/hwui/OpenGLRenderer.cpp +20 −40 Original line number Diff line number Diff line Loading @@ -2300,15 +2300,6 @@ void OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int count, return; } float x = 0.0f; float y = 0.0f; const bool pureTranslate = mSnapshot->transform->isPureTranslate(); if (CC_LIKELY(pureTranslate)) { x = (int) floorf(x + mSnapshot->transform->getTranslateX() + 0.5f); y = (int) floorf(y + mSnapshot->transform->getTranslateY() + 0.5f); } FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer(paint); fontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()), paint->getTextSize()); Loading @@ -2326,41 +2317,30 @@ void OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int count, setupDrawShader(); setupDrawBlending(true, mode); setupDrawProgram(); setupDrawModelView(x, y, x, y, pureTranslate, true); setupDrawModelView(0.0f, 0.0f, 0.0f, 0.0f, false, true); setupDrawTexture(fontRenderer.getTexture(true)); setupDrawPureColorUniforms(); setupDrawColorFilterUniforms(); setupDrawShaderUniforms(pureTranslate); setupDrawShaderUniforms(false); const Rect* clip = &mSnapshot->getLocalClip(); Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f); // mat4 pathTransform; // pathTransform.loadTranslate(hOffset, vOffset, 0.0f); // // float offset = 0.0f; // SkPathMeasure pathMeasure(*path, false); // // if (paint->getTextAlign() != SkPaint::kLeft_Align) { // SkScalar pathLength = pathMeasure.getLength(); // if (paint->getTextAlign() == SkPaint::kCenter_Align) { // pathLength = SkScalarHalf(pathLength); // } // offset += SkScalarToFloat(pathLength); // } // SkScalar x; // SkPath tmp; // SkMatrix m(scaledMatrix); // // m.postTranslate(xpos + hOffset, 0); // if (matrix) { // m.postConcat(*matrix); // } // morphpath(&tmp, *iterPath, meas, m); // if (fDevice) { // fDevice->drawPath(*this, tmp, iter.getPaint(), NULL, true); // } else { // this->drawPath(tmp, iter.getPaint(), NULL, true); // } // } #if RENDER_LAYERS_AS_REGIONS const bool hasActiveLayer = hasLayer(); #else const bool hasActiveLayer = false; #endif if (fontRenderer.renderTextOnPath(paint, clip, text, 0, bytesCount, count, path, hOffset, vOffset, hasActiveLayer ? &bounds : NULL)) { #if RENDER_LAYERS_AS_REGIONS if (hasActiveLayer) { mSnapshot->transform->mapRect(bounds); dirtyLayerUnchecked(bounds, getRegion()); } #endif } } void OpenGLRenderer::drawPath(SkPath* path, SkPaint* paint) { Loading tests/HwAccelerationTest/src/com/android/test/hwui/TextOnPathActivity.java +54 −1 Original line number Diff line number Diff line Loading @@ -21,18 +21,21 @@ import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PathMeasure; import android.os.Bundle; import android.view.View; @SuppressWarnings({"UnusedDeclaration"}) public class TextOnPathActivity extends Activity { private Path mPath; private Path mStraightPath; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mPath = makePath(); mStraightPath = makeStraightPath(); final TextOnPathView view = new TextOnPathView(this); setContentView(view); Loading @@ -51,11 +54,28 @@ public class TextOnPathActivity extends Activity { path.cubicTo(-80.0f, 200.0f, 100.0f, 200.0f, 200.0f, 0.0f); } private Path makeStraightPath() { Path path = new Path(); buildStraightPath(path); return path; } private void buildStraightPath(Path path) { path.moveTo(0.0f, 0.0f); path.lineTo(400.0f, 0.0f); } public class TextOnPathView extends View { private static final String TEST_STRING = "Hello OpenGL renderer, text on path! "; private final Paint mPaint; private final Paint mPathPaint; private final String mText; private final PathMeasure mMeasure; private final float mLength; private final float[] mLines; private final float[] mPos; private final float[] mTan; public TextOnPathView(Context c) { super(c); Loading @@ -64,11 +84,23 @@ public class TextOnPathActivity extends Activity { mPaint.setAntiAlias(true); mPaint.setColor(0xff000000); mPathPaint = new Paint(); mPathPaint.setAntiAlias(true); mPathPaint.setStyle(Paint.Style.STROKE); mPathPaint.setColor(0xff000099); StringBuilder builder = new StringBuilder(TEST_STRING.length() * 2); for (int i = 0; i < 2; i++) { builder.append(TEST_STRING); } mText = builder.toString(); mMeasure = new PathMeasure(mPath, false); mLength = mMeasure.getLength(); mLines = new float[100 * 4]; mPos = new float[2]; mTan = new float[2]; } @Override Loading @@ -81,19 +113,40 @@ public class TextOnPathActivity extends Activity { canvas.translate(400.0f, 350.0f); mPaint.setTextAlign(Paint.Align.LEFT); canvas.drawTextOnPath(mText + mText, mPath, 0.0f, 0.0f, mPaint); canvas.drawPath(mPath, mPathPaint); for (int i = 0; i < 100; i++) { mMeasure.getPosTan(i * mLength / 100.0f, mPos, mTan); mLines[i * 4 ] = mPos[0]; mLines[i * 4 + 1] = mPos[1]; mLines[i * 4 + 2] = mPos[0] + mTan[1] * 15; mLines[i * 4 + 3] = mPos[1] - mTan[0] * 15; } canvas.drawLines(mLines, mPathPaint); canvas.translate(200.0f, 0.0f); canvas.drawTextOnPath(mText + mText, mStraightPath, 0.0f, 0.0f, mPaint); canvas.drawPath(mStraightPath, mPathPaint); canvas.restore(); canvas.save(); canvas.translate(150.0f, 60.0f); canvas.drawTextOnPath(mText, mPath, 0.0f, 0.0f, mPaint); canvas.drawTextOnPath(mText, mPath, 0.0f, 10.0f, mPaint); mMeasure.getPosTan(5.0f, mPos, mTan); canvas.drawLine(mPos[0], mPos[1], mPos[0] + mTan[1] * 10, mPos[1] - mTan[0] * 10, mPathPaint); canvas.drawPath(mPath, mPathPaint); canvas.translate(250.0f, 0.0f); mPaint.setTextAlign(Paint.Align.CENTER); canvas.drawTextOnPath(mText, mPath, 0.0f, 0.0f, mPaint); canvas.drawPath(mPath, mPathPaint); canvas.translate(250.0f, 0.0f); mPaint.setTextAlign(Paint.Align.RIGHT); canvas.drawTextOnPath(mText, mPath, 0.0f, 0.0f, mPaint); canvas.drawPath(mPath, mPathPaint); canvas.restore(); } } Loading Loading
libs/hwui/FontRenderer.cpp +145 −19 Original line number Diff line number Diff line Loading @@ -39,6 +39,8 @@ namespace uirenderer { #define MAX_TEXT_CACHE_WIDTH 2048 #define TEXTURE_BORDER_SIZE 2 #define AUTO_KERN(prev, next) (((next) - (prev) + 32) >> 6 << 16) /////////////////////////////////////////////////////////////////////////////// // CacheTextureLine /////////////////////////////////////////////////////////////////////////////// Loading Loading @@ -163,6 +165,44 @@ void Font::drawCachedGlyphBitmap(CachedGlyphInfo* glyph, int x, int y, } } 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; float nPenX = glyph->mBitmapLeft; vOffset += glyph->mBitmapTop + height; const float u1 = glyph->mBitmapMinU; const float u2 = glyph->mBitmapMaxU; const float v1 = glyph->mBitmapMinV; const float v2 = glyph->mBitmapMaxV; SkPoint destination[4]; measure.getPosTan(x + hOffset + nPenX + 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); 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->mCachedTextureLine->mCacheTexture); } CachedGlyphInfo* Font::getCachedGlyph(SkPaint* paint, glyph_t textUnit) { CachedGlyphInfo* cachedGlyph = NULL; ssize_t index = mCachedGlyphs.indexOfKey(textUnit); Loading Loading @@ -198,6 +238,56 @@ void Font::render(SkPaint* paint, const char *text, uint32_t start, uint32_t len 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, roundf(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) { if (bounds == NULL) { Loading @@ -208,8 +298,6 @@ 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, NULL); } #define SkAutoKern_AdjustF(prev, next) (((next) - (prev) + 32) >> 6 << 16) void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len, int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) { Loading @@ -217,10 +305,6 @@ void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len return; } int glyphsCount = 0; text += start; static RenderGlyph gRenderGlyph[] = { &android::uirenderer::Font::drawCachedGlyph, &android::uirenderer::Font::drawCachedGlyphBitmap, Loading @@ -228,14 +312,15 @@ void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len }; RenderGlyph render = gRenderGlyph[mode]; text += start; int glyphsCount = 0; if (CC_LIKELY(positions == NULL)) { SkFixed prevRsbDelta = 0; float penX = x; float penX = x + 0.5f; int penY = y; penX += 0.5f; while (glyphsCount < numGlyphs) { glyph_t glyph = GET_GLYPH(text); Loading @@ -245,7 +330,7 @@ void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len } CachedGlyphInfo* cachedGlyph = getCachedGlyph(paint, glyph); penX += SkFixedToFloat(SkAutoKern_AdjustF(prevRsbDelta, cachedGlyph->mLsbDelta)); 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 Loading Loading @@ -582,6 +667,7 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol]; } } cachedGlyph->mIsValid = true; } Loading Loading @@ -758,15 +844,9 @@ void FontRenderer::issueDrawCommand() { mDrawn = true; } void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1, float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, void FontRenderer::appendMeshQuadNoClip(float x1, float y1, float u1, float v1, float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, float x4, float y4, float u4, float v4, CacheTexture* texture) { if (mClip && (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) { return; } if (texture != mCurrentCacheTexture) { if (mCurrentQuadIndex != 0) { // First, draw everything stored already which uses the previous texture Loading Loading @@ -802,6 +882,18 @@ void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1, (*currentPos++) = v4; mCurrentQuadIndex++; } void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1, float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, float x4, float y4, float u4, float v4, CacheTexture* texture) { if (mClip && (x1 > mClip->right || y1 < mClip->top || x2 < mClip->left || y4 > mClip->bottom)) { return; } appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture); if (mBounds) { mBounds->left = fmin(mBounds->left, x1); Loading @@ -816,6 +908,25 @@ void FontRenderer::appendMeshQuad(float x1, float y1, float u1, float v1, } } void FontRenderer::appendRotatedMeshQuad(float x1, float y1, float u1, float v1, float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, float x4, float y4, float u4, float v4, CacheTexture* texture) { appendMeshQuadNoClip(x1, y1, u1, v1, x2, y2, u2, v2, x3, y3, u3, v3, x4, y4, u4, v4, texture); if (mBounds) { mBounds->left = fmin(mBounds->left, fmin(x1, fmin(x2, fmin(x3, x4)))); mBounds->top = fmin(mBounds->top, fmin(y1, fmin(y2, fmin(y3, y4)))); mBounds->right = fmax(mBounds->right, fmax(x1, fmax(x2, fmax(x3, x4)))); mBounds->bottom = fmax(mBounds->bottom, fmax(y1, fmax(y2, fmax(y3, y4)))); } if (mCurrentQuadIndex == mMaxNumberOfQuads) { issueDrawCommand(); mCurrentQuadIndex = 0; } } uint32_t FontRenderer::getRemainingCacheCapacity() { uint32_t remainingCapacity = 0; float totalPixels = 0; Loading Loading @@ -956,6 +1067,21 @@ bool FontRenderer::renderPosText(SkPaint* paint, const Rect* clip, const char *t return mDrawn; } bool FontRenderer::renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path, float hOffset, float vOffset, Rect* bounds) { if (!mCurrentFont) { ALOGE("No font set"); return false; } initRender(clip, bounds); mCurrentFont->render(paint, text, startIndex, len, numGlyphs, path, hOffset, vOffset); finishRender(); return mDrawn; } void FontRenderer::computeGaussianWeights(float* weights, int32_t radius) { // Compute gaussian weights for the blur // e is the euler's number Loading
libs/hwui/FontRenderer.h +19 −1 Original line number Diff line number Diff line Loading @@ -24,6 +24,8 @@ #include <SkScalerContext.h> #include <SkPaint.h> #include <SkPathMeasure.h> #include <SkPoint.h> #include <GLES2/gl2.h> Loading Loading @@ -155,6 +157,9 @@ public: 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. */ Loading Loading @@ -200,6 +205,8 @@ protected: 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); Loading Loading @@ -244,6 +251,9 @@ public: // bounds is an out parameter bool renderPosText(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex, uint32_t len, int numGlyphs, int x, int y, const float* positions, Rect* bounds); // bounds is an out parameter bool renderTextOnPath(SkPaint* paint, const Rect* clip, const char *text, uint32_t startIndex, uint32_t len, int numGlyphs, SkPath* path, float hOffset, float vOffset, Rect* bounds); struct DropShadow { DropShadow() { }; Loading Loading @@ -320,10 +330,18 @@ protected: void precacheLatin(SkPaint* paint); void issueDrawCommand(); void appendMeshQuadNoClip(float x1, float y1, float u1, float v1, float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, float x4, float y4, float u4, float v4, CacheTexture* texture); void appendMeshQuad(float x1, float y1, float u1, float v1, float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, float x4, float y4, float u4, float v4, CacheTexture* texture); void appendRotatedMeshQuad(float x1, float y1, float u1, float v1, float x2, float y2, float u2, float v2, float x3, float y3, float u3, float v3, float x4, float y4, float u4, float v4, CacheTexture* texture); uint32_t mSmallCacheWidth; uint32_t mSmallCacheHeight; Loading
libs/hwui/OpenGLRenderer.cpp +20 −40 Original line number Diff line number Diff line Loading @@ -2300,15 +2300,6 @@ void OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int count, return; } float x = 0.0f; float y = 0.0f; const bool pureTranslate = mSnapshot->transform->isPureTranslate(); if (CC_LIKELY(pureTranslate)) { x = (int) floorf(x + mSnapshot->transform->getTranslateX() + 0.5f); y = (int) floorf(y + mSnapshot->transform->getTranslateY() + 0.5f); } FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer(paint); fontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()), paint->getTextSize()); Loading @@ -2326,41 +2317,30 @@ void OpenGLRenderer::drawTextOnPath(const char* text, int bytesCount, int count, setupDrawShader(); setupDrawBlending(true, mode); setupDrawProgram(); setupDrawModelView(x, y, x, y, pureTranslate, true); setupDrawModelView(0.0f, 0.0f, 0.0f, 0.0f, false, true); setupDrawTexture(fontRenderer.getTexture(true)); setupDrawPureColorUniforms(); setupDrawColorFilterUniforms(); setupDrawShaderUniforms(pureTranslate); setupDrawShaderUniforms(false); const Rect* clip = &mSnapshot->getLocalClip(); Rect bounds(FLT_MAX / 2.0f, FLT_MAX / 2.0f, FLT_MIN / 2.0f, FLT_MIN / 2.0f); // mat4 pathTransform; // pathTransform.loadTranslate(hOffset, vOffset, 0.0f); // // float offset = 0.0f; // SkPathMeasure pathMeasure(*path, false); // // if (paint->getTextAlign() != SkPaint::kLeft_Align) { // SkScalar pathLength = pathMeasure.getLength(); // if (paint->getTextAlign() == SkPaint::kCenter_Align) { // pathLength = SkScalarHalf(pathLength); // } // offset += SkScalarToFloat(pathLength); // } // SkScalar x; // SkPath tmp; // SkMatrix m(scaledMatrix); // // m.postTranslate(xpos + hOffset, 0); // if (matrix) { // m.postConcat(*matrix); // } // morphpath(&tmp, *iterPath, meas, m); // if (fDevice) { // fDevice->drawPath(*this, tmp, iter.getPaint(), NULL, true); // } else { // this->drawPath(tmp, iter.getPaint(), NULL, true); // } // } #if RENDER_LAYERS_AS_REGIONS const bool hasActiveLayer = hasLayer(); #else const bool hasActiveLayer = false; #endif if (fontRenderer.renderTextOnPath(paint, clip, text, 0, bytesCount, count, path, hOffset, vOffset, hasActiveLayer ? &bounds : NULL)) { #if RENDER_LAYERS_AS_REGIONS if (hasActiveLayer) { mSnapshot->transform->mapRect(bounds); dirtyLayerUnchecked(bounds, getRegion()); } #endif } } void OpenGLRenderer::drawPath(SkPath* path, SkPaint* paint) { Loading
tests/HwAccelerationTest/src/com/android/test/hwui/TextOnPathActivity.java +54 −1 Original line number Diff line number Diff line Loading @@ -21,18 +21,21 @@ import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PathMeasure; import android.os.Bundle; import android.view.View; @SuppressWarnings({"UnusedDeclaration"}) public class TextOnPathActivity extends Activity { private Path mPath; private Path mStraightPath; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mPath = makePath(); mStraightPath = makeStraightPath(); final TextOnPathView view = new TextOnPathView(this); setContentView(view); Loading @@ -51,11 +54,28 @@ public class TextOnPathActivity extends Activity { path.cubicTo(-80.0f, 200.0f, 100.0f, 200.0f, 200.0f, 0.0f); } private Path makeStraightPath() { Path path = new Path(); buildStraightPath(path); return path; } private void buildStraightPath(Path path) { path.moveTo(0.0f, 0.0f); path.lineTo(400.0f, 0.0f); } public class TextOnPathView extends View { private static final String TEST_STRING = "Hello OpenGL renderer, text on path! "; private final Paint mPaint; private final Paint mPathPaint; private final String mText; private final PathMeasure mMeasure; private final float mLength; private final float[] mLines; private final float[] mPos; private final float[] mTan; public TextOnPathView(Context c) { super(c); Loading @@ -64,11 +84,23 @@ public class TextOnPathActivity extends Activity { mPaint.setAntiAlias(true); mPaint.setColor(0xff000000); mPathPaint = new Paint(); mPathPaint.setAntiAlias(true); mPathPaint.setStyle(Paint.Style.STROKE); mPathPaint.setColor(0xff000099); StringBuilder builder = new StringBuilder(TEST_STRING.length() * 2); for (int i = 0; i < 2; i++) { builder.append(TEST_STRING); } mText = builder.toString(); mMeasure = new PathMeasure(mPath, false); mLength = mMeasure.getLength(); mLines = new float[100 * 4]; mPos = new float[2]; mTan = new float[2]; } @Override Loading @@ -81,19 +113,40 @@ public class TextOnPathActivity extends Activity { canvas.translate(400.0f, 350.0f); mPaint.setTextAlign(Paint.Align.LEFT); canvas.drawTextOnPath(mText + mText, mPath, 0.0f, 0.0f, mPaint); canvas.drawPath(mPath, mPathPaint); for (int i = 0; i < 100; i++) { mMeasure.getPosTan(i * mLength / 100.0f, mPos, mTan); mLines[i * 4 ] = mPos[0]; mLines[i * 4 + 1] = mPos[1]; mLines[i * 4 + 2] = mPos[0] + mTan[1] * 15; mLines[i * 4 + 3] = mPos[1] - mTan[0] * 15; } canvas.drawLines(mLines, mPathPaint); canvas.translate(200.0f, 0.0f); canvas.drawTextOnPath(mText + mText, mStraightPath, 0.0f, 0.0f, mPaint); canvas.drawPath(mStraightPath, mPathPaint); canvas.restore(); canvas.save(); canvas.translate(150.0f, 60.0f); canvas.drawTextOnPath(mText, mPath, 0.0f, 0.0f, mPaint); canvas.drawTextOnPath(mText, mPath, 0.0f, 10.0f, mPaint); mMeasure.getPosTan(5.0f, mPos, mTan); canvas.drawLine(mPos[0], mPos[1], mPos[0] + mTan[1] * 10, mPos[1] - mTan[0] * 10, mPathPaint); canvas.drawPath(mPath, mPathPaint); canvas.translate(250.0f, 0.0f); mPaint.setTextAlign(Paint.Align.CENTER); canvas.drawTextOnPath(mText, mPath, 0.0f, 0.0f, mPaint); canvas.drawPath(mPath, mPathPaint); canvas.translate(250.0f, 0.0f); mPaint.setTextAlign(Paint.Align.RIGHT); canvas.drawTextOnPath(mText, mPath, 0.0f, 0.0f, mPaint); canvas.drawPath(mPath, mPathPaint); canvas.restore(); } } Loading