summaryrefslogtreecommitdiffstats
path: root/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2Settings.java
diff options
context:
space:
mode:
Diffstat (limited to 'camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2Settings.java')
-rw-r--r--camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2Settings.java160
1 files changed, 137 insertions, 23 deletions
diff --git a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2Settings.java b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2Settings.java
index efa68e8..0062097 100644
--- a/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2Settings.java
+++ b/camera2/portability/src/com/android/ex/camera2/portability/AndroidCamera2Settings.java
@@ -18,10 +18,13 @@ package com.android.ex.camera2.portability;
import static android.hardware.camera2.CaptureRequest.*;
+import android.graphics.Matrix;
import android.graphics.Rect;
+import android.graphics.RectF;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.params.MeteringRectangle;
+import android.location.Location;
import android.util.Range;
import com.android.ex.camera2.portability.CameraCapabilities.FlashMode;
@@ -41,8 +44,13 @@ public class AndroidCamera2Settings extends CameraSettings {
private static final Log.Tag TAG = new Log.Tag("AndCam2Set");
private final Builder mTemplateSettings;
- private final Rect mActiveArray;
private final Camera2RequestSettingsSet mRequestSettings;
+ /** Sensor's active array bounds. */
+ private final Rect mActiveArray;
+ /** Crop rectangle for digital zoom (measured WRT the active array). */
+ private final Rect mCropRectangle;
+ /** Bounds of visible preview portion (measured WRT the active array). */
+ private Rect mVisiblePreviewRectangle;
/**
* Create a settings representation that answers queries of unspecified
@@ -56,19 +64,33 @@ public class AndroidCamera2Settings extends CameraSettings {
* their effective values when submitting a capture request will be those of
* the template that is provided to the camera framework at that time.</p>
*
- * @param camera Device from which to draw default settings.
+ * @param camera Device from which to draw default settings
+ * (non-{@code null}).
* @param template Specific template to use for the defaults.
- * @param activeArray Boundary coordinates of the sensor's active array.
+ * @param activeArray Boundary coordinates of the sensor's active array
+ * (non-{@code null}).
* @param preview Dimensions of preview streams.
* @param photo Dimensions of captured images.
*
+ * @throws IllegalArgumentException If {@code camera} or {@code activeArray}
+ * is {@code null}.
* @throws CameraAccessException Upon internal framework/driver failure.
*/
public AndroidCamera2Settings(CameraDevice camera, int template, Rect activeArray,
Size preview, Size photo) throws CameraAccessException {
+ if (camera == null) {
+ throw new NullPointerException("camera must not be null");
+ }
+ if (activeArray == null) {
+ throw new NullPointerException("activeArray must not be null");
+ }
+
mTemplateSettings = camera.createCaptureRequest(template);
- mActiveArray = activeArray;
mRequestSettings = new Camera2RequestSettingsSet();
+ mActiveArray = activeArray;
+ mCropRectangle = new Rect(0, 0, activeArray.width(), activeArray.height());
+
+ mSizesLocked = false;
Range<Integer> previewFpsRange = mTemplateSettings.get(CONTROL_AE_TARGET_FPS_RANGE);
if (previewFpsRange != null) {
@@ -79,8 +101,8 @@ public class AndroidCamera2Settings extends CameraSettings {
setPhotoSize(photo);
mJpegCompressQuality = queryTemplateDefaultOrMakeOneUp(JPEG_QUALITY, (byte) 0);
// TODO: mCurrentPhotoFormat
- // TODO: mCurrentZoomRatio
- mCurrentZoomRatio = 1.0f;
+ // NB: We're assuming that templates won't be zoomed in by default.
+ mCurrentZoomRatio = CameraCapabilities.ZOOM_RATIO_UNZOOMED;
// TODO: mCurrentZoomIndex
mExposureCompensationIndex =
queryTemplateDefaultOrMakeOneUp(CONTROL_AE_EXPOSURE_COMPENSATION, 0);
@@ -116,8 +138,9 @@ public class AndroidCamera2Settings extends CameraSettings {
public AndroidCamera2Settings(AndroidCamera2Settings other) {
super(other);
mTemplateSettings = other.mTemplateSettings;
- mActiveArray = other.mActiveArray;
mRequestSettings = new Camera2RequestSettingsSet(other.mRequestSettings);
+ mActiveArray = other.mActiveArray;
+ mCropRectangle = new Rect(other.mCropRectangle);
}
@Override
@@ -159,13 +182,31 @@ public class AndroidCamera2Settings extends CameraSettings {
return null;
}
+ @Override
+ public void setZoomRatio(float ratio) {
+ super.setZoomRatio(ratio);
+
+ // Compute the crop rectangle to be passed to the framework
+ mCropRectangle.set(0, 0,
+ toIntConstrained(
+ mActiveArray.width() / mCurrentZoomRatio, 0, mActiveArray.width()),
+ toIntConstrained(
+ mActiveArray.height() / mCurrentZoomRatio, 0, mActiveArray.height()));
+ mCropRectangle.offsetTo((mActiveArray.width() - mCropRectangle.width()) / 2,
+ (mActiveArray.height() - mCropRectangle.height()) / 2);
+
+ // Compute the effective crop rectangle to be used for computing focus/metering coordinates
+ mVisiblePreviewRectangle =
+ effectiveCropRectFromRequested(mCropRectangle, mCurrentPreviewSize);
+ }
+
private boolean matchesTemplateDefault(Key<?> setting) {
if (setting == CONTROL_AE_REGIONS) {
return mMeteringAreas.size() == 0;
} else if (setting == CONTROL_AF_REGIONS) {
return mFocusAreas.size() == 0;
} else if (setting == CONTROL_AE_TARGET_FPS_RANGE) {
- Range defaultFpsRange = mTemplateSettings.get(CONTROL_AE_TARGET_FPS_RANGE);
+ Range<Integer> defaultFpsRange = mTemplateSettings.get(CONTROL_AE_TARGET_FPS_RANGE);
return (mPreviewFpsRangeMin == 0 && mPreviewFpsRangeMax == 0) ||
(defaultFpsRange != null && mPreviewFpsRangeMin == defaultFpsRange.getLower() &&
mPreviewFpsRangeMax == defaultFpsRange.getUpper());
@@ -187,6 +228,11 @@ public class AndroidCamera2Settings extends CameraSettings {
} else if (setting == CONTROL_AWB_LOCK) {
return Objects.equals(mAutoWhiteBalanceLocked, mTemplateSettings.get(CONTROL_AWB_LOCK));
} else if (setting == JPEG_THUMBNAIL_SIZE) {
+ if (mExifThumbnailSize == null) {
+ // It doesn't matter if this is true or false since setting this
+ // to null in the request settings will use the default anyway.
+ return false;
+ }
android.util.Size defaultThumbnailSize = mTemplateSettings.get(JPEG_THUMBNAIL_SIZE);
return (mExifThumbnailSize.width() == 0 && mExifThumbnailSize.height() == 0) ||
(defaultThumbnailSize != null &&
@@ -213,7 +259,7 @@ public class AndroidCamera2Settings extends CameraSettings {
// TODO: mCurrentPreviewFormat
updateRequestSettingOrForceToDefault(JPEG_QUALITY, mJpegCompressQuality);
// TODO: mCurrentPhotoFormat
- // TODO: mCurrentZoomRatio
+ mRequestSettings.set(SCALER_CROP_REGION, mCropRectangle);
// TODO: mCurrentZoomIndex
updateRequestSettingOrForceToDefault(CONTROL_AE_EXPOSURE_COMPENSATION,
mExposureCompensationIndex);
@@ -231,10 +277,14 @@ public class AndroidCamera2Settings extends CameraSettings {
updateRequestSettingOrForceToDefault(CONTROL_AE_LOCK, mAutoExposureLocked);
updateRequestSettingOrForceToDefault(CONTROL_AWB_LOCK, mAutoWhiteBalanceLocked);
// TODO: mRecordingHintEnabled
- // TODO: mGpsData
- updateRequestSettingOrForceToDefault(JPEG_THUMBNAIL_SIZE,
- new android.util.Size(
- mExifThumbnailSize.width(), mExifThumbnailSize.height()));
+ updateRequestGpsData();
+ if (mExifThumbnailSize != null) {
+ updateRequestSettingOrForceToDefault(JPEG_THUMBNAIL_SIZE,
+ new android.util.Size(
+ mExifThumbnailSize.width(), mExifThumbnailSize.height()));
+ } else {
+ updateRequestSettingOrForceToDefault(JPEG_THUMBNAIL_SIZE, null);
+ }
return mRequestSettings;
}
@@ -243,25 +293,25 @@ public class AndroidCamera2Settings extends CameraSettings {
List<android.hardware.Camera.Area> reference) {
MeteringRectangle[] transformed = null;
if (reference.size() > 0) {
-
transformed = new MeteringRectangle[reference.size()];
for (int index = 0; index < reference.size(); ++index) {
android.hardware.Camera.Area source = reference.get(index);
Rect rectangle = source.rect;
// Old API coordinates were [-1000,1000]; new ones are [0,ACTIVE_ARRAY_SIZE).
+ // We're also going from preview image--relative to sensor active array--relative.
double oldLeft = (rectangle.left + 1000) / 2000.0;
double oldTop = (rectangle.top + 1000) / 2000.0;
double oldRight = (rectangle.right + 1000) / 2000.0;
double oldBottom = (rectangle.bottom + 1000) / 2000.0;
- int left = toIntConstrained( mActiveArray.width() * oldLeft + mActiveArray.left,
- 0, mActiveArray.width() - 1);
- int top = toIntConstrained( mActiveArray.height() * oldTop + mActiveArray.top,
- 0, mActiveArray.height() - 1);
- int right = toIntConstrained( mActiveArray.width() * oldRight + mActiveArray.left,
- 0, mActiveArray.width() - 1);
- int bottom = toIntConstrained( mActiveArray.height() * oldBottom + mActiveArray.top,
- 0, mActiveArray.height() - 1);
+ int left = mCropRectangle.left + toIntConstrained(
+ mCropRectangle.width() * oldLeft, 0, mCropRectangle.width() - 1);
+ int top = mCropRectangle.top + toIntConstrained(
+ mCropRectangle.height() * oldTop, 0, mCropRectangle.height() - 1);
+ int right = mCropRectangle.left + toIntConstrained(
+ mCropRectangle.width() * oldRight, 0, mCropRectangle.width() - 1);
+ int bottom = mCropRectangle.top + toIntConstrained(
+ mCropRectangle.height() * oldBottom, 0, mCropRectangle.height() - 1);
transformed[index] = new MeteringRectangle(left, top, right - left, bottom - top,
source.weight);
}
@@ -378,7 +428,10 @@ public class AndroidCamera2Settings extends CameraSettings {
mode = CONTROL_SCENE_MODE_FIREWORKS;
break;
}
- // TODO: We cannot support HDR
+ case HDR: {
+ mode = LegacyVendorTags.CONTROL_SCENE_MODE_HDR;
+ break;
+ }
case LANDSCAPE: {
mode = CONTROL_SCENE_MODE_LANDSCAPE;
break;
@@ -469,4 +522,65 @@ public class AndroidCamera2Settings extends CameraSettings {
}
mRequestSettings.set(CONTROL_AWB_MODE, mode);
}
+
+ private void updateRequestGpsData() {
+ if (mGpsData == null || mGpsData.processingMethod == null) {
+ // It's a hack since we always use GPS time stamp but does
+ // not use other fields sometimes. Setting processing
+ // method to null means the other fields should not be used.
+ mRequestSettings.set(JPEG_GPS_LOCATION, null);
+ } else {
+ Location location = new Location(mGpsData.processingMethod);
+ location.setTime(mGpsData.timeStamp);
+ location.setAltitude(mGpsData.altitude);
+ location.setLatitude(mGpsData.latitude);
+ location.setLongitude(mGpsData.longitude);
+ mRequestSettings.set(JPEG_GPS_LOCATION, location);
+ }
+ }
+
+ /**
+ * Calculate the effective crop rectangle for this preview viewport;
+ * assumes the preview is centered to the sensor and scaled to fit across one of the dimensions
+ * without skewing.
+ *
+ * <p>Assumes the zoom level of the provided desired crop rectangle.</p>
+ *
+ * @param requestedCrop Desired crop rectangle, in active array space.
+ * @param previewSize Size of the preview buffer render target, in pixels (not in sensor space).
+ * @return A rectangle that serves as the preview stream's effective crop region (unzoomed), in
+ * sensor space.
+ *
+ * @throws NullPointerException
+ * If any of the args were {@code null}.
+ */
+ private static Rect effectiveCropRectFromRequested(Rect requestedCrop, Size previewSize) {
+ float aspectRatioArray = requestedCrop.width() * 1.0f / requestedCrop.height();
+ float aspectRatioPreview = previewSize.width() * 1.0f / previewSize.height();
+
+ float cropHeight, cropWidth;
+ if (aspectRatioPreview < aspectRatioArray) {
+ // The new width must be smaller than the height, so scale the width by AR
+ cropHeight = requestedCrop.height();
+ cropWidth = cropHeight * aspectRatioPreview;
+ } else {
+ // The new height must be smaller (or equal) than the width, so scale the height by AR
+ cropWidth = requestedCrop.width();
+ cropHeight = cropWidth / aspectRatioPreview;
+ }
+
+ Matrix translateMatrix = new Matrix();
+ RectF cropRect = new RectF(/*left*/0, /*top*/0, cropWidth, cropHeight);
+
+ // Now center the crop rectangle so its center is in the center of the active array
+ translateMatrix.setTranslate(requestedCrop.exactCenterX(), requestedCrop.exactCenterY());
+ translateMatrix.postTranslate(-cropRect.centerX(), -cropRect.centerY());
+
+ translateMatrix.mapRect(/*inout*/cropRect);
+
+ // Round the rect corners towards the nearest integer values
+ Rect result = new Rect();
+ cropRect.roundOut(result);
+ return result;
+ }
}