diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java index 08833e1a..4d8b2c04 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Camera2Engine.java @@ -83,6 +83,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv private CaptureRequest.Builder mRepeatingRequestBuilder; private CaptureRequest mRepeatingRequest; private CameraCaptureSession.CaptureCallback mRepeatingRequestCallback; + private CaptureResult mLastRepeatingResult; private final Camera2Mapper mMapper = Camera2Mapper.get(); // Frame processing @@ -240,6 +241,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv @Override public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) { super.onCaptureCompleted(session, request, result); + mLastRepeatingResult = result; if (mPictureRecorder instanceof Full2PictureRecorder) { ((Full2PictureRecorder) mPictureRecorder).onCaptureCompleted(result); } @@ -1175,7 +1177,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv mRepeatingRequestBuilder, mCameraCharacteristics, Camera2Engine.this); - mMeter.startMetering(point, gesture); + mMeter.startMetering(mLastRepeatingResult, point, gesture); } }); } @@ -1187,9 +1189,11 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv * @param gesture gesture */ @Override - public void onMeteringStarted(@NonNull PointF point, @Nullable Gesture gesture) { + public void onMeteringStarted(@Nullable PointF point, @Nullable Gesture gesture) { LOG.w("onMeteringStarted - point:", point, "gesture:", gesture); - mCallback.dispatchOnFocusStart(gesture, point); + if (point != null) { + mCallback.dispatchOnFocusStart(gesture, point); + } applyRepeatingRequestBuilder(); } @@ -1201,9 +1205,11 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv * @param success success */ @Override - public void onMeteringEnd(@NonNull PointF point, @Nullable Gesture gesture, boolean success) { + public void onMeteringEnd(@Nullable PointF point, @Nullable Gesture gesture, boolean success) { LOG.w("onMeteringEnd - point:", point, "gesture:", gesture, "success:", success); - mCallback.dispatchOnFocusEnd(gesture, success, point); + if (point != null) { + mCallback.dispatchOnFocusEnd(gesture, success, point); + } } /** @@ -1214,7 +1220,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv * @return true if metering can be reset */ @Override - public boolean canResetMetering(@NonNull PointF point, @Nullable Gesture gesture) { + public boolean canResetMetering(@Nullable PointF point, @Nullable Gesture gesture) { return getEngineState() == STATE_STARTED; } @@ -1225,7 +1231,7 @@ public class Camera2Engine extends CameraEngine implements ImageReader.OnImageAv * @param gesture gesture */ @Override - public void onMeteringReset(@NonNull PointF point, @Nullable Gesture gesture) { + public void onMeteringReset(@Nullable PointF point, @Nullable Gesture gesture) { applyDefaultFocus(mRepeatingRequestBuilder); applyRepeatingRequestBuilder(); // only if preview started already } diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Meter.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Meter.java index 751a3c44..1381cf58 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Meter.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/Meter.java @@ -24,6 +24,7 @@ import com.otaliastudios.cameraview.gesture.Gesture; import com.otaliastudios.cameraview.size.AspectRatio; import com.otaliastudios.cameraview.size.Size; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -52,7 +53,7 @@ public class Meter { * @param point point * @param gesture gesture */ - void onMeteringStarted(@NonNull PointF point, @Nullable Gesture gesture); + void onMeteringStarted(@Nullable PointF point, @Nullable Gesture gesture); /** * Notifies that metering has ended. No action is required for implementors. @@ -62,16 +63,16 @@ public class Meter { * @param gesture gesture * @param success success */ - void onMeteringEnd(@NonNull PointF point, @Nullable Gesture gesture, boolean success); + void onMeteringEnd(@Nullable PointF point, @Nullable Gesture gesture, boolean success); /** * Notifies that metering has been reset. From now on, this meter instance * is done, although in theory it could be reused by calling - * {@link #startMetering(PointF, Gesture)} again. + * {@link #startMetering(CaptureResult, PointF, Gesture)} again. * @param point point * @param gesture gesture */ - void onMeteringReset(@NonNull PointF point, @Nullable Gesture gesture); + void onMeteringReset(@Nullable PointF point, @Nullable Gesture gesture); /** * Whether metering can be reset. Since it happens at a future time, this should @@ -81,7 +82,7 @@ public class Meter { * @return true if can reset */ // TODO is this useful? engine could do its checks onMeteringReset() - boolean canResetMetering(@NonNull PointF point, @Nullable Gesture gesture); + boolean canResetMetering(@Nullable PointF point, @Nullable Gesture gesture); } private static final String TAG = Meter.class.getSimpleName(); @@ -121,53 +122,61 @@ public class Meter { /** * Starts a metering sequence. + * @param lastResult the last result * @param point point * @param gesture gesture */ @SuppressWarnings("WeakerAccess") - public void startMetering(@NonNull PointF point, @Nullable Gesture gesture) { + public void startMetering(@NonNull CaptureResult lastResult, @Nullable PointF point, @Nullable Gesture gesture) { mPoint = point; mGesture = gesture; mIsMetering = true; - // This is a good Q/A. https://stackoverflow.com/a/33181620/4288782 - // At first, the point is relative to the View system and does not account our own cropping. - // Will keep updating these two below. - final PointF referencePoint = new PointF(mPoint.x, mPoint.y); - Size referenceSize = mEngine.mPreview.getSurfaceSize(); - - // 1. Account for cropping. - // This will enlarge the preview size so that aspect ratio matches. - referenceSize = applyPreviewCropping(referenceSize, referencePoint); - - // 2. Scale to the preview stream coordinates. - // This will move to the preview stream coordinates by scaling. - referenceSize = applyPreviewScale(referenceSize, referencePoint); - - // 3. Rotate to the stream coordinate system. - // This leaves us with sensor stream coordinates. - referenceSize = applyPreviewToSensorRotation(referenceSize, referencePoint); - - // 4. Move to the crop region coordinate system. - // The crop region is the union of all currently active streams. - referenceSize = applyCropRegionCoordinates(referenceSize, referencePoint); - - // 5. Move to the active array coordinate system. - referenceSize = applyActiveArrayCoordinates(referenceSize, referencePoint); - - // 6. Now we can compute the metering regions. - // We want to define them as a fraction of the visible size which (apart from cropping) - // can be obtained through the SENSOR rotated preview stream size. - Size visibleSize = mEngine.getPreviewStreamSize(Reference.SENSOR); - //noinspection ConstantConditions - MeteringRectangle area1 = createMeteringRectangle(referenceSize, referencePoint, visibleSize, 0.05F, 1000); - MeteringRectangle area2 = createMeteringRectangle(referenceSize, referencePoint, visibleSize, 0.1F, 100); - List areas = Arrays.asList(area1, area2); + List areas = new ArrayList<>(); + if (point != null) { + // This is a good Q/A. https://stackoverflow.com/a/33181620/4288782 + // At first, the point is relative to the View system and does not account our own cropping. + // Will keep updating these two below. + final PointF referencePoint = new PointF(mPoint.x, mPoint.y); + Size referenceSize = mEngine.mPreview.getSurfaceSize(); + + // 1. Account for cropping. + // This will enlarge the preview size so that aspect ratio matches. + referenceSize = applyPreviewCropping(referenceSize, referencePoint); + + // 2. Scale to the preview stream coordinates. + // This will move to the preview stream coordinates by scaling. + referenceSize = applyPreviewScale(referenceSize, referencePoint); + + // 3. Rotate to the stream coordinate system. + // This leaves us with sensor stream coordinates. + referenceSize = applyPreviewToSensorRotation(referenceSize, referencePoint); + + // 4. Move to the crop region coordinate system. + // The crop region is the union of all currently active streams. + referenceSize = applyCropRegionCoordinates(referenceSize, referencePoint); + + // 5. Move to the active array coordinate system. + referenceSize = applyActiveArrayCoordinates(referenceSize, referencePoint); + + // 6. Now we can compute the metering regions. + // We want to define them as a fraction of the visible size which (apart from cropping) + // can be obtained through the SENSOR rotated preview stream size. + Size visibleSize = mEngine.getPreviewStreamSize(Reference.SENSOR); + //noinspection ConstantConditions + MeteringRectangle area1 = createMeteringRectangle(referenceSize, referencePoint, + visibleSize, 0.05F, 1000); + MeteringRectangle area2 = createMeteringRectangle(referenceSize, referencePoint, + visibleSize, 0.1F, 100); + areas.add(area1); + areas.add(area2); + } // 7. And finally dispatch everything - mAutoFocus.startMetering(mCharacteristics, mBuilder, areas); - mAutoWhiteBalance.startMetering(mCharacteristics, mBuilder, areas); - mAutoExposure.startMetering(mCharacteristics, mBuilder, areas); + boolean skipIfPossible = mPoint == null; + mAutoFocus.startMetering(mCharacteristics, mBuilder, areas, lastResult, skipIfPossible); + mAutoWhiteBalance.startMetering(mCharacteristics, mBuilder, areas, lastResult, skipIfPossible); + mAutoExposure.startMetering(mCharacteristics, mBuilder, areas, lastResult, skipIfPossible); // Dispatch to callback mCallback.onMeteringStarted(mPoint, mGesture); @@ -310,7 +319,7 @@ public class Meter { /** * True if we're metering. False if we're not, for example if we're waiting for - * a reset call, or if {@link #startMetering(PointF, Gesture)} was never called. + * a reset call, or if {@link #startMetering(CaptureResult, PointF, Gesture)} was never called. * @return true if metering */ @SuppressWarnings("WeakerAccess") @@ -361,12 +370,16 @@ public class Meter { mEngine.mHandler.remove(mResetRunnable); if (mCallback.canResetMetering(mPoint, mGesture)) { LOG.i("Resetting the meter parameters."); - Rect whole = mCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE); - if (whole == null) whole = new Rect(); - MeteringRectangle rectangle = new MeteringRectangle(whole, MeteringRectangle.METERING_WEIGHT_DONT_CARE); - mAutoFocus.resetMetering(mCharacteristics, mBuilder, rectangle); - mAutoWhiteBalance.resetMetering(mCharacteristics, mBuilder, rectangle); - mAutoExposure.resetMetering(mCharacteristics, mBuilder, rectangle); + MeteringRectangle whole = null; + if (mPoint != null) { + // If we have a point, we must reset the metering areas. + Rect wholeRect = mCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE); + if (wholeRect == null) wholeRect = new Rect(); + whole = new MeteringRectangle(wholeRect, MeteringRectangle.METERING_WEIGHT_DONT_CARE); + } + mAutoFocus.resetMetering(mCharacteristics, mBuilder, whole); + mAutoWhiteBalance.resetMetering(mCharacteristics, mBuilder, whole); + mAutoExposure.resetMetering(mCharacteristics, mBuilder, whole); mCallback.onMeteringReset(mPoint, mGesture); } } diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/metering/AutoExposure.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/metering/AutoExposure.java index 3641f450..3b36e6c5 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/metering/AutoExposure.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/metering/AutoExposure.java @@ -7,6 +7,7 @@ import android.hardware.camera2.params.MeteringRectangle; import android.os.Build; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import com.otaliastudios.cameraview.CameraLogger; @@ -22,13 +23,9 @@ public class AutoExposure extends MeteringParameter { private boolean isStarted; @Override - public void startMetering(@NonNull CameraCharacteristics characteristics, - @NonNull CaptureRequest.Builder builder, - @NonNull List areas) { - isSuccessful = false; - isMetered = false; - isStarted = false; - + protected boolean checkSupportsProcessing(@NonNull CameraCharacteristics characteristics, + @NonNull CaptureRequest.Builder builder) { + // In our case, this means checking if we support the AE precapture trigger. boolean isNotLegacy = readCharacteristic(characteristics, CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL, -1) != CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY; @@ -39,9 +36,23 @@ public class AutoExposure extends MeteringParameter { || aeMode == CameraCharacteristics.CONTROL_AE_MODE_ON_AUTO_FLASH || aeMode == CameraCharacteristics.CONTROL_AE_MODE_ON_AUTO_FLASH_REDEYE || aeMode == 5 /* CameraCharacteristics.CONTROL_AE_MODE_ON_EXTERNAL_FLASH, API 28 */); - isSupported = isNotLegacy && isAEOn; + return isNotLegacy && isAEOn; + } + + @Override + protected boolean checkShouldSkip(@NonNull CaptureResult lastResult) { + Integer aeState = lastResult.get(CaptureResult.CONTROL_AE_STATE); + return aeState != null && aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED; + } - if (isSupported) { + @Override + protected void onStartMetering(@NonNull CameraCharacteristics characteristics, + @NonNull CaptureRequest.Builder builder, + @NonNull List areas, + boolean supportsProcessing) { + isStarted = false; + + if (supportsProcessing) { builder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_START); } @@ -49,16 +60,16 @@ public class AutoExposure extends MeteringParameter { // Even if precapture is not supported, check the regions anyway. int maxRegions = readCharacteristic(characteristics, CameraCharacteristics.CONTROL_MAX_REGIONS_AE, 0); - if (maxRegions > 0) { + if (!areas.isEmpty() && maxRegions > 0) { int max = Math.min(maxRegions, areas.size()); builder.set(CaptureRequest.CONTROL_AE_REGIONS, areas.subList(0, max).toArray(new MeteringRectangle[]{})); } } + @Override - public void onCapture(@NonNull CaptureResult result) { - if (isMetered || !isSupported) return; + public void processCapture(@NonNull CaptureResult result) { Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE); LOG.i("onCapture:", "aeState:", aeState); if (aeState == null) return; @@ -82,15 +93,16 @@ public class AutoExposure extends MeteringParameter { } @Override - public void resetMetering(@NonNull CameraCharacteristics characteristics, - @NonNull CaptureRequest.Builder builder, - @NonNull MeteringRectangle area) { + protected void onResetMetering(@NonNull CameraCharacteristics characteristics, + @NonNull CaptureRequest.Builder builder, + @Nullable MeteringRectangle area, + boolean supportsProcessing) { int maxRegions = readCharacteristic(characteristics, CameraCharacteristics.CONTROL_MAX_REGIONS_AE, 0); - if (maxRegions > 0) { + if (area != null && maxRegions > 0) { builder.set(CaptureRequest.CONTROL_AE_REGIONS, new MeteringRectangle[]{area}); } - if (isSupported) { + if (supportsProcessing) { // Cleanup any precapture sequence. if (Build.VERSION.SDK_INT >= 23) { builder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER, diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/metering/AutoFocus.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/metering/AutoFocus.java index 6c61639c..0d98d272 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/metering/AutoFocus.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/metering/AutoFocus.java @@ -7,6 +7,7 @@ import android.hardware.camera2.params.MeteringRectangle; import android.os.Build; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import com.otaliastudios.cameraview.CameraLogger; @@ -20,36 +21,44 @@ public class AutoFocus extends MeteringParameter { private static final CameraLogger LOG = CameraLogger.create(TAG); @Override - public void startMetering(@NonNull CameraCharacteristics characteristics, - @NonNull CaptureRequest.Builder builder, - @NonNull List areas) { - isSuccessful = false; - isMetered = false; - - Integer afMode = builder.get(CaptureRequest.CONTROL_AF_MODE); + protected boolean checkSupportsProcessing(@NonNull CameraCharacteristics characteristics, + @NonNull CaptureRequest.Builder builder) { // Exclude OFF and EDOF as per docs. - isSupported = afMode != null && + Integer afMode = builder.get(CaptureRequest.CONTROL_AF_MODE); + return afMode != null && (afMode == CameraCharacteristics.CONTROL_AF_MODE_AUTO - || afMode == CameraCharacteristics.CONTROL_AF_MODE_CONTINUOUS_PICTURE - || afMode == CameraCharacteristics.CONTROL_AF_MODE_CONTINUOUS_VIDEO - || afMode == CameraCharacteristics.CONTROL_AF_MODE_MACRO); - if (isSupported) { + || afMode == CameraCharacteristics.CONTROL_AF_MODE_CONTINUOUS_PICTURE + || afMode == CameraCharacteristics.CONTROL_AF_MODE_CONTINUOUS_VIDEO + || afMode == CameraCharacteristics.CONTROL_AF_MODE_MACRO); + } + + @Override + protected boolean checkShouldSkip(@NonNull CaptureResult lastResult) { + Integer afState = lastResult.get(CaptureResult.CONTROL_AF_STATE); + return afState != null && afState == CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED; + } + + @Override + protected void onStartMetering(@NonNull CameraCharacteristics characteristics, + @NonNull CaptureRequest.Builder builder, + @NonNull List areas, + boolean supportsProcessing) { + if (supportsProcessing) { builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_START); } // Even if auto is not supported, change the regions anyway. - int maxRegions = readCharacteristic(characteristics, CameraCharacteristics.CONTROL_MAX_REGIONS_AF, 0); - if (maxRegions > 0) { + int maxRegions = readCharacteristic(characteristics, + CameraCharacteristics.CONTROL_MAX_REGIONS_AF, 0); + if (!areas.isEmpty() && maxRegions > 0) { int max = Math.min(maxRegions, areas.size()); builder.set(CaptureRequest.CONTROL_AF_REGIONS, areas.subList(0, max).toArray(new MeteringRectangle[]{})); } - } @Override - public void onCapture(@NonNull CaptureResult result) { - if (isMetered || !isSupported) return; + public void processCapture(@NonNull CaptureResult result) { Integer afState = result.get(CaptureResult.CONTROL_AF_STATE); LOG.i("onCapture:", "afState:", afState); if (afState == null) return; @@ -71,16 +80,17 @@ public class AutoFocus extends MeteringParameter { } @Override - public void resetMetering(@NonNull CameraCharacteristics characteristics, - @NonNull CaptureRequest.Builder builder, - @NonNull MeteringRectangle area) { + protected void onResetMetering(@NonNull CameraCharacteristics characteristics, + @NonNull CaptureRequest.Builder builder, + @Nullable MeteringRectangle area, + boolean supportsProcessing) { int maxRegions = readCharacteristic(characteristics, CameraCharacteristics.CONTROL_MAX_REGIONS_AF, 0); - if (maxRegions > 0) { + if (area != null && maxRegions > 0) { builder.set(CaptureRequest.CONTROL_AF_REGIONS, new MeteringRectangle[]{area}); } - if (isSupported) { // Cleanup any trigger. + if (supportsProcessing) { // Cleanup any trigger. builder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_CANCEL); } } diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/metering/AutoWhiteBalance.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/metering/AutoWhiteBalance.java index a9eaf03c..b1ab9f53 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/metering/AutoWhiteBalance.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/metering/AutoWhiteBalance.java @@ -7,6 +7,7 @@ import android.hardware.camera2.params.MeteringRectangle; import android.os.Build; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import com.otaliastudios.cameraview.CameraLogger; @@ -20,37 +21,44 @@ public class AutoWhiteBalance extends MeteringParameter { private static final CameraLogger LOG = CameraLogger.create(TAG); @Override - public void startMetering(@NonNull CameraCharacteristics characteristics, - @NonNull CaptureRequest.Builder builder, - @NonNull List areas) { - isSuccessful = false; - isMetered = false; - + protected boolean checkSupportsProcessing(@NonNull CameraCharacteristics characteristics, + @NonNull CaptureRequest.Builder builder) { boolean isNotLegacy = readCharacteristic(characteristics, CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL, -1) != CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY; Integer awbMode = builder.get(CaptureRequest.CONTROL_AWB_MODE); - isSupported = isNotLegacy && awbMode != null && awbMode == CaptureRequest.CONTROL_AWB_MODE_AUTO; + return isNotLegacy && awbMode != null && awbMode == CaptureRequest.CONTROL_AWB_MODE_AUTO; + } - if (isSupported) { + @Override + protected boolean checkShouldSkip(@NonNull CaptureResult lastResult) { + Integer awbState = lastResult.get(CaptureResult.CONTROL_AWB_STATE); + return awbState != null && awbState == CaptureRequest.CONTROL_AWB_STATE_CONVERGED; + } + + @Override + protected void onStartMetering(@NonNull CameraCharacteristics characteristics, + @NonNull CaptureRequest.Builder builder, + @NonNull List areas, + boolean supportsProcessing) { + if (supportsProcessing) { // Remove any lock. We're not setting any, but just in case. + // This would make processing be stuck into the process method. builder.set(CaptureRequest.CONTROL_AWB_LOCK, false); } // Even if auto is not supported, change the regions anyway. int maxRegions = readCharacteristic(characteristics, CameraCharacteristics.CONTROL_MAX_REGIONS_AWB, 0); - if (maxRegions > 0) { + if (!areas.isEmpty() && maxRegions > 0) { int max = Math.min(maxRegions, areas.size()); builder.set(CaptureRequest.CONTROL_AWB_REGIONS, areas.subList(0, max).toArray(new MeteringRectangle[]{})); } - } @Override - public void onCapture(@NonNull CaptureResult result) { - if (isMetered || !isSupported) return; + public void processCapture(@NonNull CaptureResult result) { Integer awbState = result.get(CaptureResult.CONTROL_AWB_STATE); LOG.i("onCapture:", "awbState:", awbState); if (awbState == null) return; @@ -69,12 +77,13 @@ public class AutoWhiteBalance extends MeteringParameter { } @Override - public void resetMetering(@NonNull CameraCharacteristics characteristics, - @NonNull CaptureRequest.Builder builder, - @NonNull MeteringRectangle area) { + protected void onResetMetering(@NonNull CameraCharacteristics characteristics, + @NonNull CaptureRequest.Builder builder, + @Nullable MeteringRectangle area, + boolean supportsProcessing) { int maxRegions = readCharacteristic(characteristics, CameraCharacteristics.CONTROL_MAX_REGIONS_AWB, 0); - if (maxRegions > 0) { + if (area != null && maxRegions > 0) { builder.set(CaptureRequest.CONTROL_AWB_REGIONS, new MeteringRectangle[]{area}); } } diff --git a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/metering/MeteringParameter.java b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/metering/MeteringParameter.java index 2fb4a5af..ae774c8b 100644 --- a/cameraview/src/main/java/com/otaliastudios/cameraview/engine/metering/MeteringParameter.java +++ b/cameraview/src/main/java/com/otaliastudios/cameraview/engine/metering/MeteringParameter.java @@ -7,6 +7,7 @@ import android.hardware.camera2.params.MeteringRectangle; import android.os.Build; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; import java.util.List; @@ -14,15 +15,15 @@ import java.util.List; @RequiresApi(Build.VERSION_CODES.LOLLIPOP) public abstract class MeteringParameter { - @SuppressWarnings("WeakerAccess") - protected boolean isSupported; - @SuppressWarnings("WeakerAccess") protected boolean isSuccessful; @SuppressWarnings("WeakerAccess") protected boolean isMetered; + private boolean shouldSkip; + private boolean supportsProcessing; + @SuppressWarnings("WeakerAccess") @NonNull protected T readCharacteristic(@NonNull CameraCharacteristics characteristics, @@ -34,21 +35,53 @@ public abstract class MeteringParameter { public final boolean isMetered() { // A non supported parameter should always appear as metered - return isMetered || !isSupported; + return isMetered || !supportsProcessing || shouldSkip; } public final boolean isSuccessful() { // A non supported parameter should always appear as successful - return isSuccessful || !isSupported; + return isSuccessful || !supportsProcessing || shouldSkip; } - public abstract void startMetering(@NonNull CameraCharacteristics characteristics, - @NonNull CaptureRequest.Builder builder, - @NonNull List areas); + public final void startMetering(@NonNull CameraCharacteristics characteristics, + @NonNull CaptureRequest.Builder builder, + @NonNull List areas, + @NonNull CaptureResult lastResult, + boolean skipIfPossible) { + isSuccessful = false; + isMetered = false; + shouldSkip = skipIfPossible && checkShouldSkip(lastResult); + supportsProcessing = checkSupportsProcessing(characteristics, builder); + if (!shouldSkip) { + onStartMetering(characteristics, builder, areas, supportsProcessing); + } + } + + protected abstract boolean checkSupportsProcessing(@NonNull CameraCharacteristics characteristics, + @NonNull CaptureRequest.Builder builder); + + protected abstract boolean checkShouldSkip(@NonNull CaptureResult lastResult); - public abstract void resetMetering(@NonNull CameraCharacteristics characteristics, - @NonNull CaptureRequest.Builder builder, - @NonNull MeteringRectangle area); + protected abstract void onStartMetering(@NonNull CameraCharacteristics characteristics, + @NonNull CaptureRequest.Builder builder, + @NonNull List areas, + boolean supportsProcessing); + + public final void onCapture(@NonNull CaptureResult result) { + if (isMetered()) return; + processCapture(result); + } + + protected abstract void processCapture(@NonNull CaptureResult result); + + public final void resetMetering(@NonNull CameraCharacteristics characteristics, + @NonNull CaptureRequest.Builder builder, + @Nullable MeteringRectangle area) { + onResetMetering(characteristics, builder, area, supportsProcessing); + } - public abstract void onCapture(@NonNull CaptureResult result); + protected abstract void onResetMetering(@NonNull CameraCharacteristics characteristics, + @NonNull CaptureRequest.Builder builder, + @Nullable MeteringRectangle area, + boolean supportsProcessing); }