diff options
author | Angus Kong <shkong@google.com> | 2011-08-30 20:31:11 +0800 |
---|---|---|
committer | Angus Kong <shkong@google.com> | 2011-09-01 16:22:57 +0800 |
commit | c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382 (patch) | |
tree | 1fd56fe3a16e66bca88a0d0bb3f1d7867d87be42 /src | |
parent | c6c0c7a38e716fe88fb86f24463f79d130ca42dd (diff) | |
download | packages_apps_LegacyCamera-c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382.zip packages_apps_LegacyCamera-c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382.tar.gz packages_apps_LegacyCamera-c89a0eb7b1f0cd5cd45de8fd08ff051f6f74f382.tar.bz2 |
The angle indication now goes in two directions.
The computation of angles is moved into PanoramaActivity from MosaicFrameProcessor for rapid update
of the UI.
bug:5141674
Change-Id: I1bb6721c8354a0460701b5b0f60d772dd0b8633a
Diffstat (limited to 'src')
-rw-r--r-- | src/com/android/camera/panorama/CaptureView.java | 70 | ||||
-rw-r--r-- | src/com/android/camera/panorama/IndicationView.java | 120 | ||||
-rw-r--r-- | src/com/android/camera/panorama/MosaicFrameProcessor.java | 41 | ||||
-rw-r--r-- | src/com/android/camera/panorama/PanoramaActivity.java | 134 |
4 files changed, 221 insertions, 144 deletions
diff --git a/src/com/android/camera/panorama/CaptureView.java b/src/com/android/camera/panorama/CaptureView.java deleted file mode 100644 index 3b1dd29..0000000 --- a/src/com/android/camera/panorama/CaptureView.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.camera.panorama; - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Paint.Align; -import android.graphics.RectF; -import android.graphics.Typeface; -import android.util.AttributeSet; -import android.util.Log; -import android.view.View; - -class CaptureView extends View { - private static final String TAG = "CaptureView"; - private int mStartAngle = 0; - private int mSweepAngle = 0; - private int mWidth; - private int mHeight; - private final Paint mPaint = new Paint(); - - public CaptureView(Context context, AttributeSet attrs) { - super(context, attrs); - mPaint.setStyle(Paint.Style.FILL); - mPaint.setColor(Color.RED); - mPaint.setAntiAlias(true); - mPaint.setTextSize(40); - mPaint.setTypeface(Typeface.create((Typeface) null, Typeface.BOLD)); - mPaint.setTextAlign(Align.CENTER); - } - - protected void onSizeChanged(int w, int h, int oldw, int oldh) { - Log.v(TAG, "onSizeChanged: W = " + w + ", H = " + h); - } - - public void setStartAngle(int angle) { - mStartAngle = angle; - } - - public void setSweepAngle(int angle) { - mSweepAngle = angle; - } - - @Override - protected void onDraw(Canvas canvas) { - mWidth = getWidth(); - mHeight = getHeight(); - - RectF rect = new RectF(mWidth / 2 - 100, 3 * mHeight / 4, - mWidth / 2 + 100, 3 * mHeight / 4 + 200); - canvas.drawArc(rect, -90 + mStartAngle, mSweepAngle, true, mPaint); - canvas.drawArc(rect, -90 - mStartAngle, mSweepAngle > 0 ? 2 : 0, true, mPaint); - } -} diff --git a/src/com/android/camera/panorama/IndicationView.java b/src/com/android/camera/panorama/IndicationView.java new file mode 100644 index 0000000..d0eaf9e --- /dev/null +++ b/src/com/android/camera/panorama/IndicationView.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.camera.panorama; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.RectF; +import android.util.AttributeSet; +import android.view.View; + +class IndicationView extends View { + private static final String TAG = "IndicationView"; + private static final int PAN_DIRECTION_NONE = 0; + private static final int PAN_DIRECTION_LEFT = 1; + private static final int PAN_DIRECTION_RIGHT = 2; + private int mSweepAngle = 0; + private int mStartAngle = 0; + private int mMaxSweepAngle = 0; + private int mLeftMostAngle = 0; + private int mRightMostAngle = 0; + private int mAngleOffset = 0; + private int mPanningDirection = 0; + private final Paint mBackgroundPaint = new Paint(); + private final Paint mSweepAreaPaint = new Paint(); + private final Paint mIndicatorPaint = new Paint(); + private RectF mRect; + + public IndicationView(Context context, AttributeSet attrs) { + super(context, attrs); + mSweepAreaPaint.setStyle(Paint.Style.FILL); + mSweepAreaPaint.setColor(Color.RED); + + mBackgroundPaint.setStyle(Paint.Style.FILL); + mBackgroundPaint.setColor(Color.BLUE); + + mIndicatorPaint.setStyle(Paint.Style.FILL); + mIndicatorPaint.setColor(0xFFFFBBBB); + + mRect = new RectF(); + } + + protected void onSizeChanged(int w, int h, int oldw, int oldh) { + mRect.set(0, 0, w, h * 2); + } + + public void setMaxSweepAngle(int angle) { + mStartAngle = -90 - angle / 2; + mMaxSweepAngle = angle; + } + + public void setSweepAngle(int angle) { + // The panning direction will be decided after user pan more than 10 degrees in one + // direction. + if (mPanningDirection == PAN_DIRECTION_NONE) { + if (angle > 10) { + mPanningDirection = PAN_DIRECTION_RIGHT; + mAngleOffset = -mMaxSweepAngle / 2; + } + if (angle < -10) { + mPanningDirection = PAN_DIRECTION_LEFT; + mAngleOffset = mMaxSweepAngle / 2; + } + } else { + mSweepAngle = angle; + if (mPanningDirection == PAN_DIRECTION_RIGHT) { + // Bounded by the left most angle. + mSweepAngle = Math.max(mLeftMostAngle, mSweepAngle); + // Bounded by the max angle in the right direction. + mSweepAngle = Math.min(mMaxSweepAngle, mSweepAngle); + // The right most angle is adjusted. + mRightMostAngle = Math.max(mRightMostAngle, mSweepAngle); + } + if (mPanningDirection == PAN_DIRECTION_LEFT) { + // Bounded by the right most angle. + mSweepAngle = Math.min(mRightMostAngle, mSweepAngle); + // Bounded by the max angle in the left direction. + mSweepAngle = Math.max(-mMaxSweepAngle, mSweepAngle); + // The left most angle is adjusted. + mLeftMostAngle = Math.min(mLeftMostAngle, mSweepAngle); + } + } + } + + public void resetAngles() { + mSweepAngle = 0; + mLeftMostAngle = 0; + mRightMostAngle = 0; + mAngleOffset = 0; + mPanningDirection = PAN_DIRECTION_NONE; + } + + @Override + protected void onDraw(Canvas canvas) { + // the background + canvas.drawArc(mRect, mStartAngle, mMaxSweepAngle, true, mBackgroundPaint); + if (mPanningDirection != PAN_DIRECTION_NONE) { + // the spanned area + canvas.drawArc(mRect, -90 + mLeftMostAngle + mAngleOffset, + mRightMostAngle - mLeftMostAngle, true, mSweepAreaPaint); + // the indication line + canvas.drawArc(mRect, -91 + mSweepAngle + mAngleOffset, 2, true, mIndicatorPaint); + } + } +} diff --git a/src/com/android/camera/panorama/MosaicFrameProcessor.java b/src/com/android/camera/panorama/MosaicFrameProcessor.java index 2e077fa..5af074b 100644 --- a/src/com/android/camera/panorama/MosaicFrameProcessor.java +++ b/src/com/android/camera/panorama/MosaicFrameProcessor.java @@ -45,14 +45,6 @@ public class MosaicFrameProcessor { private ProgressListener mProgressListener; - private float mCompassValueX; - private float mCompassValueY; - private float mCompassValueXStart; - private float mCompassValueYStart; - private int mCompassThreshold; - private int mTraversedAngleX; - private int mTraversedAngleY; - // Panning rate is in unit of percentage of image content translation / second. private float mPanningRateX; private float mPanningRateY; @@ -62,13 +54,11 @@ public class MosaicFrameProcessor { private int mPreviewBufferSize; public interface ProgressListener { - public void onProgress(boolean isFinished, float panningRateX, float panningRateY, - int traversedAngleX, int traversedAngleY); + public void onProgress(boolean isFinished, float panningRateX, float panningRateY); } - public MosaicFrameProcessor(int sweepAngle, int previewWidth, int previewHeight, int bufSize) { + public MosaicFrameProcessor(int previewWidth, int previewHeight, int bufSize) { mMosaicer = new Mosaic(); - mCompassThreshold = sweepAngle; mPreviewWidth = previewWidth; mPreviewHeight = previewHeight; mPreviewBufferSize = bufSize; @@ -146,36 +136,20 @@ public class MosaicFrameProcessor { // Access the timestamp associated with it... long timestamp = mFrameTimestamp[mCurrProcessFrameIdx]; - // Keep track of what compass bearing we started at... - if (mTotalFrameCount == 0) { // First frame - mCompassValueXStart = mCompassValueX; - mCompassValueYStart = mCompassValueY; - } - - // By what angle has the camera moved since start of capture? - mTraversedAngleX = (int) PanoUtil.calculateDifferenceBetweenAngles( - mCompassValueX, mCompassValueXStart); - mTraversedAngleY = (int) PanoUtil.calculateDifferenceBetweenAngles( - mCompassValueY, mCompassValueYStart); - // TODO: make the termination condition regarding reaching // MAX_NUMBER_OF_FRAMES solely determined in the library. - if (mTotalFrameCount < MAX_NUMBER_OF_FRAMES - && mTraversedAngleX < mCompassThreshold - && mTraversedAngleY < mCompassThreshold) { + if (mTotalFrameCount < MAX_NUMBER_OF_FRAMES) { // If we are still collecting new frames for the current mosaic, // process the new frame. calculateTranslationRate(timestamp); // Publish progress of the ongoing processing if (mProgressListener != null) { - mProgressListener.onProgress(false, mPanningRateX, mPanningRateY, - mTraversedAngleX, mTraversedAngleY); + mProgressListener.onProgress(false, mPanningRateX, mPanningRateY); } } else { if (mProgressListener != null) { - mProgressListener.onProgress(true, mPanningRateX, mPanningRateY, - mTraversedAngleX, mTraversedAngleY); + mProgressListener.onProgress(true, mPanningRateX, mPanningRateY); } } } @@ -204,9 +178,4 @@ public class MosaicFrameProcessor { mTranslationLastX = translationCurrX; mTranslationLastY = translationCurrY; } - - public void updateCompassValue(float valueX, float valueY) { - mCompassValueX = valueX; - mCompassValueY = valueY; - } } diff --git a/src/com/android/camera/panorama/PanoramaActivity.java b/src/com/android/camera/panorama/PanoramaActivity.java index b6246ee..beb24de 100644 --- a/src/com/android/camera/panorama/PanoramaActivity.java +++ b/src/com/android/camera/panorama/PanoramaActivity.java @@ -55,6 +55,7 @@ import android.os.Handler; import android.os.Message; import android.util.Log; import android.view.Gravity; +import android.view.OrientationEventListener; import android.view.View; import android.view.WindowManager; import android.view.animation.Animation; @@ -102,7 +103,7 @@ public class PanoramaActivity extends Activity implements private Button mStopCaptureButton; private View mReviewLayout; private ImageView mReview; - private CaptureView mCaptureView; + private IndicationView mIndicationView; private MosaicRendererSurfaceView mMosaicView; private TextView mTooFastPrompt; private Animation mSlideIn, mSlideOut; @@ -114,6 +115,22 @@ public class PanoramaActivity extends Activity implements private String mDialogOk; private AlertDialog mAlertDialog; + private float mCompassValueX; + private float mCompassValueY; + private float mCompassValueXStart; + private float mCompassValueYStart; + private float mCompassValueXStartBuffer; + private float mCompassValueYStartBuffer; + private int mCompassThreshold; + private int mTraversedAngleX; + private int mTraversedAngleY; + private long mTimestamp; + // Control variables for the terminate condition. + private int mMinAngleX; + private int mMaxAngleX; + private int mMinAngleY; + private int mMaxAngleY; + private RotateImageView mThumbnailView; private Thumbnail mThumbnail; private SharePopup mSharePopup; @@ -127,14 +144,17 @@ public class PanoramaActivity extends Activity implements private Sensor mSensor; private ModePicker mModePicker; private MosaicFrameProcessor mMosaicFrameProcessor; - private String mCurrentImagePath = null; private long mTimeTaken; private Handler mMainHandler; private SurfaceTexture mSurfaceTexture; private boolean mThreadRunning; private float[] mTransformMatrix; private float mHorizontalViewAngle; - private float mVerticalViewAngle; + + private PanoOrientationEventListener mOrientationEventListener; + // The value could be 0, 1, 2, 3 for the 4 different orientations measured in clockwise + // respectively. + private int mDeviceOrientation; private class MosaicJpeg { public MosaicJpeg(byte[] data, int width, int height) { @@ -148,6 +168,19 @@ public class PanoramaActivity extends Activity implements public final int height; } + private class PanoOrientationEventListener extends OrientationEventListener { + public PanoOrientationEventListener(Context context) { + super(context); + } + + @Override + public void onOrientationChanged(int orientation) { + // Default to the last known orientation. + if (orientation == ORIENTATION_UNKNOWN) return; + mDeviceOrientation = ((orientation + 45) / 90) % 4; + } + } + @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); @@ -163,6 +196,8 @@ public class PanoramaActivity extends Activity implements mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION); } + mOrientationEventListener = new PanoOrientationEventListener(this); + mTransformMatrix = new float[16]; mPreparePreviewString = @@ -289,8 +324,8 @@ public class PanoramaActivity extends Activity implements parameters.setRecordingHint(false); - mHorizontalViewAngle = parameters.getHorizontalViewAngle(); - mVerticalViewAngle = parameters.getVerticalViewAngle(); + mHorizontalViewAngle = ((mDeviceOrientation % 2) == 0) ? + parameters.getHorizontalViewAngle() : parameters.getVerticalViewAngle(); } public int getPreviewBufSize() { @@ -391,20 +426,30 @@ public class PanoramaActivity extends Activity implements mPanoControlLayout.startAnimation(mSlideOut); mPanoControlLayout.setVisibility(View.GONE); + mCompassValueXStart = mCompassValueXStartBuffer; + mCompassValueYStart = mCompassValueYStartBuffer; + mMinAngleX = 0; + mMaxAngleX = 0; + mMinAngleY = 0; + mMaxAngleY = 0; + mTimestamp = 0; + mMosaicFrameProcessor.setProgressListener(new MosaicFrameProcessor.ProgressListener() { @Override - public void onProgress(boolean isFinished, float panningRateX, float panningRateY, - int traversedAngleX, int traversedAngleY) { - if (isFinished) { + public void onProgress(boolean isFinished, float panningRateX, float panningRateY) { + if (isFinished + || (mMaxAngleX - mMinAngleX >= DEFAULT_SWEEP_ANGLE) + || (mMaxAngleY - mMinAngleY >= DEFAULT_SWEEP_ANGLE)) { stopCapture(); } else { - updateProgress(panningRateX, panningRateY, traversedAngleX, traversedAngleY); + updateProgress(panningRateX); } } }); mStopCaptureButton.setVisibility(View.VISIBLE); - mCaptureView.setVisibility(View.VISIBLE); + mIndicationView.resetAngles(); + mIndicationView.setVisibility(View.VISIBLE); mMosaicView.setVisibility(View.VISIBLE); } @@ -434,25 +479,18 @@ public class PanoramaActivity extends Activity implements } } - private void updateProgress(float panningRateX, float panningRateY, - int traversedAngleX, int traversedAngleY) { - + private void updateProgress(float panningRate) { mMosaicView.setReady(); mMosaicView.requestRender(); // TODO: Now we just display warning message by the panning speed. // Since we only support horizontal panning, we should display a warning message // in UI when there're significant vertical movements. - if ((panningRateX * mHorizontalViewAngle > PANNING_SPEED_THRESHOLD) - || (panningRateY * mVerticalViewAngle > PANNING_SPEED_THRESHOLD)) { + if (Math.abs(panningRate * mHorizontalViewAngle) > PANNING_SPEED_THRESHOLD) { // TODO: draw speed indication according to the UI spec. mTooFastPrompt.setVisibility(View.VISIBLE); - mCaptureView.setSweepAngle(Math.max(traversedAngleX, traversedAngleY) + 1); - mCaptureView.invalidate(); } else { mTooFastPrompt.setVisibility(View.GONE); - mCaptureView.setSweepAngle(Math.max(traversedAngleX, traversedAngleY) + 1); - mCaptureView.invalidate(); } } @@ -462,8 +500,8 @@ public class PanoramaActivity extends Activity implements mCaptureState = CAPTURE_VIEWFINDER; mCaptureLayout = (View) findViewById(R.id.pano_capture_layout); - mCaptureView = (CaptureView) findViewById(R.id.pano_capture_view); - mCaptureView.setStartAngle(-DEFAULT_SWEEP_ANGLE / 2); + mIndicationView = (IndicationView) findViewById(R.id.pano_capture_view); + mIndicationView.setMaxSweepAngle(DEFAULT_SWEEP_ANGLE); mStopCaptureButton = (Button) findViewById(R.id.pano_capture_stop_button); mTooFastPrompt = (TextView) findViewById(R.id.pano_capture_too_fast_textview); @@ -604,7 +642,7 @@ public class PanoramaActivity extends Activity implements mReviewLayout.setVisibility(View.GONE); mStopCaptureButton.setVisibility(View.GONE); - mCaptureView.setVisibility(View.GONE); + mIndicationView.setVisibility(View.GONE); mPanoControlLayout.setVisibility(View.VISIBLE); mPanoControlLayout.startAnimation(mSlideIn); mCaptureLayout.setVisibility(View.VISIBLE); @@ -623,7 +661,6 @@ public class PanoramaActivity extends Activity implements } mCaptureLayout.setVisibility(View.GONE); mReviewLayout.setVisibility(View.VISIBLE); - mCaptureView.setSweepAngle(0); } private Uri savePanorama(byte[] jpegData, int orientation) { @@ -645,7 +682,7 @@ public class PanoramaActivity extends Activity implements if (mPausing || mThreadRunning) return; if (mMosaicFrameProcessor == null) { // Start the activity for the first time. - mMosaicFrameProcessor = new MosaicFrameProcessor(DEFAULT_SWEEP_ANGLE - 5, + mMosaicFrameProcessor = new MosaicFrameProcessor( mPreviewWidth, mPreviewHeight, getPreviewBufSize()); } mMosaicFrameProcessor.initialize(); @@ -660,6 +697,7 @@ public class PanoramaActivity extends Activity implements mMosaicView.onPause(); mSensorManager.unregisterListener(mListener); clearMosaicFrameProcessorIfNeeded(); + mOrientationEventListener.disable(); System.gc(); } @@ -668,9 +706,10 @@ public class PanoramaActivity extends Activity implements super.onResume(); mPausing = false; + mOrientationEventListener.enable(); /* * It is not necessary to get accelerometer events at a very high rate, - * by using a slower rate (SENSOR_DELAY_UI), we get an automatic + * by using a game rate (SENSOR_DELAY_UI), we get an automatic * low-pass filter, which "extracts" the gravity component of the * acceleration. As an added benefit, we use less power and CPU * resources. @@ -688,27 +727,46 @@ public class PanoramaActivity extends Activity implements mMosaicView.onResume(); } - private final SensorEventListener mListener = new SensorEventListener() { - private float mCompassCurrX; // degrees - private float mCompassCurrY; // degrees - private float mTimestamp; + private void updateCompassValue() { + // By what angle has the camera moved since start of capture? + mTraversedAngleX = (int) (mCompassValueX - mCompassValueXStart); + mTraversedAngleY = (int) (mCompassValueY - mCompassValueYStart); + mMinAngleX = Math.min(mMinAngleX, mTraversedAngleX); + mMaxAngleX = Math.max(mMaxAngleX, mTraversedAngleX); + mMinAngleY = Math.min(mMinAngleY, mTraversedAngleY); + mMaxAngleY = Math.max(mMaxAngleY, mTraversedAngleY); + + // Use orientation to identify if the user is panning to the right or the left. + switch (mDeviceOrientation) { + case 0: + mIndicationView.setSweepAngle(-mTraversedAngleX); + break; + case 1: + mIndicationView.setSweepAngle(mTraversedAngleY); + break; + case 2: + mIndicationView.setSweepAngle(mTraversedAngleX); + break; + case 3: + mIndicationView.setSweepAngle(-mTraversedAngleY); + break; + } + mIndicationView.invalidate(); + } + private final SensorEventListener mListener = new SensorEventListener() { public void onSensorChanged(SensorEvent event) { if (event.sensor.getType() == Sensor.TYPE_GYROSCOPE) { if (mTimestamp != 0) { final float dT = (event.timestamp - mTimestamp) * NS2S; - mCompassCurrX += event.values[1] * dT * 180.0f / Math.PI; - mCompassCurrY += event.values[0] * dT * 180.0f / Math.PI; + mCompassValueX += event.values[1] * dT * 180.0f / Math.PI; + mCompassValueY += event.values[0] * dT * 180.0f / Math.PI; + mCompassValueXStartBuffer = mCompassValueX; + mCompassValueYStartBuffer = mCompassValueY; + updateCompassValue(); } mTimestamp = event.timestamp; - } else if (event.sensor.getType() == Sensor.TYPE_ORIENTATION) { - mCompassCurrX = event.values[0]; - mCompassCurrY = event.values[1]; - } - - if (mMosaicFrameProcessor != null) { - mMosaicFrameProcessor.updateCompassValue(mCompassCurrX, mCompassCurrY); } } |