Merge "Don't use full screen FBOs, this dramatically increase performance."
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index ccf1221..b460910 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -368,19 +368,19 @@
 
     @Override
     public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) {
-        final float width = bitmap.getWidth();
-        final float height = bitmap.getHeight();
         final int nativePaint = paint == null ? 0 : paint.mNativePaint;
-        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, 0.0f, 0.0f, width, height,
-                0.0f, 0.0f, width, height, matrix.native_instance, nativePaint,
+        nDrawBitmap(mRenderer, bitmap.mNativeBitmap, matrix.native_instance, nativePaint,
                 bitmap.getDensity(), mDensity, mScreenDensity);
     }
 
+    private native void nDrawBitmap(int renderer, int bitmap, int matrix, int paint,
+            int bitmapDensity, int canvasDensity, int screenDensity);
+
     @Override
     public void drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint) {
         final int nativePaint = paint == null ? 0 : paint.mNativePaint;
         nDrawBitmap(mRenderer, bitmap.mNativeBitmap, src.left, src.top, src.right, src.bottom,
-                dst.left, dst.top, dst.right, dst.bottom, 0, nativePaint,
+                dst.left, dst.top, dst.right, dst.bottom, nativePaint,
                 bitmap.getDensity(), mDensity, mScreenDensity);
     }
 
@@ -388,7 +388,7 @@
     public void drawBitmap(Bitmap bitmap, Rect src, RectF dst, Paint paint) {
         final int nativePaint = paint == null ? 0 : paint.mNativePaint;
         nDrawBitmap(mRenderer, bitmap.mNativeBitmap, src.left, src.top, src.right, src.bottom,
-                dst.left, dst.top, dst.right, dst.bottom, 0, nativePaint,
+                dst.left, dst.top, dst.right, dst.bottom, nativePaint,
                 bitmap.getDensity(), mDensity, mScreenDensity);
     }
 
@@ -397,7 +397,7 @@
 
     private native void nDrawBitmap(int renderer, int bitmap,
             float srcLeft, float srcTop, float srcRight, float srcBottom,
-            float left, float top, float right, float bottom, int matrix, int paint,
+            float left, float top, float right, float bottom, int paint,
             int bitmapDensity, int canvasDensity, int screenDensity);
 
     @Override
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index a1ee3d3..d30bce9 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -413,33 +413,30 @@
             }
 
             EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
-                int[] num_config = new int[1];
-                if (!egl.eglChooseConfig(display, mConfigSpec, null, 0,
-                        num_config)) {
+                int[] index = new int[1];
+                if (!egl.eglChooseConfig(display, mConfigSpec, null, 0, index)) {
                     throw new IllegalArgumentException("eglChooseConfig failed");
                 }
 
-                int numConfigs = num_config[0];
-
+                int numConfigs = index[0];
                 if (numConfigs <= 0) {
-                    throw new IllegalArgumentException(
-                            "No configs match configSpec");
+                    throw new IllegalArgumentException("No configs match configSpec");
                 }
 
                 EGLConfig[] configs = new EGLConfig[numConfigs];
-                if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs,
-                        num_config)) {
-                    throw new IllegalArgumentException("eglChooseConfig#2 failed");
+                if (!egl.eglChooseConfig(display, mConfigSpec, configs, numConfigs, index)) {
+                    throw new IllegalArgumentException("eglChooseConfig failed");
                 }
+
                 EGLConfig config = chooseConfig(egl, display, configs);
                 if (config == null) {
                     throw new IllegalArgumentException("No config chosen");
                 }
+
                 return config;
             }
 
-            abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display,
-                    EGLConfig[] configs);
+            abstract EGLConfig chooseConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs);
 
             private int[] filterConfigSpec(int[] configSpec) {
                 if (mGlVersion != 2) {
@@ -450,10 +447,10 @@
                  */
                 int len = configSpec.length;
                 int[] newConfigSpec = new int[len + 2];
-                System.arraycopy(configSpec, 0, newConfigSpec, 0, len-1);
-                newConfigSpec[len-1] = EGL10.EGL_RENDERABLE_TYPE;
+                System.arraycopy(configSpec, 0, newConfigSpec, 0, len - 1);
+                newConfigSpec[len - 1] = EGL10.EGL_RENDERABLE_TYPE;
                 newConfigSpec[len] = 4; /* EGL_OPENGL_ES2_BIT */
-                newConfigSpec[len+1] = EGL10.EGL_NONE;
+                newConfigSpec[len + 1] = EGL10.EGL_NONE;
                 return newConfigSpec;
             }
         }
@@ -496,13 +493,12 @@
                 for (EGLConfig config : configs) {
                     int d = findConfigAttrib(egl, display, config, EGL10.EGL_DEPTH_SIZE, 0);
                     int s = findConfigAttrib(egl, display, config, EGL10.EGL_STENCIL_SIZE, 0);
-                    if ((d >= mDepthSize) && (s >= mStencilSize)) {
+                    if (d >= mDepthSize && s >= mStencilSize) {
                         int r = findConfigAttrib(egl, display, config, EGL10.EGL_RED_SIZE, 0);
                         int g = findConfigAttrib(egl, display, config, EGL10.EGL_GREEN_SIZE, 0);
                         int b = findConfigAttrib(egl, display, config, EGL10.EGL_BLUE_SIZE, 0);
                         int a = findConfigAttrib(egl, display, config, EGL10.EGL_ALPHA_SIZE, 0);
-                        if ((r == mRedSize) && (g == mGreenSize) && (b == mBlueSize) &&
-                                (a == mAlphaSize)) {
+                        if (r == mRedSize && g == mGreenSize && b == mBlueSize && a >= mAlphaSize) {
                             return config;
                         }
                     }
@@ -510,16 +506,15 @@
                 return null;
             }
 
-            private int findConfigAttrib(EGL10 egl, EGLDisplay display,
-                    EGLConfig config, int attribute, int defaultValue) {
-
+            private int findConfigAttrib(EGL10 egl, EGLDisplay display, EGLConfig config,
+                    int attribute, int defaultValue) {
                 if (egl.eglGetConfigAttrib(display, config, attribute, mValue)) {
                     return mValue[0];
                 }
 
                 return defaultValue;
             }
-        }        
+        }
     }
     
     /**
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index cba6ed1..89ad534 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -195,13 +195,22 @@
         OpenGLRenderer* renderer, SkBitmap* bitmap,
         float srcLeft, float srcTop, float srcRight, float srcBottom,
         float dstLeft, float dstTop, float dstRight, float dstBottom,
-        SkMatrix* matrix, SkPaint* paint,
-        jint bitmapDensity, jint canvasDensity, jint screenDensity) {
+        SkPaint* paint, jint bitmapDensity, jint canvasDensity, jint screenDensity) {
     if (canvasDensity == bitmapDensity || canvasDensity == 0 || bitmapDensity == 0) {
         renderer->drawBitmap(bitmap, srcLeft, srcTop, srcRight, srcBottom,
-                dstLeft, dstTop, dstRight, dstBottom, matrix, paint);
+                dstLeft, dstTop, dstRight, dstBottom, paint);
     } else {
+        // TODO: implement
+    }
+}
 
+static void android_view_GLES20Canvas_drawBitmapMatrix(JNIEnv* env, jobject canvas,
+        OpenGLRenderer* renderer, SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint,
+        jint bitmapDensity, jint canvasDensity,jint screenDensity) {
+    if (canvasDensity == bitmapDensity || canvasDensity == 0 || bitmapDensity == 0) {
+        renderer->drawBitmap(bitmap, matrix, paint);
+    } else {
+        // TODO: implement
     }
 }
 
@@ -249,7 +258,8 @@
     {   "nConcatMatrix",      "(II)V",              (void*) android_view_GLES20Canvas_concatMatrix },
 
     {   "nDrawBitmap",        "(IIFFIIII)V",        (void*) android_view_GLES20Canvas_drawBitmap },
-    {   "nDrawBitmap",        "(IIFFFFFFFFIIIII)V", (void*) android_view_GLES20Canvas_drawBitmapRect },
+    {   "nDrawBitmap",        "(IIFFFFFFFFIIII)V",  (void*) android_view_GLES20Canvas_drawBitmapRect },
+    {   "nDrawBitmap",        "(IIIIIII)V",         (void*) android_view_GLES20Canvas_drawBitmapMatrix },
     {   "nDrawColor",         "(III)V",             (void*) android_view_GLES20Canvas_drawColor },
     {   "nDrawRect",          "(IFFFFI)V",          (void*) android_view_GLES20Canvas_drawRect },
 
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 091abb0..8b4fb9b 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -111,6 +111,7 @@
 
     mWidth = width;
     mHeight = height;
+    mFirstSnapshot.height = height;
 }
 
 void OpenGLRenderer::prepare() {
@@ -170,10 +171,15 @@
 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) {
+        memcpy(mOrthoMatrix, current->orthoMatrix, sizeof(mOrthoMatrix));
+    }
+
     if (restoreLayer) {
         composeLayer(current, previous);
     }
@@ -197,21 +203,11 @@
     // The texture is currently as big as the window but drawn with
     // a quad of the appropriate size
     const Rect& layer = current->layer;
-    Rect texCoords(current->layer);
-    mSnapshot->transform.mapRect(texCoords);
-
-    const float u1 = texCoords.left / float(mWidth);
-    const float v1 = (mHeight - texCoords.top) / float(mHeight);
-    const float u2 = texCoords.right / float(mWidth);
-    const float v2 = (mHeight - texCoords.bottom) / float(mHeight);
-
-    resetDrawTextureTexCoords(u1, v1, u2, v2);
 
     drawTextureRect(layer.left, layer.top, layer.right, layer.bottom,
             current->texture, current->alpha, current->mode, true, true);
 
-    resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f);
-
+    // TODO Don't delete these things, but cache them
     glDeleteFramebuffers(1, &current->fbo);
     glDeleteTextures(1, &current->texture);
 }
@@ -268,14 +264,10 @@
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 
     // TODO VERY IMPORTANT: Fix TextView to not call saveLayer() all the time
-    // TODO ***** IMPORTANT *****
-    // Creating a texture-backed FBO works only if the texture is the same size
-    // as the original rendering buffer (in this case, mWidth and mHeight.)
-    // This is expensive and wasteful and must be fixed.
-    // TODO Additionally we should use an FBO cache
+    // TODO Use an FBO cache
 
-    const GLsizei width = mWidth; //right - left;
-    const GLsizei height = mHeight; //bottom - right;
+    const GLsizei width = right - left;
+    const GLsizei height = bottom - top;
 
     const GLint format = (flags & SkCanvas::kHasAlphaLayer_SaveFlag) ? GL_RGBA : GL_RGB;
     glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, NULL);
@@ -287,7 +279,7 @@
 
     GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
     if (status != GL_FRAMEBUFFER_COMPLETE) {
-        LOGD("Framebuffer incomplete %d", status);
+        LOGD("Framebuffer incomplete (GL error code 0x%x)", status);
 
         glDeleteFramebuffers(1, &snapshot->fbo);
         glDeleteTextures(1, &snapshot->texture);
@@ -295,11 +287,34 @@
         return false;
     }
 
+    // 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);
+
     snapshot->flags |= Snapshot::kFlagIsLayer;
     snapshot->mode = mode;
     snapshot->alpha = alpha / 255.0f;
     snapshot->layer.set(left, top, right, bottom);
 
+    // Creates a new snapshot to draw into the FBO
+    saveSnapshot();
+    // TODO: This doesn't preserve other transformations (check Skia first)
+    mSnapshot->transform.loadTranslate(-left, -top, 0.0f);
+    mSnapshot->clipRect.set(left, top, right, bottom);
+    mSnapshot->height = bottom - top;
+    setScissorFromClip();
+
+    mSnapshot->flags = Snapshot::kFlagDirtyTransform | Snapshot::kFlagDirtyOrtho |
+            Snapshot::kFlagClipSet;
+    memcpy(mSnapshot->orthoMatrix, mOrthoMatrix, sizeof(mOrthoMatrix));
+
+    // Change the ortho projection
+    mat4 ortho;
+    ortho.loadOrtho(0.0f, right - left, bottom - top, 0.0f, 0.0f, 1.0f);
+    ortho.copyTo(mOrthoMatrix);
+
     return true;
 }
 
@@ -343,7 +358,7 @@
 
 void OpenGLRenderer::setScissorFromClip() {
     const Rect& clip = mSnapshot->getMappedClip();
-    glScissor(clip.left, mHeight - clip.bottom, clip.getWidth(), clip.getHeight());
+    glScissor(clip.left, mSnapshot->height - clip.bottom, clip.getWidth(), clip.getHeight());
 }
 
 const Rect& OpenGLRenderer::getClipBounds() {
@@ -381,7 +396,7 @@
 ///////////////////////////////////////////////////////////////////////////////
 
 void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, const SkPaint* paint) {
-    Texture* texture = mTextureCache.get(bitmap);
+    const Texture* texture = mTextureCache.get(bitmap);
 
     int alpha;
     SkXfermode::Mode mode;
@@ -391,11 +406,26 @@
             alpha / 255.0f, mode, texture->blend, true);
 }
 
+void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, const SkMatrix* matrix, const SkPaint* paint) {
+    Rect r(0.0f, 0.0f, bitmap->width(), bitmap->height());
+    const mat4 transform(*matrix);
+    transform.mapRect(r);
+
+    const Texture* texture = mTextureCache.get(bitmap);
+
+    int alpha;
+    SkXfermode::Mode mode;
+    getAlphaAndMode(paint, &alpha, &mode);
+
+    drawTextureRect(r.left, r.top, r.right, r.bottom, texture->id,
+            alpha / 255.0f, mode, texture->blend, true);
+}
+
 void OpenGLRenderer::drawBitmap(SkBitmap* bitmap,
          float srcLeft, float srcTop, float srcRight, float srcBottom,
          float dstLeft, float dstTop, float dstRight, float dstBottom,
-         const SkMatrix* matrix, const SkPaint* paint) {
-    Texture* texture = mTextureCache.get(bitmap);
+         const SkPaint* paint) {
+    const Texture* texture = mTextureCache.get(bitmap);
 
     int alpha;
     SkXfermode::Mode mode;
@@ -411,8 +441,6 @@
 
     resetDrawTextureTexCoords(u1, v1, u2, v2);
 
-    // TODO: implement Matrix
-
     drawTextureRect(dstLeft, dstTop, dstRight, dstBottom, texture->id,
             alpha / 255.0f, mode, texture->blend, true);
 
diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h
index a698e79..bd5f84f 100644
--- a/libs/hwui/OpenGLRenderer.h
+++ b/libs/hwui/OpenGLRenderer.h
@@ -103,9 +103,9 @@
     bool clipRect(float left, float top, float right, float bottom);
 
     void drawBitmap(SkBitmap* bitmap, float left, float top, const SkPaint* paint);
+    void drawBitmap(SkBitmap* bitmap, const SkMatrix* matrix, const SkPaint* paint);
     void drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom,
-            float dstLeft, float dstTop, float dstRight, float dstBottom,
-            const SkMatrix* matrix, const SkPaint* paint);
+            float dstLeft, float dstTop, float dstRight, float dstBottom, const SkPaint* paint);
     void drawColor(int color, SkXfermode::Mode mode);
     void drawRect(float left, float top, float right, float bottom, const SkPaint* paint);
 
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index ca91b34..d1809f3 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -51,6 +51,7 @@
      * snapshot.
      */
     Snapshot(const sp<Snapshot> s):
+            height(s->height),
             transform(s->transform),
             clipRect(s->clipRect),
             flags(kFlagDirtyTransform),
@@ -80,6 +81,10 @@
          * a new layer.
          */
         kFlagIsLayer = 0x4,
+        /**
+         * Indicates that this snapshot has changed the ortho matrix.
+         */
+        kFlagDirtyOrtho = 0x8,
     };
 
     /**
@@ -95,6 +100,11 @@
     }
 
     /**
+     * Height of the framebuffer the snapshot is rendering into.
+     */
+    int height;
+
+    /**
      * Local transformation. Holds the current translation, scale and
      * rotation values.
      */
@@ -141,6 +151,11 @@
      */
     SkXfermode::Mode mode;
 
+    /**
+     * Contains the previous ortho matrix.
+     */
+    float orthoMatrix[16];
+
 private:
     // Clipping rectangle mapped with the transform
     Rect mappedClip;
diff --git a/tests/HwAccelerationTest/res/drawable/sunset1.jpg b/tests/HwAccelerationTest/res/drawable/sunset1.jpg
new file mode 100644
index 0000000..92851f3
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable/sunset1.jpg
Binary files differ
diff --git a/tests/HwAccelerationTest/res/drawable/sunset2.png b/tests/HwAccelerationTest/res/drawable/sunset2.png
new file mode 100644
index 0000000..3258ee7
--- /dev/null
+++ b/tests/HwAccelerationTest/res/drawable/sunset2.png
Binary files differ