Correct implementation of saveLayer().

Change-Id: I5375126636913e0a84f2d6bbd0ebe40d2e4f2763
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 6c90704..7cf70f7 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -80,6 +80,21 @@
         { SkXfermode::kXor_Mode,     GL_ONE_MINUS_DST_ALPHA,  GL_ONE_MINUS_SRC_ALPHA }
 };
 
+static const Blender gBlendsSwap[] = {
+        { SkXfermode::kClear_Mode,   GL_ZERO,                 GL_ZERO },
+        { SkXfermode::kSrc_Mode,     GL_ZERO,                 GL_ONE },
+        { SkXfermode::kDst_Mode,     GL_ONE,                  GL_ZERO },
+        { SkXfermode::kSrcOver_Mode, GL_ONE_MINUS_DST_ALPHA,  GL_ONE },
+        { SkXfermode::kDstOver_Mode, GL_ONE,                  GL_ONE_MINUS_SRC_ALPHA },
+        { SkXfermode::kSrcIn_Mode,   GL_ZERO,                 GL_SRC_ALPHA },
+        { SkXfermode::kDstIn_Mode,   GL_DST_ALPHA,            GL_ZERO },
+        { SkXfermode::kSrcOut_Mode,  GL_ZERO,                 GL_ONE_MINUS_SRC_ALPHA },
+        { SkXfermode::kDstOut_Mode,  GL_ONE_MINUS_DST_ALPHA,  GL_ZERO },
+        { SkXfermode::kSrcATop_Mode, GL_ONE_MINUS_DST_ALPHA,  GL_SRC_ALPHA },
+        { SkXfermode::kDstATop_Mode, GL_DST_ALPHA,            GL_ONE_MINUS_SRC_ALPHA },
+        { SkXfermode::kXor_Mode,     GL_ONE_MINUS_DST_ALPHA,  GL_ONE_MINUS_SRC_ALPHA }
+};
+
 static const GLenum gTextureUnits[] = {
         GL_TEXTURE0,
         GL_TEXTURE1,
@@ -122,8 +137,6 @@
 
     mWidth = width;
     mHeight = height;
-    mFirstSnapshot->height = height;
-    mFirstSnapshot->viewport.set(0, 0, width, height);
 }
 
 void OpenGLRenderer::prepare() {
@@ -155,14 +168,19 @@
 }
 
 void OpenGLRenderer::releaseContext() {
-    glViewport(0, 0, mSnapshot->viewport.getWidth(), mSnapshot->viewport.getHeight());
+    glViewport(0, 0, mWidth, mHeight);
 
     glEnable(GL_SCISSOR_TEST);
     setScissorFromClip();
 
+    glDisable(GL_DITHER);
+
+    glBindFramebuffer(GL_FRAMEBUFFER, 0);
+
     if (mCaches.blend) {
         glEnable(GL_BLEND);
         glBlendFunc(mCaches.lastSrcMode, mCaches.lastDstMode);
+        glBlendEquation(GL_FUNC_ADD);
     } else {
         glDisable(GL_BLEND);
     }
@@ -202,17 +220,10 @@
 bool OpenGLRenderer::restoreSnapshot() {
     bool restoreClip = mSnapshot->flags & Snapshot::kFlagClipSet;
     bool restoreLayer = mSnapshot->flags & Snapshot::kFlagIsLayer;
-    bool restoreOrtho = mSnapshot->flags & Snapshot::kFlagDirtyOrtho;
 
     sp<Snapshot> current = mSnapshot;
     sp<Snapshot> previous = mSnapshot->previous;
 
-    if (restoreOrtho) {
-        Rect& r = previous->viewport;
-        glViewport(r.left, r.top, r.right, r.bottom);
-        mOrthoMatrix.load(current->orthoMatrix);
-    }
-
     mSaveCount--;
     mSnapshot = previous;
 
@@ -253,11 +264,7 @@
         mode = SkXfermode::kSrcOver_Mode;
     }
 
-    if (alpha > 0 && !mSnapshot->invisible) {
-        createLayer(mSnapshot, left, top, right, bottom, alpha, mode, flags);
-    } else {
-        mSnapshot->invisible = true;
-    }
+    createLayer(mSnapshot, left, top, right, bottom, alpha, mode, flags);
 
     return count;
 }
@@ -278,49 +285,42 @@
     LAYER_LOGD("Requesting layer %fx%f", right - left, bottom - top);
     LAYER_LOGD("Layer cache size = %d", mCaches.layerCache.getSize());
 
+    // Window coordinates of the layer
     Rect bounds(left, top, right, bottom);
-    // TODO: Apply transformations and treat layers in screen coordinates
-    // mSnapshot->transform->mapRect(bounds);
+    mSnapshot->transform->mapRect(bounds);
 
-    GLuint previousFbo = snapshot->previous.get() ? snapshot->previous->fbo : 0;
     LayerSize size(bounds.getWidth(), bounds.getHeight());
 
-    Layer* layer = mCaches.layerCache.get(size, previousFbo);
+    Layer* layer = mCaches.layerCache.get(size);
     if (!layer) {
         return false;
     }
 
-    glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo);
-
-    // Clear the FBO
-    glDisable(GL_SCISSOR_TEST);
-    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
-    glClear(GL_COLOR_BUFFER_BIT);
-    glEnable(GL_SCISSOR_TEST);
-
     layer->mode = mode;
-    layer->alpha = alpha / 255.0f;
+    layer->alpha = alpha;
     layer->layer.set(bounds);
 
     // Save the layer in the snapshot
     snapshot->flags |= Snapshot::kFlagIsLayer;
     snapshot->layer = layer;
-    snapshot->fbo = layer->fbo;
-    // TODO: Temporary until real layer support is implemented
-    snapshot->resetTransform(-bounds.left, -bounds.top, 0.0f);
-    // TODO: Temporary until real layer support is implemented
-    snapshot->resetClip(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight());
-    snapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight());
-    snapshot->height = bounds.getHeight();
-    snapshot->flags |= Snapshot::kFlagDirtyOrtho;
-    snapshot->orthoMatrix.load(mOrthoMatrix);
 
+    // Copy the framebuffer into the layer
+    glBindTexture(GL_TEXTURE_2D, layer->texture);
+    glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bounds.left, mHeight - bounds.bottom,
+            bounds.getWidth(), bounds.getHeight(), 0);
+
+    // Clear the framebuffer where the layer will draw
+    glScissor(bounds.left, mHeight - bounds.bottom, bounds.getWidth(), bounds.getHeight());
+    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
+    glClear(GL_COLOR_BUFFER_BIT);
+
+    if (flags & SkCanvas::kClipToLayer_SaveFlag) {
+        mSnapshot->clipTransformed(bounds);
+    }
+
+    // Restore the initial clip
     setScissorFromClip();
 
-    // Change the ortho projection
-    glViewport(0, 0, bounds.getWidth(), bounds.getHeight());
-    mOrthoMatrix.loadOrtho(0.0f, bounds.getWidth(), bounds.getHeight(), 0.0f, -1.0f, 1.0f);
-
     return true;
 }
 
@@ -330,22 +330,32 @@
         return;
     }
 
-    // Unbind current FBO and restore previous one
-    // Most of the time, previous->fbo will be 0 to bind the default buffer
-    glBindFramebuffer(GL_FRAMEBUFFER, previous->fbo);
-
     // Restore the clip from the previous snapshot
     const Rect& clip = *previous->clipRect;
-    glScissor(clip.left, previous->height - clip.bottom, clip.getWidth(), clip.getHeight());
+    glScissor(clip.left, mHeight - clip.bottom, clip.getWidth(), clip.getHeight());
 
     Layer* layer = current->layer;
     const Rect& rect = layer->layer;
 
-    // FBOs are already drawn with a top-left origin, don't flip the texture
+    if (layer->alpha < 255) {
+        glEnable(GL_BLEND);
+        glBlendFuncSeparate(GL_ZERO, GL_SRC_ALPHA, GL_DST_ALPHA, GL_ZERO);
+
+        drawColorRect(rect.left, rect.top, rect.right, rect.bottom,
+                layer->alpha << 24, SkXfermode::kSrcOver_Mode, true, true);
+
+        glBlendFunc(mCaches.lastSrcMode, mCaches.lastDstMode);
+        if (!mCaches.blend) {
+            glDisable(GL_BLEND);
+        }
+    }
+
+    // Layers are already drawn with a top-left origin, don't flip the texture
     resetDrawTextureTexCoords(0.0f, 1.0f, 1.0f, 0.0f);
 
-    drawTextureRect(rect.left, rect.top, rect.right, rect.bottom,
-            layer->texture, layer->alpha, layer->mode, layer->blend);
+    drawTextureMesh(rect.left, rect.top, rect.right, rect.bottom, layer->texture,
+            1.0f, layer->mode, layer->blend, &mMeshVertices[0].position[0],
+            &mMeshVertices[0].texture[0], NULL, 0, true, true);
 
     resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
 
@@ -355,7 +365,6 @@
     if (!mCaches.layerCache.put(size, layer)) {
         LAYER_LOGD("Deleting layer");
 
-        glDeleteFramebuffers(1, &layer->fbo);
         glDeleteTextures(1, &layer->texture);
 
         delete layer;
@@ -397,7 +406,7 @@
 
 void OpenGLRenderer::setScissorFromClip() {
     const Rect& clip = *mSnapshot->clipRect;
-    glScissor(clip.left, mSnapshot->height - clip.bottom, clip.getWidth(), clip.getHeight());
+    glScissor(clip.left, mHeight - clip.bottom, clip.getWidth(), clip.getHeight());
 }
 
 const Rect& OpenGLRenderer::getClipBounds() {
@@ -405,8 +414,6 @@
 }
 
 bool OpenGLRenderer::quickReject(float left, float top, float right, float bottom) {
-    if (mSnapshot->invisible) return true;
-
     Rect r(left, top, right, bottom);
     mSnapshot->transform->mapRect(r);
     return !mSnapshot->clipRect->intersects(r);
@@ -512,7 +519,6 @@
 }
 
 void OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) {
-    if (mSnapshot->invisible) return;
     const Rect& clip = *mSnapshot->clipRect;
     drawColorRect(clip.left, clip.top, clip.right, clip.bottom, color, mode, true);
 }
@@ -545,10 +551,10 @@
 
 void OpenGLRenderer::drawText(const char* text, int bytesCount, int count,
         float x, float y, SkPaint* paint) {
-    if (mSnapshot->invisible || text == NULL || count == 0 ||
-            (paint->getAlpha() == 0 && paint->getXfermode() == NULL)) {
+    if (text == NULL || count == 0 || (paint->getAlpha() == 0 && paint->getXfermode() == NULL)) {
         return;
     }
+    paint->setAntiAlias(true);
 
     float scaleX = paint->getTextScaleX();
     bool applyScaleX = scaleX < 0.9999f || scaleX > 1.0001f;
@@ -619,8 +625,6 @@
 }
 
 void OpenGLRenderer::drawPath(SkPath* path, SkPaint* paint) {
-    if (mSnapshot->invisible) return;
-
     GLuint textureUnit = 0;
     glActiveTexture(gTextureUnits[textureUnit]);
 
@@ -778,6 +782,7 @@
      }
 }
 
+// Same values used by Skia
 #define kStdStrikeThru_Offset   (-6.0f / 21.0f)
 #define kStdUnderline_Offset    (1.0f / 9.0f)
 #define kStdUnderline_Thickness (1.0f / 18.0f)
@@ -830,7 +835,7 @@
 }
 
 void OpenGLRenderer::drawColorRect(float left, float top, float right, float bottom,
-        int color, SkXfermode::Mode mode, bool ignoreTransform) {
+        int color, SkXfermode::Mode mode, bool ignoreTransform, bool ignoreBlending) {
     // If a shader is set, preserve only the alpha
     if (mShader) {
         color |= 0x00ffffff;
@@ -854,8 +859,10 @@
         mColorFilter->describe(description, mExtensions);
     }
 
-    // Setup the blending mode
-    chooseBlending(alpha < 255 || (mShader && mShader->blend()), mode, description);
+    if (!ignoreBlending) {
+        // Setup the blending mode
+        chooseBlending(alpha < 255 || (mShader && mShader->blend()), mode, description);
+    }
 
     // Build and use the appropriate shader
     useProgram(mCaches.programCache.get(description));
@@ -905,7 +912,8 @@
 
 void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float bottom,
         GLuint texture, float alpha, SkXfermode::Mode mode, bool blend,
-        GLvoid* vertices, GLvoid* texCoords, GLvoid* indices, GLsizei elementsCount) {
+        GLvoid* vertices, GLvoid* texCoords, GLvoid* indices, GLsizei elementsCount,
+        bool swapSrcDst, bool ignoreTransform) {
     ProgramDescription description;
     description.hasTexture = true;
     if (mColorFilter) {
@@ -915,10 +923,15 @@
     mModelView.loadTranslate(left, top, 0.0f);
     mModelView.scale(right - left, bottom - top, 1.0f);
 
-    chooseBlending(blend || alpha < 1.0f, mode, description);
+    chooseBlending(blend || alpha < 1.0f, mode, description, swapSrcDst);
 
     useProgram(mCaches.programCache.get(description));
-    mCaches.currentProgram->set(mOrthoMatrix, mModelView, *mSnapshot->transform);
+    if (!ignoreTransform) {
+        mCaches.currentProgram->set(mOrthoMatrix, mModelView, *mSnapshot->transform);
+    } else {
+        mat4 m;
+        mCaches.currentProgram->set(mOrthoMatrix, mModelView, m);
+    }
 
     // Texture
     bindTexture(texture, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, 0);
@@ -948,7 +961,7 @@
 }
 
 void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode,
-        ProgramDescription& description) {
+        ProgramDescription& description, bool swapSrcDst) {
     blend = blend || mode != SkXfermode::kSrcOver_Mode;
     if (blend) {
         if (mode < SkXfermode::kPlus_Mode) {
@@ -956,8 +969,8 @@
                 glEnable(GL_BLEND);
             }
 
-            GLenum sourceMode = gBlends[mode].src;
-            GLenum destMode = gBlends[mode].dst;
+            GLenum sourceMode = swapSrcDst ? gBlendsSwap[mode].src : gBlends[mode].src;
+            GLenum destMode = swapSrcDst ? gBlendsSwap[mode].dst : gBlends[mode].dst;
 
             if (sourceMode != mCaches.lastSrcMode || destMode != mCaches.lastDstMode) {
                 glBlendFunc(sourceMode, destMode);
@@ -970,6 +983,7 @@
             // the blending, turn blending off here
             if (mExtensions.hasFramebufferFetch()) {
                 description.framebufferMode = mode;
+                description.swapSrcDst = swapSrcDst;
             }
 
             if (mCaches.blend) {