Make Panorama work in portrait layout.

Add the support of both portrait and landscape layout to the panorama library.
Add a step into the preview renderer that rotates the content in the
offscreen buffer by 90 degrees in the case of a portrait layout.

Change-Id: I879e3476daac522b0c8b27fe3ef5b17ebf0797e3
diff --git a/jni/mosaic_renderer_jni.cpp b/jni/mosaic_renderer_jni.cpp
index fbf6862..bb01e7f 100644
--- a/jni/mosaic_renderer_jni.cpp
+++ b/jni/mosaic_renderer_jni.cpp
@@ -118,6 +118,10 @@
 double gUILayoutScalingX = 1.0f;
 double gUILayoutScalingY = 1.0f;
 
+// Whether the view that we will render preview FBO onto is in landscape or portrait
+// orientation.
+bool gIsLandscapeOrientation = true;
+
 // State of the viewfinder. Set to false when the viewfinder hits the UI edge.
 bool gPanViewfinder = true;
 
@@ -137,12 +141,26 @@
 double g_dTranslationToFBOCenter[16];
 
 // GL 4x4 Identity transformation
-GLfloat g_dAffinetransIdent[] = {
+GLfloat g_dAffinetransIdentGL[] = {
     1., 0., 0., 0.,
     0., 1., 0., 0.,
     0., 0., 1., 0.,
     0., 0., 0., 1.};
 
+// GL 4x4 Rotation transformation (column-majored): 90 degree
+GLfloat g_dAffinetransRotation90GL[] = {
+    0., 1., 0., 0.,
+    -1., 0., 0., 0.,
+    0., 0., 1., 0.,
+    0., 0., 0., 1.};
+
+// 3x3 Rotation transformation (row-majored): 90 degree
+double gRotation90[] = {
+    0., -1., 0.,
+    1., 0., 0.,
+    0., 0., 1.,};
+
+
 float g_dIdent3x3[] = {
     1.0, 0.0, 0.0,
     0.0, 1.0, 0.0,
@@ -219,6 +237,39 @@
     matGL44[15] = mat33[8];
 }
 
+bool continuePanningFBO(double panOffset) {
+    double normalizedScreenLimitLeft = -1.0 + VIEWPORT_BORDER_FACTOR_HORZ * 2.0;
+    double normalizedScreenLimitRight = 1.0 - VIEWPORT_BORDER_FACTOR_HORZ * 2.0;
+    double normalizedXPositionOnScreenLeft;
+    double normalizedXPositionOnScreenRight;
+
+    // Compute the position of the current frame in the screen coordinate system
+    if (gIsLandscapeOrientation) {
+        normalizedXPositionOnScreenLeft = (2.0 *
+            (gCenterOffsetX + panOffset) / gPreviewFBOWidth - 1.0) *
+            gUILayoutScalingX;
+        normalizedXPositionOnScreenRight = (2.0 *
+            ((gCenterOffsetX + panOffset) + gPreviewImageWidth[HR]) /
+            gPreviewFBOWidth - 1.0) * gUILayoutScalingX;
+    } else {
+        normalizedXPositionOnScreenLeft = (2.0 *
+            (gCenterOffsetX + panOffset) / gPreviewFBOWidth - 1.0) *
+            gUILayoutScalingY;
+        normalizedXPositionOnScreenRight = (2.0 *
+            ((gCenterOffsetX + panOffset) + gPreviewImageWidth[HR]) /
+            gPreviewFBOWidth - 1.0) * gUILayoutScalingY;
+    }
+
+    // Stop the viewfinder panning if we hit the maximum border allowed for
+    // this UI layout
+    if (normalizedXPositionOnScreenRight > normalizedScreenLimitRight ||
+            normalizedXPositionOnScreenLeft < normalizedScreenLimitLeft) {
+        return false;
+    } else {
+        return true;
+    }
+}
+
 // This function computes fills the 4x4 matrices g_dAffinetrans,
 // and g_dAffinetransPan using the specified 3x3 affine
 // transformation between the first captured frame and the current frame.
@@ -289,23 +340,7 @@
     }
 
     gLastTx = gThisTx;
-
-    // Compute the position of the current frame in the screen coordinate system
-    // and stop the viewfinder panning if we hit the maximum border allowed for
-    // this UI layout
-    double normalizedXPositionOnScreenLeft = (2.0 *
-            (gCenterOffsetX + gPanOffset) / gPreviewFBOWidth - 1.0) *
-            gUILayoutScalingX;
-    double normalizedScreenLimitLeft = -1.0 + VIEWPORT_BORDER_FACTOR_HORZ * 2.0;
-
-    double normalizedXPositionOnScreenRight = (2.0 *
-            ((gCenterOffsetX + gPanOffset) + gPreviewImageWidth[HR]) /
-            gPreviewFBOWidth - 1.0) * gUILayoutScalingX;
-    double normalizedScreenLimitRight = 1.0 - VIEWPORT_BORDER_FACTOR_HORZ * 2.0;
-
-    if(normalizedXPositionOnScreenRight > normalizedScreenLimitRight ||
-            normalizedXPositionOnScreenLeft < normalizedScreenLimitLeft)
-        gPanViewfinder = false;
+    gPanViewfinder = continuePanningFBO(gPanOffset);
 
     db_Identity3x3(H);
     H[2] = gPanOffset;
@@ -315,7 +350,13 @@
     db_Multiply3x3_3x3(Htemp1, H, gKm);
     db_Multiply3x3_3x3(Hp, gKminv, Htemp1);
 
-    ConvertAffine3x3toGL4x4(g_dAffinetransPan, Hp);
+    if (gIsLandscapeOrientation) {
+        ConvertAffine3x3toGL4x4(g_dAffinetransPan, Hp);
+    } else {
+        // rotate Hp by 90 degress.
+        db_Multiply3x3_3x3(Htemp1, gRotation90, Hp);
+        ConvertAffine3x3toGL4x4(g_dAffinetransPan, Htemp1);
+    }
 }
 
 void AllocateTextureMemory(int widthHR, int heightHR, int widthLR, int heightLR)
@@ -421,7 +462,8 @@
     JNIEXPORT jint JNICALL Java_com_android_camera_panorama_MosaicRenderer_init(
             JNIEnv * env, jobject obj);
     JNIEXPORT void JNICALL Java_com_android_camera_panorama_MosaicRenderer_reset(
-            JNIEnv * env, jobject obj,  jint width, jint height);
+            JNIEnv * env, jobject obj,  jint width, jint height,
+            jboolean isLandscapeOrientation);
     JNIEXPORT void JNICALL Java_com_android_camera_panorama_MosaicRenderer_preprocess(
             JNIEnv * env, jobject obj, jfloatArray stMatrix);
     JNIEXPORT void JNICALL Java_com_android_camera_panorama_MosaicRenderer_transferGPUtoCPU(
@@ -461,19 +503,48 @@
 }
 
 
-JNIEXPORT void JNICALL Java_com_android_camera_panorama_MosaicRenderer_reset(
-        JNIEnv * env, jobject obj,  jint width, jint height)
-{
-    // Scale the current frame's height to the height of view and
-    // maintain the aspect ratio of the current frame on the screen.
-    gUILayoutScalingY = PREVIEW_FBO_HEIGHT_SCALE;
+void calculateUILayoutScaling(int width, int height, bool isLandscape) {
+    if (isLandscape) {
+        //  __________        ______
+        // |__________|  =>  |______|
+        // (Preview FBO)      (View)
+        //
+        // Scale the preview FBO's height to the height of view and
+        // maintain the aspect ratio of the current frame on the screen.
+        gUILayoutScalingY = PREVIEW_FBO_HEIGHT_SCALE;
 
-    // Note that OpenGL scales a texture to view's width and height automatically.
-    // The "width / height" inverts the scaling, so as to maintain the aspect ratio
-    // of the current frame.
-    gUILayoutScalingX = ((float) (PREVIEW_FBO_WIDTH_SCALE * gPreviewImageWidth[LR])
-            / (PREVIEW_FBO_HEIGHT_SCALE * gPreviewImageHeight[LR]) *  PREVIEW_FBO_HEIGHT_SCALE)
-            / ((float) width / height);
+        // Note that OpenGL scales a texture to view's width and height automatically.
+        // The "width / height" inverts the scaling, so as to maintain the aspect ratio
+        // of the current frame.
+        gUILayoutScalingX = ((float) (PREVIEW_FBO_WIDTH_SCALE * gPreviewImageWidth[LR])
+                / (PREVIEW_FBO_HEIGHT_SCALE * gPreviewImageHeight[LR]) *  PREVIEW_FBO_HEIGHT_SCALE)
+                / ((float) width / height);
+    } else {
+        //                     __
+        //  __________        |  |
+        // |__________|  =>   |  |
+        // (Preview FBO)      |  |
+        //                    |__|
+        //                   (View)
+        // Scale the preview FBO's height to the width of view and
+        // maintain the aspect ratio of the current frame on the screen.
+        gUILayoutScalingX = PREVIEW_FBO_HEIGHT_SCALE;
+
+        // Note that OpenGL scales a texture to view's width and height automatically.
+        // The "height / width" inverts the scaling, so as to maintain the aspect ratio
+        // of the current frame.
+        gUILayoutScalingY = ((float) (PREVIEW_FBO_WIDTH_SCALE * gPreviewImageWidth[LR])
+                / (PREVIEW_FBO_HEIGHT_SCALE * gPreviewImageHeight[LR]) *  PREVIEW_FBO_HEIGHT_SCALE)
+                / ((float) height / width);
+
+    }
+}
+
+JNIEXPORT void JNICALL Java_com_android_camera_panorama_MosaicRenderer_reset(
+        JNIEnv * env, jobject obj,  jint width, jint height, jboolean isLandscapeOrientation)
+{
+    gIsLandscapeOrientation = isLandscapeOrientation;
+    calculateUILayoutScaling(width, height, gIsLandscapeOrientation);
 
     gBuffer[0].Init(gPreviewFBOWidth, gPreviewFBOHeight, GL_RGBA);
     gBuffer[1].Init(gPreviewFBOWidth, gPreviewFBOHeight, GL_RGBA);
@@ -543,6 +614,7 @@
     gPreview.SetupGraphics(width, height);
     gPreview.Clear(0.0, 0.0, 0.0, 1.0);
     gPreview.SetViewportMatrix(1, 1, 1, 1);
+
     // Scale the previewFBO so that the viewfinder window fills the layout height
     // while maintaining the image aspect ratio
     gPreview.SetScalingMatrix(gUILayoutScalingX, -1.0f * gUILayoutScalingY);
@@ -560,8 +632,8 @@
 
     env->ReleaseFloatArrayElements(stMatrix, stmat, 0);
 
-    gSurfTexRenderer[LR].DrawTexture(g_dAffinetransIdent);
-    gSurfTexRenderer[HR].DrawTexture(g_dAffinetransIdent);
+    gSurfTexRenderer[LR].DrawTexture(g_dAffinetransIdentGL);
+    gSurfTexRenderer[HR].DrawTexture(g_dAffinetransIdentGL);
 }
 
 #ifndef now_ms
@@ -626,7 +698,12 @@
         gPreview.SetInputTextureName(gBuffer[gCurrentFBOIndex].GetTextureName());
 
         gWarper2.DrawTexture(g_dTranslationToFBOCenterGL);
-        gPreview.DrawTexture(g_dAffinetransIdent);
+
+        if (gIsLandscapeOrientation) {
+            gPreview.DrawTexture(g_dAffinetransIdentGL);
+        } else {
+            gPreview.DrawTexture(g_dAffinetransRotation90GL);
+        }
     }
     else
     {
diff --git a/src/com/android/camera/panorama/MosaicRenderer.java b/src/com/android/camera/panorama/MosaicRenderer.java
index 1ff307d..f055c0e 100644
--- a/src/com/android/camera/panorama/MosaicRenderer.java
+++ b/src/com/android/camera/panorama/MosaicRenderer.java
@@ -43,8 +43,9 @@
       *
       * @param width width of the drawing surface in pixels.
       * @param height height of the drawing surface in pixels.
+      * @param isLandscapeOrientation is the orientation of the activity layout in landscape.
       */
-     public static native void reset(int width, int height);
+     public static native void reset(int width, int height, boolean isLandscapeOrientation);
 
      /**
       * Calling this function will render the SurfaceTexture to a new 2D texture
diff --git a/src/com/android/camera/panorama/MosaicRendererSurfaceView.java b/src/com/android/camera/panorama/MosaicRendererSurfaceView.java
index 08d4eff..b2acfde 100644
--- a/src/com/android/camera/panorama/MosaicRendererSurfaceView.java
+++ b/src/com/android/camera/panorama/MosaicRendererSurfaceView.java
@@ -16,7 +16,9 @@
 
 package com.android.camera.panorama;
 
+import android.app.Activity;
 import android.content.Context;
+import android.content.pm.ActivityInfo;
 import android.graphics.PixelFormat;
 import android.opengl.GLSurfaceView;
 import android.os.ConditionVariable;
@@ -33,23 +35,34 @@
     private static final boolean DEBUG = false;
     private MosaicRendererSurfaceViewRenderer mRenderer;
     private ConditionVariable mPreviewFrameReadyForProcessing;
+    private boolean mIsLandscapeOrientation = true;
 
     public MosaicRendererSurfaceView(Context context) {
         super(context);
-        init(false, 0, 0);
-        setZOrderMediaOverlay(true);
+        initialize(context, false, 0, 0);
     }
 
     public MosaicRendererSurfaceView(Context context, AttributeSet attrs) {
         super(context, attrs);
-        init(false, 0, 0);
+        initialize(context, false, 0, 0);
+    }
+
+    public MosaicRendererSurfaceView(Context context, boolean translucent,
+            int depth, int stencil) {
+        super(context);
+        initialize(context, translucent, depth, stencil);
+    }
+
+    private void initialize(Context context, boolean translucent, int depth, int stencil) {
+        getDisplayOrientation(context);
+        init(translucent, depth, stencil);
         setZOrderMediaOverlay(true);
     }
 
-    public MosaicRendererSurfaceView(Context context, boolean translucent, int depth, int stencil) {
-        super(context);
-        init(translucent, depth, stencil);
-        setZOrderMediaOverlay(true);
+    private void getDisplayOrientation(Context context) {
+        Activity activity = (PanoramaActivity) context;
+        mIsLandscapeOrientation = (activity.getRequestedOrientation()
+                == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE );
     }
 
     private void init(boolean translucent, int depth, int stencil) {
@@ -78,7 +91,7 @@
             new ConfigChooser(5, 6, 5, 0, depth, stencil));
 
         /* Set the renderer responsible for frame rendering */
-        mRenderer = new MosaicRendererSurfaceViewRenderer();
+        mRenderer = new MosaicRendererSurfaceViewRenderer(mIsLandscapeOrientation);
         setRenderer(mRenderer);
         setRenderMode(RENDERMODE_WHEN_DIRTY);
         mPreviewFrameReadyForProcessing = new ConditionVariable();
diff --git a/src/com/android/camera/panorama/MosaicRendererSurfaceViewRenderer.java b/src/com/android/camera/panorama/MosaicRendererSurfaceViewRenderer.java
index b2b2f56..3089972 100755
--- a/src/com/android/camera/panorama/MosaicRendererSurfaceViewRenderer.java
+++ b/src/com/android/camera/panorama/MosaicRendererSurfaceViewRenderer.java
@@ -25,9 +25,14 @@
 public class MosaicRendererSurfaceViewRenderer implements GLSurfaceView.Renderer
 {
     private static final String TAG = "MosaicRendererSurfaceViewRenderer";
+    private boolean mIsLandscapeOrientation;
 
     private MosaicSurfaceCreateListener mSurfaceCreateListener;
 
+    public MosaicRendererSurfaceViewRenderer(boolean isLandscapeOrientation) {
+        mIsLandscapeOrientation = isLandscapeOrientation;
+    }
+
     /** A callback to be called when the surface is created */
     public interface MosaicSurfaceCreateListener {
         public void onMosaicSurfaceCreated(final int surface);
@@ -41,7 +46,7 @@
 
     @Override
     public void onSurfaceChanged(GL10 gl, int width, int height) {
-        MosaicRenderer.reset(width, height);
+        MosaicRenderer.reset(width, height, mIsLandscapeOrientation);
         Log.i(TAG, "Renderer: onSurfaceChanged");
         if (mSurfaceCreateListener != null) {
             mSurfaceCreateListener.onMosaicSurfaceChanged();
diff --git a/src/com/android/camera/panorama/PanoramaActivity.java b/src/com/android/camera/panorama/PanoramaActivity.java
index 6fd9b1a..e63ef71 100755
--- a/src/com/android/camera/panorama/PanoramaActivity.java
+++ b/src/com/android/camera/panorama/PanoramaActivity.java
@@ -983,9 +983,10 @@
         // the screen).
         if (mCameraState != PREVIEW_STOPPED) stopCameraPreview();
 
-        int orientation = Util.getDisplayOrientation(Util.getDisplayRotation(this),
-                CameraHolder.instance().getBackCameraId());
-        mCameraDevice.setDisplayOrientation(orientation);
+        // Set the display orientation to 0, so that the underlying mosaic library
+        // can always get undistorted mPreviewWidth x mPreviewHeight image data
+        // from SurfaceTexture.
+        mCameraDevice.setDisplayOrientation(0);
 
         setPreviewTexture(mSurfaceTexture);