Allow user to specify a custom matrix to transform cards.

Change-Id: I28901c4bdee160c06deae1167489510122d45e98
diff --git a/carousel/java/com/android/ex/carousel/CarouselController.java b/carousel/java/com/android/ex/carousel/CarouselController.java
index 6a3f3a1..a2f1e08 100644
--- a/carousel/java/com/android/ex/carousel/CarouselController.java
+++ b/carousel/java/com/android/ex/carousel/CarouselController.java
@@ -61,6 +61,7 @@
             new int[] {0x00000000, 0xffffffff, 0x00000000}, 0, 3, 3, 1, Bitmap.Config.ARGB_4444);
     private Mesh mDefaultGeometry;
     private Mesh mLoadingGeometry;
+    private float[] mDefaultCardMatrix;
     private int mCardCount = 0;
     private int mVisibleSlots = 0;
     private int mVisibleDetails = DEFAULT_VISIBLE_DETAIL_COUNT;
@@ -115,6 +116,7 @@
         setDefaultBitmap(mDefaultBitmap);
         setLoadingBitmap(mLoadingBitmap);
         setDefaultGeometry(mDefaultGeometry);
+        setDefaultCardMatrix(mDefaultCardMatrix);
         setLoadingGeometry(mLoadingGeometry);
         setBackgroundColor(mBackgroundColor.x, mBackgroundColor.y, mBackgroundColor.z,
                 mBackgroundColor.w);
@@ -465,6 +467,21 @@
     }
 
     /**
+     * Sets the matrix used to transform card geometries.  By default, this
+     * is the identity matrix, but you can specify a different matrix if you
+     * want to scale, translate and / or rotate the card before drawing.
+     *
+     * @param matrix array of 9 or 16 floats representing a 3x3 or 4x4 matrix,
+     * or null as a shortcut for an identity matrix.
+     */
+    public void setDefaultCardMatrix(float[] matrix) {
+        mDefaultCardMatrix = matrix;
+        if (mRenderScript != null) {
+           mRenderScript.setDefaultCardMatrix(matrix);
+        }
+    }
+
+    /**
      * This is an intermediate version of the object to show while geometry is loading. If not set,
      * a quad will be drawn in its place.  It is shared for all cards. If something other than
      * simple planar geometry is used, consider enabling depth test with
diff --git a/carousel/java/com/android/ex/carousel/CarouselRS.java b/carousel/java/com/android/ex/carousel/CarouselRS.java
index 5f99250..a0cffdb 100644
--- a/carousel/java/com/android/ex/carousel/CarouselRS.java
+++ b/carousel/java/com/android/ex/carousel/CarouselRS.java
@@ -300,6 +300,27 @@
         mScript.set_fillDirection(direction);
     }
 
+    public void setDefaultCardMatrix(float[] matrix) {
+        int dimensions;
+        if (matrix == null || matrix.length == 0) {
+          dimensions = 0;
+        } else if (matrix.length == 16) {
+          dimensions = 4;
+        } else if (matrix.length == 9) {
+          dimensions = 3;
+        } else {
+          throw new IllegalArgumentException("matrix length not 0,9 or 16");
+        }
+
+        Matrix4f rsMatrix = new Matrix4f();  // initialized as identity.
+        for (int i = 0; i < dimensions; i++) {
+            for (int j = 0; j < dimensions; j++) {
+                rsMatrix.set(i, j, matrix[i*dimensions + j]);
+            }
+        }
+        mScript.set_defaultCardMatrix(rsMatrix);
+    }
+
     private void initVertexProgram() {
         ProgramVertex.Builder pvb = new ProgramVertex.Builder(mRS, null, null);
         mVertexProgram = pvb.create();
diff --git a/carousel/java/com/android/ex/carousel/CarouselView.java b/carousel/java/com/android/ex/carousel/CarouselView.java
index 623fdfc..5bc6896 100644
--- a/carousel/java/com/android/ex/carousel/CarouselView.java
+++ b/carousel/java/com/android/ex/carousel/CarouselView.java
@@ -415,6 +415,18 @@
     }
 
     /**
+     * Sets the matrix used to transform card geometries.  By default, this
+     * is the identity matrix, but you can specify a different matrix if you
+     * want to scale, translate and / or rotate the card before drawing.
+     *
+     * @param matrix array of 9 or 16 floats representing a 3x3 or 4x4 matrix,
+     * or null as a shortcut for an identity matrix.
+     */
+    public void setDefaultCardMatrix(float[] matrix) {
+        mController.setDefaultCardMatrix(matrix);
+    }
+
+    /**
      * This is an intermediate version of the object to show while geometry is loading. If not set,
      * a quad will be drawn in its place.  It is shared for all cards. If something other than
      * simple planar geometry is used, consider enabling depth test with
diff --git a/carousel/java/com/android/ex/carousel/carousel.rs b/carousel/java/com/android/ex/carousel/carousel.rs
index 44edd69..c2a74b2 100644
--- a/carousel/java/com/android/ex/carousel/carousel.rs
+++ b/carousel/java/com/android/ex/carousel/carousel.rs
@@ -189,6 +189,7 @@
 rs_allocation detailLoadingTexture; // used when detail texture is loading
 rs_mesh defaultGeometry; // shown when no geometry is loaded
 rs_mesh loadingGeometry; // shown when geometry is loading
+rs_matrix4x4 defaultCardMatrix;
 rs_matrix4x4 projectionMatrix;
 rs_matrix4x4 modelviewMatrix;
 FragmentShaderConstants* shaderConstants;
@@ -277,6 +278,7 @@
     fadeInDuration = 250;
     rezInCardCount = 0.0f; // alpha will ramp to 1.0f over this many cards (0.0f means disabled)
     detailFadeRate = 0.5f; // fade details over this many slot positions.
+    rsMatrixLoadIdentity(&defaultCardMatrix);
 }
 
 static void updateAllocationVars(Card_t* newcards)
@@ -298,7 +300,7 @@
     static const float2 zero = {0.0f, 0.0f};
     card->detailTextureOffset = zero;
     card->detailLineOffset = zero;
-    rsMatrixLoadIdentity(&card->matrix);
+    rsMatrixLoad(&card->matrix, &defaultCardMatrix);
     card->textureState = STATE_INVALID;
     card->detailTextureState = STATE_INVALID;
     card->geometryState = STATE_INVALID;
@@ -559,7 +561,7 @@
         stillAnimating = getAnimatedScaleForSelected(&scale);
         rsMatrixScale(matrix, scale.x, scale.y, scale.z);
     }
-    // TODO: apply custom matrix for cards[i].geometry
+    rsMatrixLoadMultiply(matrix, &cards[i].matrix, matrix);
     return stillAnimating;
 }