Add an API to set the transform on a TextureView's surface texture.
Bug #5156689

Change-Id: I635a625885c9b832a60d44ece0de7613ceb84109
diff --git a/api/current.txt b/api/current.txt
index c70cea8..e64e1424 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -22478,12 +22478,14 @@
     method public android.graphics.Bitmap getBitmap(android.graphics.Bitmap);
     method public android.graphics.SurfaceTexture getSurfaceTexture();
     method public android.view.TextureView.SurfaceTextureListener getSurfaceTextureListener();
+    method public android.graphics.Matrix getTransform(android.graphics.Matrix);
     method public boolean isAvailable();
     method public android.graphics.Canvas lockCanvas();
     method public android.graphics.Canvas lockCanvas(android.graphics.Rect);
     method protected final void onDraw(android.graphics.Canvas);
     method public void setOpaque(boolean);
     method public void setSurfaceTextureListener(android.view.TextureView.SurfaceTextureListener);
+    method public void setTransform(android.graphics.Matrix);
     method public void unlockCanvasAndPost(android.graphics.Canvas);
   }
 
diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java
index e586370..cfbb47c 100644
--- a/core/java/android/view/GLES20Canvas.java
+++ b/core/java/android/view/GLES20Canvas.java
@@ -151,6 +151,7 @@
     static native void nResizeLayer(int layerId, int width, int height, int[] layerInfo);
     static native void nUpdateTextureLayer(int layerId, int width, int height, boolean opaque,
             SurfaceTexture surface);
+    static native void nSetTextureLayerTransform(int layerId, int matrix);
     static native void nDestroyLayer(int layerId);
     static native void nDestroyLayerDeferred(int layerId);
     static native boolean nCopyLayer(int layerId, int bitmap);
diff --git a/core/java/android/view/GLES20RenderLayer.java b/core/java/android/view/GLES20RenderLayer.java
index 41f16e2..23a7166 100644
--- a/core/java/android/view/GLES20RenderLayer.java
+++ b/core/java/android/view/GLES20RenderLayer.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import android.graphics.Canvas;
+import android.graphics.Matrix;
 
 /**
  * An OpenGL ES 2.0 implementation of {@link HardwareLayer}. This
@@ -87,4 +88,11 @@
         }
         return getCanvas();
     }
+
+    /**
+     * Ignored
+     */
+    @Override
+    void setTransform(Matrix matrix) {
+    }
 }
diff --git a/core/java/android/view/GLES20TextureLayer.java b/core/java/android/view/GLES20TextureLayer.java
index 391d9f4..6c41023 100644
--- a/core/java/android/view/GLES20TextureLayer.java
+++ b/core/java/android/view/GLES20TextureLayer.java
@@ -17,6 +17,7 @@
 package android.view;
 
 import android.graphics.Canvas;
+import android.graphics.Matrix;
 import android.graphics.SurfaceTexture;
 
 /**
@@ -75,4 +76,9 @@
         super.update(width, height, isOpaque);
         GLES20Canvas.nUpdateTextureLayer(mLayer, width, height, isOpaque, mSurface);
     }
+
+    @Override
+    void setTransform(Matrix matrix) {
+        GLES20Canvas.nSetTextureLayerTransform(mLayer, matrix.native_instance);
+    }
 }
diff --git a/core/java/android/view/HardwareLayer.java b/core/java/android/view/HardwareLayer.java
index dfb39ae..28389ab 100644
--- a/core/java/android/view/HardwareLayer.java
+++ b/core/java/android/view/HardwareLayer.java
@@ -18,6 +18,7 @@
 
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Matrix;
 
 /**
  * A hardware layer can be used to render graphics operations into a hardware
@@ -150,4 +151,11 @@
         mHeight = height;
         mOpaque = isOpaque;
     }
+
+    /**
+     * Sets an optional transform on this layer.
+     * 
+     * @param matrix The transform to apply to the layer.
+     */
+    abstract void setTransform(Matrix matrix);
 }
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index 53a6bcb..b72222e 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -19,6 +19,7 @@
 import android.content.Context;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.Rect;
 import android.graphics.SurfaceTexture;
@@ -104,6 +105,9 @@
 
     private boolean mOpaque = true;
 
+    private final Matrix mMatrix = new Matrix();
+    private boolean mMatrixChanged;
+
     private final Object[] mLock = new Object[0];
     private boolean mUpdateLayer;
 
@@ -312,6 +316,11 @@
 
         applyUpdate();
 
+        if (mMatrixChanged) {
+            mLayer.setTransform(mMatrix);
+            mMatrixChanged = false;
+        }
+
         return mLayer;
     }
 
@@ -358,6 +367,50 @@
     }
 
     /**
+     * <p>Sets the transform to associate with this texture view.
+     * The specified transform applies to the underlying surface
+     * texture and does not affect the size or position of the view
+     * itself, only of its content.</p>
+     * 
+     * <p>Some transforms might prevent the content from drawing
+     * all the pixels contained within this view's bounds. In such
+     * situations, make sure this texture view is not marked opaque.</p>
+     * 
+     * @param transform The transform to apply to the content of
+     *        this view.
+     * 
+     * @see #getTransform(android.graphics.Matrix) 
+     * @see #isOpaque() 
+     * @see #setOpaque(boolean) 
+     */
+    public void setTransform(Matrix transform) {
+        mMatrix.set(transform);
+        mMatrixChanged = true;
+        invalidate();
+    }
+
+    /**
+     * Returns the transform associated with this texture view.
+     * 
+     * @param transform The {@link Matrix} in which to copy the current
+     *        transform. Can be null.
+     * 
+     * @return The specified matrix if not null or a new {@link Matrix}
+     *         instance otherwise.
+     *         
+     * @see #setTransform(android.graphics.Matrix) 
+     */
+    public Matrix getTransform(Matrix transform) {
+        if (transform == null) {
+            transform = new Matrix();
+        }
+
+        transform.set(mMatrix);
+
+        return transform;
+    }
+
+    /**
      * <p>Returns a {@link android.graphics.Bitmap} representation of the content
      * of the associated surface texture. If the surface texture is not available,
      * this method returns null.</p>
diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp
index 039c5ba..80c79fd 100644
--- a/core/jni/android_view_GLES20Canvas.cpp
+++ b/core/jni/android_view_GLES20Canvas.cpp
@@ -683,6 +683,12 @@
     LayerRenderer::updateTextureLayer(layer, width, height, isOpaque, renderTarget, transform);
 }
 
+static void android_view_GLES20Canvas_setTextureLayerTransform(JNIEnv* env, jobject clazz,
+        Layer* layer, SkMatrix* matrix) {
+
+    layer->getTransform().load(*matrix);
+}
+
 static void android_view_GLES20Canvas_destroyLayer(JNIEnv* env, jobject clazz, Layer* layer) {
     LayerRenderer::destroyLayer(layer);
 }
@@ -827,6 +833,7 @@
     { "nCreateTextureLayer",     "(Z[I)I",     (void*) android_view_GLES20Canvas_createTextureLayer },
     { "nUpdateTextureLayer",     "(IIIZLandroid/graphics/SurfaceTexture;)V",
                                                (void*) android_view_GLES20Canvas_updateTextureLayer },
+    { "nSetTextureLayerTransform", "(II)V",    (void*) android_view_GLES20Canvas_setTextureLayerTransform },
     { "nDestroyLayer",           "(I)V",       (void*) android_view_GLES20Canvas_destroyLayer },
     { "nDestroyLayerDeferred",   "(I)V",       (void*) android_view_GLES20Canvas_destroyLayerDeferred },
     { "nDrawLayer",              "(IIFFI)V",   (void*) android_view_GLES20Canvas_drawLayer },
diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h
index dd75497..a8ae5c6 100644
--- a/libs/hwui/Layer.h
+++ b/libs/hwui/Layer.h
@@ -203,6 +203,10 @@
         return texTransform;
     }
 
+    inline mat4& getTransform() {
+        return transform;
+    }
+
     /**
      * Bounds of the layer.
      */
@@ -282,6 +286,11 @@
      */
     mat4 texTransform;
 
+    /**
+     * Optional transform.
+     */
+    mat4 transform;
+
 }; // struct Layer
 
 }; // namespace uirenderer
diff --git a/libs/hwui/Matrix.cpp b/libs/hwui/Matrix.cpp
index 9fc5131..769c99c 100644
--- a/libs/hwui/Matrix.cpp
+++ b/libs/hwui/Matrix.cpp
@@ -51,6 +51,7 @@
     data[kTranslateZ]   = 0.0f;
     data[kPerspective2] = 1.0f;
 
+    mIsIdentity = true;
     mSimpleMatrix = true;
 }
 
@@ -71,14 +72,21 @@
     return mSimpleMatrix;
 }
 
+bool Matrix4::isIdentity() {
+    return mIsIdentity;
+}
+
 void Matrix4::load(const float* v) {
     memcpy(data, v, sizeof(data));
+    // TODO: Do something smarter here
     mSimpleMatrix = false;
+    mIsIdentity = false;
 }
 
 void Matrix4::load(const Matrix4& v) {
     memcpy(data, v.data, sizeof(data));
     mSimpleMatrix = v.mSimpleMatrix;
+    mIsIdentity = v.mIsIdentity;
 }
 
 void Matrix4::load(const SkMatrix& v) {
@@ -99,6 +107,7 @@
     data[kScaleZ] = 1.0f;
 
     mSimpleMatrix = (v.getType() <= (SkMatrix::kScale_Mask | SkMatrix::kTranslate_Mask));
+    mIsIdentity = v.isIdentity();
 }
 
 void Matrix4::copyTo(SkMatrix& v) const {
@@ -148,6 +157,7 @@
             v.data[kSkewX] * v.data[kSkewY]) * scale;
 
     mSimpleMatrix = v.mSimpleMatrix;
+    mIsIdentity = v.mIsIdentity;
 }
 
 void Matrix4::copyTo(float* v) const {
@@ -166,20 +176,27 @@
     for (int i = 0; i < 16; i++) {
         data[i] *= v;
     }
+    mIsIdentity = false;
 }
 
 void Matrix4::loadTranslate(float x, float y, float z) {
     loadIdentity();
+
     data[kTranslateX] = x;
     data[kTranslateY] = y;
     data[kTranslateZ] = z;
+
+    mIsIdentity = false;
 }
 
 void Matrix4::loadScale(float sx, float sy, float sz) {
     loadIdentity();
+
     data[kScaleX] = sx;
     data[kScaleY] = sy;
     data[kScaleZ] = sz;
+
+    mIsIdentity = false;
 }
 
 void Matrix4::loadSkew(float sx, float sy) {
@@ -198,6 +215,7 @@
     data[kPerspective2] = 1.0f;
 
     mSimpleMatrix = false;
+    mIsIdentity = false;
 }
 
 void Matrix4::loadRotate(float angle, float x, float y, float z) {
@@ -238,6 +256,7 @@
     data[kScaleZ] = z * z * nc +  c;
 
     mSimpleMatrix = false;
+    mIsIdentity = false;
 }
 
 void Matrix4::loadMultiply(const Matrix4& u, const Matrix4& v) {
@@ -262,16 +281,20 @@
     }
 
     mSimpleMatrix = u.mSimpleMatrix && v.mSimpleMatrix;
+    mIsIdentity = false;
 }
 
 void Matrix4::loadOrtho(float left, float right, float bottom, float top, float near, float far) {
     loadIdentity();
+
     data[kScaleX] = 2.0f / (right - left);
     data[kScaleY] = 2.0f / (top - bottom);
     data[kScaleZ] = -2.0f / (far - near);
     data[kTranslateX] = -(right + left) / (right - left);
     data[kTranslateY] = -(top + bottom) / (top - bottom);
     data[kTranslateZ] = -(far + near) / (far - near);
+
+    mIsIdentity = false;
 }
 
 #define MUL_ADD_STORE(a, b, c) a = (a) * (b) + (c)
diff --git a/libs/hwui/Matrix.h b/libs/hwui/Matrix.h
index 2fa6ab7..56fd37d 100644
--- a/libs/hwui/Matrix.h
+++ b/libs/hwui/Matrix.h
@@ -112,6 +112,7 @@
 
     bool isPureTranslate();
     bool isSimple();
+    bool isIdentity();
 
     bool changesBounds();
 
@@ -128,6 +129,7 @@
 
 private:
     bool mSimpleMatrix;
+    bool mIsIdentity;
 
     inline float get(int i, int j) const {
         return data[i * 4 + j];
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 4864cff..a0f806a 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -627,6 +627,12 @@
 void OpenGLRenderer::drawTextureLayer(Layer* layer, const Rect& rect) {
     float alpha = layer->getAlpha() / 255.0f;
 
+    mat4& transform = layer->getTransform();
+    if (!transform.isIdentity()) {
+        save(0);
+        mSnapshot->transform->multiply(transform);
+    }
+
     setupDraw();
     if (layer->getRenderTarget() == GL_TEXTURE_2D) {
         setupDrawWithTexture();
@@ -663,6 +669,10 @@
     glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount);
 
     finishDrawTexture();
+
+    if (!transform.isIdentity()) {
+        restore();
+    }
 }
 
 void OpenGLRenderer::composeLayerRect(Layer* layer, const Rect& rect, bool swap) {
diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java
index 97e2108..fcb57d9 100644
--- a/tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java
+++ b/tests/HwAccelerationTest/src/com/android/test/hwui/TextureViewActivity.java
@@ -17,6 +17,7 @@
 package com.android.test.hwui;
 
 import android.app.Activity;
+import android.graphics.Matrix;
 import android.graphics.SurfaceTexture;
 import android.hardware.Camera;
 import android.os.Bundle;
@@ -33,6 +34,7 @@
     private Camera mCamera;
     private TextureView mTextureView;
     private FrameLayout mContent;
+    private Matrix mMatrix = new Matrix();
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -82,8 +84,6 @@
         }
 
         mCamera.startPreview();
-
-        mTextureView.setCameraDistance(5000);
     }
 
     @Override