diff options
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.java | 160 |
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; + } } |