From d32aa042a7d7fdb52cd8cec571e7c648f837eaff Mon Sep 17 00:00:00 2001 From: Wei-Ta Chen Date: Wed, 19 Oct 2011 12:42:44 -0700 Subject: 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 --- jni/mosaic_renderer_jni.cpp | 145 ++++++++++++++++----- .../android/camera/panorama/MosaicRenderer.java | 3 +- .../camera/panorama/MosaicRendererSurfaceView.java | 25 +++- .../MosaicRendererSurfaceViewRenderer.java | 7 +- .../android/camera/panorama/PanoramaActivity.java | 7 +- 5 files changed, 142 insertions(+), 45 deletions(-) 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 gLastTx = 0.0f; 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 @@ GLfloat g_dTranslationToFBOCenterGL[16]; 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 @@ void ConvertAffine3x3toGL4x4(double *matGL44, double *mat33) 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 @@ void UpdateWarpTransformation(float *trs) } 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 @@ void UpdateWarpTransformation(float *trs) 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 @@ extern "C" 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 jint JNICALL Java_com_android_camera_panorama_MosaicRenderer_init( } +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); + } 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) + JNIEnv * env, jobject obj, jint width, jint height, jboolean isLandscapeOrientation) { - // 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; - - // 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); + gIsLandscapeOrientation = isLandscapeOrientation; + calculateUILayoutScaling(width, height, gIsLandscapeOrientation); gBuffer[0].Init(gPreviewFBOWidth, gPreviewFBOHeight, GL_RGBA); gBuffer[1].Init(gPreviewFBOWidth, gPreviewFBOHeight, GL_RGBA); @@ -543,6 +614,7 @@ JNIEXPORT void JNICALL Java_com_android_camera_panorama_MosaicRenderer_reset( 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 @@ JNIEXPORT void JNICALL Java_com_android_camera_panorama_MosaicRenderer_preproces 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 @@ JNIEXPORT void JNICALL Java_com_android_camera_panorama_MosaicRenderer_step( 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 @@ public class MosaicRenderer * * @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,25 +35,36 @@ public class MosaicRendererSurfaceView extends GLSurfaceView { 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); - setZOrderMediaOverlay(true); + initialize(context, false, 0, 0); } - public MosaicRendererSurfaceView(Context context, boolean translucent, int depth, int stencil) { + 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); } + 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) { /* By default, GLSurfaceView() creates a RGB_565 opaque surface. @@ -78,7 +91,7 @@ public class MosaicRendererSurfaceView extends GLSurfaceView { 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 @@ import javax.microedition.khronos.opengles.GL10; 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 @@ public class MosaicRendererSurfaceViewRenderer implements GLSurfaceView.Renderer @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 @@ public class PanoramaActivity extends ActivityBase implements // 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); -- cgit v1.1