Add drop shadows.

Change-Id: Ic6a72409d4785968d1fbdff229f17ee5c00b240b
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 3c1fe2a..da5b9dd 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -41,6 +41,7 @@
 #define DEFAULT_PATH_CACHE_SIZE 6.0f
 #define DEFAULT_PATCH_CACHE_SIZE 100
 #define DEFAULT_GRADIENT_CACHE_SIZE 0.5f
+#define DEFAULT_DROP_SHADOW_CACHE_SIZE 1.0f
 
 #define REQUIRED_TEXTURE_UNITS_COUNT 3
 
@@ -107,7 +108,8 @@
         mLayerCache(MB(DEFAULT_LAYER_CACHE_SIZE)),
         mGradientCache(MB(DEFAULT_GRADIENT_CACHE_SIZE)),
         mPathCache(MB(DEFAULT_PATH_CACHE_SIZE)),
-        mPatchCache(DEFAULT_PATCH_CACHE_SIZE) {
+        mPatchCache(DEFAULT_PATCH_CACHE_SIZE),
+        mDropShadowCache(MB(DEFAULT_DROP_SHADOW_CACHE_SIZE)) {
     LOGD("Create OpenGLRenderer");
 
     char property[PROPERTY_VALUE_MAX];
@@ -139,9 +141,18 @@
         LOGD("  Using default path cache size of %.2fMB", DEFAULT_PATH_CACHE_SIZE);
     }
 
+    if (property_get(PROPERTY_DROP_SHADOW_CACHE_SIZE, property, NULL) > 0) {
+        LOGD("  Setting drop shadow cache size to %sMB", property);
+        mDropShadowCache.setMaxSize(MB(atof(property)));
+    } else {
+        LOGD("  Using default drop shadow cache size of %.2fMB", DEFAULT_DROP_SHADOW_CACHE_SIZE);
+    }
+    mDropShadowCache.setFontRenderer(mFontRenderer);
+
     mCurrentProgram = NULL;
     mShader = NULL;
     mColorFilter = NULL;
+    mHasShadow = false;
 
     memcpy(mMeshVertices, gMeshVertices, sizeof(gMeshVertices));
 
@@ -163,6 +174,7 @@
     mPathCache.clear();
     mPatchCache.clear();
     mProgramCache.clear();
+    mDropShadowCache.clear();
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -550,6 +562,78 @@
     drawColorRect(left, top, right, bottom, color, mode);
 }
 
+void OpenGLRenderer::renderShadow(const ShadowTexture* texture, float x, float y,
+        SkXfermode::Mode mode) {
+    const float sx = x - texture->left + mShadowDx;
+    const float sy = y - texture->top + mShadowDy;
+
+    const GLfloat a = ((mShadowColor >> 24) & 0xFF) / 255.0f;
+    const GLfloat r = a * ((mShadowColor >> 16) & 0xFF) / 255.0f;
+    const GLfloat g = a * ((mShadowColor >>  8) & 0xFF) / 255.0f;
+    const GLfloat b = a * ((mShadowColor      ) & 0xFF) / 255.0f;
+
+    GLuint textureUnit = 0;
+    renderTextureAlpha8(texture, textureUnit, sx, sy, r, g, b, a, mode, false);
+}
+
+void OpenGLRenderer::renderTextureAlpha8(const Texture* texture, GLuint& textureUnit,
+        float x, float y, float r, float g, float b, float a, SkXfermode::Mode mode,
+        bool applyFilters) {
+     // Describe the required shaders
+     ProgramDescription description;
+     description.hasTexture = true;
+     description.hasAlpha8Texture = true;
+
+     if (applyFilters) {
+         if (mShader) {
+             mShader->describe(description, mExtensions);
+         }
+         if (mColorFilter) {
+             mColorFilter->describe(description, mExtensions);
+         }
+     }
+
+     // Build and use the appropriate shader
+     useProgram(mProgramCache.get(description));
+
+     // Setup the blending mode
+     chooseBlending(true, mode);
+     bindTexture(texture->id, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, textureUnit);
+     glUniform1i(mCurrentProgram->getUniform("sampler"), textureUnit);
+
+     int texCoordsSlot = mCurrentProgram->getAttrib("texCoords");
+     glEnableVertexAttribArray(texCoordsSlot);
+
+     // Setup attributes
+     glVertexAttribPointer(mCurrentProgram->position, 2, GL_FLOAT, GL_FALSE,
+             gMeshStride, &mMeshVertices[0].position[0]);
+     glVertexAttribPointer(texCoordsSlot, 2, GL_FLOAT, GL_FALSE,
+             gMeshStride, &mMeshVertices[0].texture[0]);
+
+     // Setup uniforms
+     mModelView.loadTranslate(x, y, 0.0f);
+     mModelView.scale(texture->width, texture->height, 1.0f);
+     mCurrentProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
+
+     glUniform4f(mCurrentProgram->color, r, g, b, a);
+
+     textureUnit++;
+     if (applyFilters) {
+         // Setup attributes and uniforms required by the shaders
+         if (mShader) {
+             mShader->setupProgram(mCurrentProgram, mModelView, *mSnapshot, &textureUnit);
+         }
+         if (mColorFilter) {
+             mColorFilter->setupProgram(mCurrentProgram);
+         }
+     }
+
+     // Draw the mesh
+     glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
+
+     glDisableVertexAttribArray(texCoordsSlot);
+}
+
 #define kStdStrikeThru_Offset   (-6.0f / 21.0f)
 #define kStdUnderline_Offset    (1.0f / 9.0f)
 #define kStdUnderline_Thickness (1.0f / 18.0f)
@@ -578,6 +662,15 @@
     SkXfermode::Mode mode;
     getAlphaAndMode(paint, &alpha, &mode);
 
+    mFontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()), paint->getTextSize());
+    if (mHasShadow) {
+        glActiveTexture(gTextureUnits[0]);
+        const ShadowTexture* shadow = mDropShadowCache.get(paint, text, bytesCount,
+                count, mShadowRadius);
+        const AutoTexture autoCleanup(shadow);
+        renderShadow(shadow, x, y, mode);
+    }
+
     uint32_t color = paint->getColor();
     const GLfloat a = alpha / 255.0f;
     const GLfloat r = a * ((color >> 16) & 0xFF) / 255.0f;
@@ -624,7 +717,6 @@
     }
 
     const Rect& clip = mSnapshot->getLocalClip();
-    mFontRenderer.setFont(paint, SkTypeface::UniqueID(paint->getTypeface()), paint->getTextSize());
     mFontRenderer.renderText(paint, &clip, text, 0, bytesCount, count, x, y);
 
     glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
@@ -693,55 +785,9 @@
     const GLfloat g = a * ((color >>  8) & 0xFF) / 255.0f;
     const GLfloat b = a * ((color      ) & 0xFF) / 255.0f;
 
-    // Describe the required shaders
-    ProgramDescription description;
-    description.hasTexture = true;
-    description.hasAlpha8Texture = true;
-    if (mShader) {
-        mShader->describe(description, mExtensions);
-    }
-    if (mColorFilter) {
-        mColorFilter->describe(description, mExtensions);
-    }
-
-    // Build and use the appropriate shader
-    useProgram(mProgramCache.get(description));
-
-    // Setup the blending mode
-    chooseBlending(true, mode);
-    bindTexture(texture->id, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, textureUnit);
-    glUniform1i(mCurrentProgram->getUniform("sampler"), textureUnit);
-
-    int texCoordsSlot = mCurrentProgram->getAttrib("texCoords");
-    glEnableVertexAttribArray(texCoordsSlot);
-
-    // Setup attributes
-    glVertexAttribPointer(mCurrentProgram->position, 2, GL_FLOAT, GL_FALSE,
-            gMeshStride, &mMeshVertices[0].position[0]);
-    glVertexAttribPointer(texCoordsSlot, 2, GL_FLOAT, GL_FALSE,
-            gMeshStride, &mMeshVertices[0].texture[0]);
-
-    // Setup uniforms
-    mModelView.loadTranslate(texture->left - texture->offset,
-            texture->top - texture->offset, 0.0f);
-    mModelView.scale(texture->width, texture->height, 1.0f);
-    mCurrentProgram->set(mOrthoMatrix, mModelView, mSnapshot->transform);
-
-    glUniform4f(mCurrentProgram->color, r, g, b, a);
-
-    textureUnit++;
-    // Setup attributes and uniforms required by the shaders
-    if (mShader) {
-        mShader->setupProgram(mCurrentProgram, mModelView, *mSnapshot, &textureUnit);
-    }
-    if (mColorFilter) {
-        mColorFilter->setupProgram(mCurrentProgram);
-    }
-
-    // Draw the mesh
-    glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
-
-    glDisableVertexAttribArray(texCoordsSlot);
+    const float x = texture->left - texture->offset;
+    const float y = texture->top - texture->offset;
+    renderTextureAlpha8(texture, textureUnit, x, y, r, g, b, a, mode, true);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -772,6 +818,22 @@
 }
 
 ///////////////////////////////////////////////////////////////////////////////
+// Drop shadow
+///////////////////////////////////////////////////////////////////////////////
+
+void OpenGLRenderer::resetShadow() {
+    mHasShadow = false;
+}
+
+void OpenGLRenderer::setupShadow(float radius, float dx, float dy, int color) {
+    mHasShadow = true;
+    mShadowRadius = radius;
+    mShadowDx = dx;
+    mShadowDy = dy;
+    mShadowColor = color;
+}
+
+///////////////////////////////////////////////////////////////////////////////
 // Drawing implementation
 ///////////////////////////////////////////////////////////////////////////////