Add drop shadow for drawPosText in hwui renderer.

This patch adds support for drop shadows (setShadowLayer) for
drawPosText in the hwui renderer. In and of itself, it's not very
important, but it's on the critical path for correct mark positioning,
tracked as bug 5443796.

The change itself is fairly straightforward - it basically just adds an
extra "positions" argument to all draw and measure methods on the code
path for drawing drop shadowed text, as well as to the cache key for
cached shadow textures.

Change-Id: Ic1cb63299ba61ccbef31779459ecb82aa4a5e672
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp
index 3b3691c..ff9c2df 100644
--- a/libs/hwui/FontRenderer.cpp
+++ b/libs/hwui/FontRenderer.cpp
@@ -288,13 +288,13 @@
 }
 
 void Font::measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
-        int numGlyphs, Rect *bounds) {
+        int numGlyphs, Rect *bounds, const float* positions) {
     if (bounds == NULL) {
         ALOGE("No return rectangle provided to measure text");
         return;
     }
     bounds->set(1e6, -1e6, -1e6, 1e6);
-    render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, NULL);
+    render(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds, positions);
 }
 
 void Font::render(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
@@ -1008,7 +1008,7 @@
 }
 
 FontRenderer::DropShadow FontRenderer::renderDropShadow(SkPaint* paint, const char *text,
-        uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius) {
+        uint32_t startIndex, uint32_t len, int numGlyphs, uint32_t radius, const float* positions) {
     checkInit();
 
     if (!mCurrentFont) {
@@ -1026,7 +1026,7 @@
     mBounds = NULL;
 
     Rect bounds;
-    mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds);
+    mCurrentFont->measure(paint, text, startIndex, len, numGlyphs, &bounds, positions);
 
     uint32_t paddedWidth = (uint32_t) (bounds.right - bounds.left) + 2 * radius;
     uint32_t paddedHeight = (uint32_t) (bounds.top - bounds.bottom) + 2 * radius;
@@ -1040,7 +1040,7 @@
     int penY = radius - bounds.bottom;
 
     mCurrentFont->render(paint, text, startIndex, len, numGlyphs, penX, penY,
-            dataBuffer, paddedWidth, paddedHeight);
+            Font::BITMAP, dataBuffer, paddedWidth, paddedHeight, NULL, positions);
     blurImage(dataBuffer, paddedWidth, paddedHeight, radius);
 
     DropShadow image;
diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h
index 2ab680e..021cad9 100644
--- a/libs/hwui/FontRenderer.h
+++ b/libs/hwui/FontRenderer.h
@@ -185,7 +185,7 @@
             uint32_t bitmapW, uint32_t bitmapH, Rect *bounds, const float* positions);
 
     void measure(SkPaint* paint, const char* text, uint32_t start, uint32_t len,
-            int numGlyphs, Rect *bounds);
+            int numGlyphs, Rect *bounds, const float* positions);
 
     Font(FontRenderer* state, uint32_t fontId, float fontSize, int flags, uint32_t italicStyle,
             uint32_t scaleX, SkPaint::Style style, uint32_t strokeWidth);
@@ -274,7 +274,7 @@
     // After renderDropShadow returns, the called owns the memory in DropShadow.image
     // and is responsible for releasing it when it's done with it
     DropShadow renderDropShadow(SkPaint* paint, const char *text, uint32_t startIndex,
-            uint32_t len, int numGlyphs, uint32_t radius);
+            uint32_t len, int numGlyphs, uint32_t radius, const float* positions);
 
     GLuint getTexture(bool linearFiltering = false) {
         checkInit();
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index a47d732..fe056b0 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -2294,6 +2294,44 @@
     return DrawGlInfo::kStatusDrew;
 }
 
+void OpenGLRenderer::drawTextShadow(SkPaint* paint, const char* text, int bytesCount, int count,
+        const float* positions, FontRenderer& fontRenderer, int alpha, SkXfermode::Mode mode,
+        float x, float y) {
+    mCaches.activeTexture(0);
+
+    // NOTE: The drop shadow will not perform gamma correction
+    //       if shader-based correction is enabled
+    mCaches.dropShadowCache.setFontRenderer(fontRenderer);
+    const ShadowTexture* shadow = mCaches.dropShadowCache.get(
+            paint, text, bytesCount, count, mShadowRadius, positions);
+    const AutoTexture autoCleanup(shadow);
+
+    const float sx = x - shadow->left + mShadowDx;
+    const float sy = y - shadow->top + mShadowDy;
+
+    const int shadowAlpha = ((mShadowColor >> 24) & 0xFF) * mSnapshot->alpha;
+    int shadowColor = mShadowColor;
+    if (mShader) {
+        shadowColor = 0xffffffff;
+    }
+
+    setupDraw();
+    setupDrawWithTexture(true);
+    setupDrawAlpha8Color(shadowColor, shadowAlpha < 255 ? shadowAlpha : alpha);
+    setupDrawColorFilter();
+    setupDrawShader();
+    setupDrawBlending(true, mode);
+    setupDrawProgram();
+    setupDrawModelView(sx, sy, sx + shadow->width, sy + shadow->height);
+    setupDrawTexture(shadow->id);
+    setupDrawPureColorUniforms();
+    setupDrawColorFilterUniforms();
+    setupDrawShaderUniforms();
+    setupDrawMesh(NULL, (GLvoid*) gMeshTextureOffset);
+
+    glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
+}
+
 status_t OpenGLRenderer::drawPosText(const char* text, int bytesCount, int count,
         const float* positions, SkPaint* paint) {
     if (text == NULL || count == 0 || mSnapshot->isIgnored() ||
@@ -2322,6 +2360,11 @@
     SkXfermode::Mode mode;
     getAlphaAndMode(paint, &alpha, &mode);
 
+    if (CC_UNLIKELY(mHasShadow)) {
+        drawTextShadow(paint, text, bytesCount, count, positions, fontRenderer, alpha, mode,
+                0.0f, 0.0f);
+    }
+
     // Pick the appropriate texture filtering
     bool linearFilter = mSnapshot->transform->changesBounds();
     if (pureTranslate && !linearFilter) {
@@ -2415,39 +2458,7 @@
     getAlphaAndMode(paint, &alpha, &mode);
 
     if (CC_UNLIKELY(mHasShadow)) {
-        mCaches.activeTexture(0);
-
-        // NOTE: The drop shadow will not perform gamma correction
-        //       if shader-based correction is enabled
-        mCaches.dropShadowCache.setFontRenderer(fontRenderer);
-        const ShadowTexture* shadow = mCaches.dropShadowCache.get(
-                paint, text, bytesCount, count, mShadowRadius);
-        const AutoTexture autoCleanup(shadow);
-
-        const float sx = oldX - shadow->left + mShadowDx;
-        const float sy = oldY - shadow->top + mShadowDy;
-
-        const int shadowAlpha = ((mShadowColor >> 24) & 0xFF) * mSnapshot->alpha;
-        int shadowColor = mShadowColor;
-        if (mShader) {
-            shadowColor = 0xffffffff;
-        }
-
-        setupDraw();
-        setupDrawWithTexture(true);
-        setupDrawAlpha8Color(shadowColor, shadowAlpha < 255 ? shadowAlpha : alpha);
-        setupDrawColorFilter();
-        setupDrawShader();
-        setupDrawBlending(true, mode);
-        setupDrawProgram();
-        setupDrawModelView(sx, sy, sx + shadow->width, sy + shadow->height);
-        setupDrawTexture(shadow->id);
-        setupDrawPureColorUniforms();
-        setupDrawColorFilterUniforms();
-        setupDrawShaderUniforms();
-        setupDrawMesh(NULL, (GLvoid*) gMeshTextureOffset);
-
-        glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
+        drawTextShadow(paint, text, bytesCount, count, NULL, fontRenderer, alpha, mode, oldX, oldY);
     }
 
     // Pick the appropriate texture filtering
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index 1926575..441e9fd 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -498,6 +498,24 @@
     void drawTextDecorations(const char* text, int bytesCount, float length,
             float x, float y, SkPaint* paint);
 
+   /**
+     * Draws shadow layer on text (with optional positions).
+     *
+     * @param paint The paint to draw the shadow with
+     * @param text The text to draw
+     * @param bytesCount The number of bytes in the text
+     * @param count The number of glyphs in the text
+     * @param positions The x, y positions of individual glyphs (or NULL)
+     * @param fontRenderer The font renderer object
+     * @param alpha The alpha value for drawing the shadow
+     * @param mode The xfermode for drawing the shadow
+     * @param x The x coordinate where the shadow will be drawn
+     * @param y The y coordinate where the shadow will be drawn
+     */
+    void drawTextShadow(SkPaint* paint, const char* text, int bytesCount, int count,
+            const float* positions, FontRenderer& fontRenderer, int alpha, SkXfermode::Mode mode,
+            float x, float y);
+
     /**
      * Draws a path texture. Path textures are alpha8 bitmaps that need special
      * compositing to apply colors/filters/etc.
@@ -507,7 +525,7 @@
      * @param y The y coordinate where the texture will be drawn
      * @param paint The paint to draw the texture with
      */
-    void drawPathTexture(const PathTexture* texture, float x, float y, SkPaint* paint);
+     void drawPathTexture(const PathTexture* texture, float x, float y, SkPaint* paint);
 
     /**
      * Resets the texture coordinates stored in mMeshVertices. Setting the values
diff --git a/libs/hwui/TextDropShadowCache.cpp b/libs/hwui/TextDropShadowCache.cpp
index bef1373..93aa8a5 100644
--- a/libs/hwui/TextDropShadowCache.cpp
+++ b/libs/hwui/TextDropShadowCache.cpp
@@ -102,13 +102,13 @@
 }
 
 ShadowTexture* TextDropShadowCache::get(SkPaint* paint, const char* text, uint32_t len,
-        int numGlyphs, uint32_t radius) {
-    ShadowText entry(paint, radius, len, text);
+        int numGlyphs, uint32_t radius, const float* positions) {
+    ShadowText entry(paint, radius, len, text, positions);
     ShadowTexture* texture = mCache.get(entry);
 
     if (!texture) {
         FontRenderer::DropShadow shadow = mRenderer->renderDropShadow(paint, text, 0,
-                len, numGlyphs, radius);
+                len, numGlyphs, radius, positions);
 
         texture = new ShadowTexture;
         texture->left = shadow.penX;
diff --git a/libs/hwui/TextDropShadowCache.h b/libs/hwui/TextDropShadowCache.h
index e2bdde1..bae0c49 100644
--- a/libs/hwui/TextDropShadowCache.h
+++ b/libs/hwui/TextDropShadowCache.h
@@ -35,8 +35,9 @@
     ShadowText(): radius(0), len(0), textSize(0.0f), typeface(NULL) {
     }
 
-    ShadowText(SkPaint* paint, uint32_t radius, uint32_t len, const char* srcText):
-            radius(radius), len(len) {
+    ShadowText(SkPaint* paint, uint32_t radius, uint32_t len, const char* srcText,
+            const float* positions):
+            radius(radius), len(len), positions(positions) {
         // TODO: Propagate this through the API, we should not cast here
         text = (const char16_t*) srcText;
 
@@ -66,11 +67,18 @@
     uint32_t italicStyle;
     uint32_t scaleX;
     const char16_t* text;
+    const float* positions;
     String16 str;
+    Vector<float> positionsCopy;
 
     void copyTextLocally() {
         str.setTo((const char16_t*) text, len >> 1);
         text = str.string();
+        if (positions != NULL) {
+            positionsCopy.clear();
+            positionsCopy.appendArray(positions, len);
+            positions = positionsCopy.array();
+        }
     }
 
     bool operator<(const ShadowText& rhs) const {
@@ -81,7 +89,12 @@
                         LTE_INT(flags) {
                             LTE_INT(italicStyle) {
                                 LTE_INT(scaleX) {
-                                    return memcmp(text, rhs.text, len) < 0;
+                                    int cmp = memcmp(text, rhs.text, len);
+                                    if (cmp < 0) return true;
+                                    if (cmp == 0 && rhs.positions != NULL) {
+                                        if (positions == NULL) return true;
+                                        return memcmp(positions, rhs.positions, len << 2) < 0;
+                                    }
                                 }
                             }
                         }
@@ -117,7 +130,7 @@
     void operator()(ShadowText& text, ShadowTexture*& texture);
 
     ShadowTexture* get(SkPaint* paint, const char* text, uint32_t len,
-            int numGlyphs, uint32_t radius);
+            int numGlyphs, uint32_t radius, const float* positions);
 
     /**
      * Clears the cache. This causes all textures to be deleted.