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);