diff options
| author | 2012-02-28 18:17:02 -0800 | |
|---|---|---|
| committer | 2012-02-29 19:14:37 -0800 | |
| commit | 9777173eb6c9eb97c7921c8288ebc65e3ab3ce6f (patch) | |
| tree | 0c2749e602c75cf72a3a2d6ffd480a47f975fdd5 | |
| parent | 765dcf32307dbd93ce43f064c426ce157be2d2ae (diff) | |
Full implementation of Canvas.drawPath()
Change-Id: I23223b89770a0cd2b4762365bead9bfddb094290
| -rw-r--r-- | libs/hwui/FontRenderer.cpp | 164 | ||||
| -rw-r--r-- | libs/hwui/FontRenderer.h | 20 | ||||
| -rw-r--r-- | libs/hwui/OpenGLRenderer.cpp | 60 | ||||
| -rw-r--r-- | tests/HwAccelerationTest/src/com/android/test/hwui/TextOnPathActivity.java | 55 |
4 files changed, 238 insertions, 61 deletions
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp index 3df105be19ea..f2bb6ec7f723 100644 --- a/libs/hwui/FontRenderer.cpp +++ b/libs/hwui/FontRenderer.cpp @@ -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 /////////////////////////////////////////////////////////////////////////////// @@ -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); @@ -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) { @@ -208,19 +298,13 @@ 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) { + uint32_t bitmapW, uint32_t bitmapH, Rect* bounds, const float* positions) { if (numGlyphs == 0 || text == NULL || len == 0) { return; } - int glyphsCount = 0; - - text += start; - static RenderGlyph gRenderGlyph[] = { &android::uirenderer::Font::drawCachedGlyph, &android::uirenderer::Font::drawCachedGlyphBitmap, @@ -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); @@ -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 @@ -582,6 +667,7 @@ void FontRenderer::cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyp cacheBuffer[cacheY * cacheWidth + cacheX] = mGammaTable[tempCol]; } } + cachedGlyph->mIsValid = true; } @@ -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 @@ -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); @@ -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; @@ -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 diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h index b767be57fbf8..4fc5862b1ee5 100644 --- a/libs/hwui/FontRenderer.h +++ b/libs/hwui/FontRenderer.h @@ -24,6 +24,8 @@ #include <SkScalerContext.h> #include <SkPaint.h> +#include <SkPathMeasure.h> +#include <SkPoint.h> #include <GLES2/gl2.h> @@ -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. */ @@ -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); @@ -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() { }; @@ -305,7 +315,7 @@ protected: void allocateTextureMemory(CacheTexture* cacheTexture); void deallocateTextureMemory(CacheTexture* cacheTexture); void initTextTexture(); - CacheTexture *createCacheTexture(int width, int height, bool allocate); + CacheTexture* createCacheTexture(int width, int height, bool allocate); void cacheBitmap(const SkGlyph& glyph, CachedGlyphInfo* cachedGlyph, uint32_t *retOriginX, uint32_t *retOriginY); @@ -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; diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index ebb6d88a0f77..73625b721b82 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -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()); @@ -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) { diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/TextOnPathActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/TextOnPathActivity.java index b798ee702dfc..9849e3c61ba8 100644 --- a/tests/HwAccelerationTest/src/com/android/test/hwui/TextOnPathActivity.java +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/TextOnPathActivity.java @@ -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); @@ -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); @@ -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 @@ -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(); } } |