diff options
| -rw-r--r-- | jni/feature_mos/src/mosaic/Blend.cpp | 219 | ||||
| -rw-r--r-- | res/raw/backdropper.graph | 3 | ||||
| -rw-r--r-- | res/raw/goofy_face.graph | 4 | ||||
| -rw-r--r-- | src/com/android/camera/Camera.java | 17 | ||||
| -rw-r--r-- | src/com/android/camera/EffectsRecorder.java | 3 | ||||
| -rwxr-xr-x | src/com/android/camera/panorama/PanoramaActivity.java | 59 |
6 files changed, 207 insertions, 98 deletions
diff --git a/jni/feature_mos/src/mosaic/Blend.cpp b/jni/feature_mos/src/mosaic/Blend.cpp index 470fba0..e8b30f3 100644 --- a/jni/feature_mos/src/mosaic/Blend.cpp +++ b/jni/feature_mos/src/mosaic/Blend.cpp @@ -148,6 +148,12 @@ int Blend::runBlend(MosaicFrame **oframes, MosaicFrame **rframes, double xLeftCorners[2] = {2e30, 2e30}; double xRightCorners[2] = {-2e30, -2e30}; + // Corners of the top-most and bottom-most frames respectively in the + // mosaic coordinate system. + double yTopCorners[2] = {2e30, 2e30}; + double yBottomCorners[2] = {-2e30, -2e30}; + + // Determine the extents of the final mosaic CSite *csite = m_AllSites ; for(int mfit = 0; mfit < frames_size; mfit++) @@ -177,6 +183,19 @@ int Blend::runBlend(MosaicFrame **oframes, MosaicFrame **rframes, xRightCorners[1] = x2; } + if(y0 < yTopCorners[0] || y3 < yTopCorners[1]) // If either of the top corners is lower + { + yTopCorners[0] = y0; + yTopCorners[1] = y3; + } + + if(y1 > yBottomCorners[0] || y2 > yBottomCorners[1]) // If either of the bottom corners is higher + { + yBottomCorners[0] = y1; + yBottomCorners[1] = y2; + } + + // Compute the centroid of the warped region FindQuadCentroid(x0, y0, x1, y1, x2, y2, x3, y3, csite->getVCenter().x, csite->getVCenter().y); @@ -198,11 +217,15 @@ int Blend::runBlend(MosaicFrame **oframes, MosaicFrame **rframes, Mheight = (unsigned short) (fullRect.bottom - fullRect.top + 1); int xLeftMost, xRightMost; + int yTopMost, yBottomMost; // Rounding up, so that we don't include the gray border. xLeftMost = max(0, max(xLeftCorners[0], xLeftCorners[1]) - fullRect.left + 1); xRightMost = min(Mwidth - 1, min(xRightCorners[0], xRightCorners[1]) - fullRect.left - 1); + yTopMost = max(0, max(yTopCorners[0], yTopCorners[1]) - fullRect.top + 1); + yBottomMost = min(Mheight - 1, min(yBottomCorners[0], yBottomCorners[1]) - fullRect.top - 1); + // Make sure image width is multiple of 4 Mwidth = (unsigned short) ((Mwidth + 3) & ~3); Mheight = (unsigned short) ((Mheight + 3) & ~3); // Round up. @@ -234,8 +257,16 @@ int Blend::runBlend(MosaicFrame **oframes, MosaicFrame **rframes, // cropped out of the computed mosaic to get rid of the gray borders. MosaicRect cropping_rect; - cropping_rect.left = xLeftMost; - cropping_rect.right = xRightMost; + if (m_wb.horizontal) + { + cropping_rect.left = xLeftMost; + cropping_rect.right = xRightMost; + } + else + { + cropping_rect.top = yTopMost; + cropping_rect.bottom = yBottomMost; + } // Do merging and blending : ret = DoMergeAndBlend(frames, numCenters, width, height, *imgMos, fullRect, @@ -378,47 +409,95 @@ int Blend::DoMergeAndBlend(MosaicFrame **frames, int nsite, // between the images on either side of each seam: if (m_wb.stripType == STRIP_TYPE_WIDE) { - // Set the number of pixels around the seam to cross-fade between - // the two component images, - int tw = STRIP_CROSS_FADE_WIDTH * width; - - for(int y = 0; y < imgMos.Y.height; y++) + if(m_wb.horizontal) { - for(int x = tw; x < imgMos.Y.width - tw + 1; ) + // Set the number of pixels around the seam to cross-fade between + // the two component images, + int tw = STRIP_CROSS_FADE_WIDTH * width; + + for(int y = 0; y < imgMos.Y.height; y++) { - // Determine where the seam is... - if (imgMos.Y.ptr[y][x] != imgMos.Y.ptr[y][x+1] && - imgMos.Y.ptr[y][x] != 255 && - imgMos.Y.ptr[y][x+1] != 255) + for(int x = tw; x < imgMos.Y.width - tw + 1; ) { - // Find the image indices on both sides of the seam - unsigned char idx1 = imgMos.Y.ptr[y][x]; - unsigned char idx2 = imgMos.Y.ptr[y][x+1]; - - for (int o = tw; o >= 0; o--) + // Determine where the seam is... + if (imgMos.Y.ptr[y][x] != imgMos.Y.ptr[y][x+1] && + imgMos.Y.ptr[y][x] != 255 && + imgMos.Y.ptr[y][x+1] != 255) { - // Set the image index to use for cross-fading - imgMos.V.ptr[y][x - o] = idx2; - // Set the intensity weights to use for cross-fading - imgMos.U.ptr[y][x - o] = 50 + (99 - 50) * o / tw; - } + // Find the image indices on both sides of the seam + unsigned char idx1 = imgMos.Y.ptr[y][x]; + unsigned char idx2 = imgMos.Y.ptr[y][x+1]; - for (int o = 1; o <= tw; o++) + for (int o = tw; o >= 0; o--) + { + // Set the image index to use for cross-fading + imgMos.V.ptr[y][x - o] = idx2; + // Set the intensity weights to use for cross-fading + imgMos.U.ptr[y][x - o] = 50 + (99 - 50) * o / tw; + } + + for (int o = 1; o <= tw; o++) + { + // Set the image index to use for cross-fading + imgMos.V.ptr[y][x + o] = idx1; + // Set the intensity weights to use for cross-fading + imgMos.U.ptr[y][x + o] = imgMos.U.ptr[y][x - o]; + } + + x += (tw + 1); + } + else { - // Set the image index to use for cross-fading - imgMos.V.ptr[y][x + o] = idx1; - // Set the intensity weights to use for cross-fading - imgMos.U.ptr[y][x + o] = imgMos.U.ptr[y][x - o]; + x++; } - - x += (tw + 1); } - else + } + } + else + { + // Set the number of pixels around the seam to cross-fade between + // the two component images, + int tw = STRIP_CROSS_FADE_WIDTH * height; + + for(int x = 0; x < imgMos.Y.width; x++) + { + for(int y = tw; y < imgMos.Y.height - tw + 1; ) { - x++; + // Determine where the seam is... + if (imgMos.Y.ptr[y][x] != imgMos.Y.ptr[y+1][x] && + imgMos.Y.ptr[y][x] != 255 && + imgMos.Y.ptr[y+1][x] != 255) + { + // Find the image indices on both sides of the seam + unsigned char idx1 = imgMos.Y.ptr[y][x]; + unsigned char idx2 = imgMos.Y.ptr[y+1][x]; + + for (int o = tw; o >= 0; o--) + { + // Set the image index to use for cross-fading + imgMos.V.ptr[y - o][x] = idx2; + // Set the intensity weights to use for cross-fading + imgMos.U.ptr[y - o][x] = 50 + (99 - 50) * o / tw; + } + + for (int o = 1; o <= tw; o++) + { + // Set the image index to use for cross-fading + imgMos.V.ptr[y + o][x] = idx1; + // Set the intensity weights to use for cross-fading + imgMos.U.ptr[y + o][x] = imgMos.U.ptr[y - o][x]; + } + + y += (tw + 1); + } + else + { + y++; + } } } } + } // Now perform the actual blending using the frame assignment determined above @@ -581,39 +660,80 @@ int Blend::PerformFinalBlending(YUVinfo &imgMos, MosaicRect &cropping_rect) } } - //Scan through each row and increment top if the row contains any gray - for (j = 0; j < imgMos.Y.height; j++) + if(m_wb.horizontal) { - for (i = cropping_rect.left; i < cropping_rect.right; i++) + //Scan through each row and increment top if the row contains any gray + for (j = 0; j < imgMos.Y.height; j++) { - if (b[j][i]) + for (i = cropping_rect.left; i < cropping_rect.right; i++) { - break; // to next row + if (b[j][i]) + { + break; // to next row + } + } + + if (i == cropping_rect.right) //no gray pixel in this row! + { + cropping_rect.top = j; + break; } } - if (i == cropping_rect.right) //no gray pixel in this row! + //Scan through each row and decrement bottom if the row contains any gray + for (j = imgMos.Y.height-1; j >= 0; j--) { - cropping_rect.top = j; - break; + for (i = cropping_rect.left; i < cropping_rect.right; i++) + { + if (b[j][i]) + { + break; // to next row + } + } + + if (i == cropping_rect.right) //no gray pixel in this row! + { + cropping_rect.bottom = j; + break; + } } } - - //Scan through each row and decrement bottom if the row contains any gray - for (j = imgMos.Y.height-1; j >= 0; j--) + else // Vertical Mosaic { - for (i = cropping_rect.left; i < cropping_rect.right; i++) + //Scan through each column and increment left if the column contains any gray + for (i = 0; i < imgMos.Y.width; i++) { - if (b[j][i]) + for (j = cropping_rect.top; j < cropping_rect.bottom; j++) + { + if (b[j][i]) + { + break; // to next column + } + } + + if (j == cropping_rect.bottom) //no gray pixel in this column! { - break; // to next row + cropping_rect.left = i; + break; } } - if (i == cropping_rect.right) //no gray pixel in this row! + //Scan through each column and decrement right if the column contains any gray + for (i = imgMos.Y.width-1; i >= 0; i--) { - cropping_rect.bottom = j; - break; + for (j = cropping_rect.top; j < cropping_rect.bottom; j++) + { + if (b[j][i]) + { + break; // to next column + } + } + + if (j == cropping_rect.bottom) //no gray pixel in this column! + { + cropping_rect.right = i; + break; + } } } @@ -1046,7 +1166,8 @@ void Blend::SelectRelevantFrames(MosaicFrame **frames, int frames_size, double deltaY = currY - prevY; double center2centerDist = sqrt(deltaY * deltaY + deltaX * deltaX); - if (fabs(deltaX) > STRIP_SEPARATION_THRESHOLD * last->width) + if (fabs(deltaX) > STRIP_SEPARATION_THRESHOLD * last->width || + fabs(deltaY) > STRIP_SEPARATION_THRESHOLD * last->height) { relevant_frames[relevant_frames_size] = mb; relevant_frames_size++; diff --git a/res/raw/backdropper.graph b/res/raw/backdropper.graph index e3fe877..25c86e6 100644 --- a/res/raw/backdropper.graph +++ b/res/raw/backdropper.graph @@ -35,6 +35,8 @@ @external previewWidth; @external previewHeight; +@external orientation; + @external learningDoneListener; // Filters --------------------------------------------------- @@ -52,6 +54,7 @@ sourceUrl = "no_file_specified"; waitForNewFrame = false; sourceIsUrl = true; + orientation = $orientation; } // Background replacer diff --git a/res/raw/goofy_face.graph b/res/raw/goofy_face.graph index e346322..430c811 100644 --- a/res/raw/goofy_face.graph +++ b/res/raw/goofy_face.graph @@ -37,6 +37,10 @@ @external previewWidth; @external previewHeight; +// Not used by this graph, but simplifies higher-level +// graph initialization code. +@external orientation; + // Filters --------------------------------------------------- // Camera input diff --git a/src/com/android/camera/Camera.java b/src/com/android/camera/Camera.java index 07863e5..10f0b80 100644 --- a/src/com/android/camera/Camera.java +++ b/src/com/android/camera/Camera.java @@ -119,9 +119,6 @@ public class Camera extends ActivityBase implements FocusManager.Listener, private Parameters mInitialParams; private boolean mFocusAreaSupported; private boolean mMeteringAreaSupported; - private boolean mAwbLockSupported; - private boolean mAeLockSupported; - private boolean mAeAwbLock; private MyOrientationEventListener mOrientationListener; // The degrees of the device rotated clockwise from its natural orientation. @@ -1420,9 +1417,6 @@ public class Camera extends ActivityBase implements FocusManager.Listener, // Do not do focus if there is not enough storage. if (pressed && !canTakePicture()) return; - // Lock AE and AWB so users can half-press shutter and recompose. - mAeAwbLock = pressed; - setCameraParameters(UPDATE_PARAM_PREFERENCE); mFocusManager.doFocus(pressed); } @@ -1527,7 +1521,6 @@ public class Camera extends ActivityBase implements FocusManager.Listener, if (mFaceView != null) { mFaceView.setDisplayOrientation(mDisplayOrientation); } - mAeAwbLock = false; // Always unlock AE and AWB before start. setCameraParameters(UPDATE_PARAM_ALL); // If the focus mode is continuous autofocus, call cancelAutoFocus to // resume it because it may have been paused by autoFocus call. @@ -1582,14 +1575,6 @@ public class Camera extends ActivityBase implements FocusManager.Listener, } private void updateCameraParametersPreference() { - if (mAwbLockSupported) { - mParameters.setAutoWhiteBalanceLock(mAeAwbLock); - } - - if (mAeLockSupported) { - mParameters.setAutoExposureLock(mAeAwbLock); - } - if (mFocusAreaSupported) { mParameters.setFocusAreas(mFocusManager.getFocusAreas()); } @@ -1985,7 +1970,5 @@ public class Camera extends ActivityBase implements FocusManager.Listener, && isSupported(Parameters.FOCUS_MODE_AUTO, mInitialParams.getSupportedFocusModes())); mMeteringAreaSupported = (mInitialParams.getMaxNumMeteringAreas() > 0); - mAwbLockSupported = mInitialParams.isAutoWhiteBalanceLockSupported(); - mAeLockSupported = mInitialParams.isAutoExposureLockSupported(); } } diff --git a/src/com/android/camera/EffectsRecorder.java b/src/com/android/camera/EffectsRecorder.java index 301f2a2..f177387 100644 --- a/src/com/android/camera/EffectsRecorder.java +++ b/src/com/android/camera/EffectsRecorder.java @@ -300,7 +300,8 @@ public class EffectsRecorder { mGraphEnv.addReferences( "previewSurface", mPreviewSurfaceHolder.getSurface(), "previewWidth", mPreviewWidth, - "previewHeight", mPreviewHeight); + "previewHeight", mPreviewHeight, + "orientation", mOrientationHint); if (mState == STATE_PREVIEW) { // Switching effects while running. Inform video camera. sendMessage(mCurrentEffect, EFFECT_MSG_SWITCHING_EFFECT); diff --git a/src/com/android/camera/panorama/PanoramaActivity.java b/src/com/android/camera/panorama/PanoramaActivity.java index a1605de..09df1e0 100755 --- a/src/com/android/camera/panorama/PanoramaActivity.java +++ b/src/com/android/camera/panorama/PanoramaActivity.java @@ -38,6 +38,7 @@ import android.animation.ValueAnimator; import android.app.Activity; import android.app.AlertDialog; import android.app.ProgressDialog; +import android.content.ContentResolver; import android.content.Context; import android.content.DialogInterface; import android.content.res.Resources; @@ -71,6 +72,7 @@ import android.widget.ImageView; import android.widget.TextView; import java.io.ByteArrayOutputStream; +import java.io.File; import java.util.List; /** @@ -147,9 +149,6 @@ public class PanoramaActivity extends ActivityBase implements private Thumbnail mThumbnail; private SharePopup mSharePopup; - private AnimatorSet mThumbnailViewAndModePickerOut; - private AnimatorSet mThumbnailViewAndModePickerIn; - private int mPreviewWidth; private int mPreviewHeight; private Camera mCameraDevice; @@ -271,9 +270,8 @@ public class PanoramaActivity extends ActivityBase implements onBackgroundThreadFinished(); // Set the thumbnail bitmap here because mThumbnailView must be accessed // from the UI thread. - if (mThumbnail != null) { - mThumbnailView.setBitmap(mThumbnail.getBitmap()); - } + updateThumbnailButton(); + // Share popup may still have the reference to the old thumbnail. Clear it. mSharePopup = null; resetToPreview(); @@ -309,6 +307,12 @@ public class PanoramaActivity extends ActivityBase implements }); } + @Override + public void onStart() { + super.onStart(); + updateThumbnailButton(); + } + private void setupCamera() { openCamera(); Parameters parameters = mCameraDevice.getParameters(); @@ -526,30 +530,6 @@ public class PanoramaActivity extends ActivityBase implements mCaptureIndicator.setVisibility(View.VISIBLE); showDirectionIndicators(PanoProgressBar.DIRECTION_NONE); - // XML-style animations can not be used here. The Y position has to be calculated runtime. - float ystart = mThumbnailView.getY(); - ValueAnimator va1 = ObjectAnimator.ofFloat( - mThumbnailView, "y", ystart, -mThumbnailView.getHeight()); - ValueAnimator va1Reverse = ObjectAnimator.ofFloat( - mThumbnailView, "y", -mThumbnailView.getHeight(), ystart); - ystart = mModePicker.getY(); - float height = mCaptureLayout.getHeight(); - ValueAnimator va2 = ObjectAnimator.ofFloat( - mModePicker, "y", ystart, height + 1); - ValueAnimator va2Reverse = ObjectAnimator.ofFloat( - mModePicker, "y", height + 1, ystart); - LinearInterpolator li = new LinearInterpolator(); - mThumbnailViewAndModePickerOut = new AnimatorSet(); - mThumbnailViewAndModePickerOut.play(va1).with(va2); - mThumbnailViewAndModePickerOut.setDuration(500); - mThumbnailViewAndModePickerOut.setInterpolator(li); - mThumbnailViewAndModePickerIn = new AnimatorSet(); - mThumbnailViewAndModePickerIn.play(va1Reverse).with(va2Reverse); - mThumbnailViewAndModePickerIn.setDuration(500); - mThumbnailViewAndModePickerIn.setInterpolator(li); - - mThumbnailViewAndModePickerOut.start(); - mCompassValueXStart = mCompassValueXStartBuffer; mCompassValueYStart = mCompassValueYStartBuffer; mMinAngleX = 0; @@ -571,6 +551,8 @@ public class PanoramaActivity extends ActivityBase implements } }); + if (mModePicker != null) mModePicker.setEnabled(false); + mPanoProgressBar.reset(); // TODO: calculate the indicator width according to different devices to reflect the actual // angle of view of the camera device. @@ -609,7 +591,8 @@ public class PanoramaActivity extends ActivityBase implements } }); } - mThumbnailViewAndModePickerIn.start(); + // do we have to wait for the thread to complete before enabling this? + if (mModePicker != null) mModePicker.setEnabled(true); } private void showTooFastIndication() { @@ -749,6 +732,19 @@ public class PanoramaActivity extends ActivityBase implements t.start(); } + private void updateThumbnailButton() { + // Update last image if URI is invalid and the storage is ready. + ContentResolver contentResolver = getContentResolver(); + if ((mThumbnail == null || !Util.isUriValid(mThumbnail.getUri(), contentResolver))) { + mThumbnail = Thumbnail.getLastThumbnail(contentResolver); + } + if (mThumbnail != null) { + mThumbnailView.setBitmap(mThumbnail.getBitmap()); + } else { + mThumbnailView.setBitmap(null); + } + } + public void saveHighResMosaic() { runBackgroundThread(new Thread() { @Override @@ -884,6 +880,7 @@ public class PanoramaActivity extends ActivityBase implements stopCapture(true); reset(); } + if (mSharePopup != null) mSharePopup.dismiss(); releaseCamera(); mMosaicView.onPause(); clearMosaicFrameProcessorIfNeeded(); |
