Skip to content
This repository has been archived by the owner on Feb 22, 2023. It is now read-only.

[camera] android-rework part 3: Android exposure related features #3797

Merged
merged 13 commits into from
May 27, 2021
Merged
2 changes: 1 addition & 1 deletion packages/camera/camera/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ android {
dependencies {
compileOnly 'androidx.annotation:annotation:1.1.0'
testImplementation 'junit:junit:4.12'
testImplementation 'org.mockito:mockito-core:3.5.13'
testImplementation 'org.mockito:mockito-inline:3.5.13'
testImplementation 'androidx.test:core:1.3.0'
testImplementation 'org.robolectric:robolectric:4.3'
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
* @param <T>
*/
public abstract class CameraFeature<T> {

protected final CameraProperties cameraProperties;

protected CameraFeature(@NonNull CameraProperties cameraProperties) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package io.flutter.plugins.camera.features;

/** Represents a point on an x/y axis. */
public class Point {
public final Double x;
public final Double y;

public Point(Double x, Double y) {
this.x = x;
this.y = y;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ public enum FocusMode {

public static FocusMode getValueForString(String modeStr) {
for (FocusMode value : values()) {
if (value.strValue.equals(modeStr)) return value;
if (value.strValue.equals(modeStr)) {
return value;
}
}
return null;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package io.flutter.plugins.camera.features.exposurelock;

import android.hardware.camera2.CaptureRequest;
import io.flutter.plugins.camera.CameraProperties;
import io.flutter.plugins.camera.features.CameraFeature;

/** Controls whether or not the exposure mode is currently locked or automatically metering. */
public class ExposureLockFeature extends CameraFeature<ExposureMode> {

private ExposureMode currentSetting = ExposureMode.auto;

/**
* Creates a new instance of the {@see ExposureLockFeature}.
*
* @param cameraProperties Collection of the characteristics for the current camera device.
*/
public ExposureLockFeature(CameraProperties cameraProperties) {
super(cameraProperties);
}

@Override
public String getDebugName() {
return "ExposureLockFeature";
}

@Override
public ExposureMode getValue() {
return currentSetting;
}

@Override
public void setValue(ExposureMode value) {
this.currentSetting = value;
}

// Available on all devices.
@Override
public boolean checkIsSupported() {
return true;
}

@Override
public void updateBuilder(CaptureRequest.Builder requestBuilder) {
if (!checkIsSupported()) {
return;
}

requestBuilder.set(CaptureRequest.CONTROL_AE_LOCK, currentSetting == ExposureMode.locked);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package io.flutter.plugins.camera.features.exposurelock;

// Mirrors exposure_mode.dart
public enum ExposureMode {
auto("auto"),
locked("locked");

private final String strValue;

ExposureMode(String strValue) {
this.strValue = strValue;
}

/**
* Tries to convert the supplied string into an {@see ExposureMode} enum value.
*
* <p>When the supplied string doesn't match a valid {@see ExposureMode} enum value, null is
* returned.
*
* @param modeStr String value to convert into an {@see ExposureMode} enum value.
* @return Matching {@see ExposureMode} enum value, or null if no match is found.
*/
public static ExposureMode getValueForString(String modeStr) {
for (ExposureMode value : values()) {
if (value.strValue.equals(modeStr)) {
return value;
}
}
return null;
}

@Override
public String toString() {
return strValue;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package io.flutter.plugins.camera.features.exposureoffset;

import android.hardware.camera2.CaptureRequest;
import android.util.Range;
import androidx.annotation.NonNull;
import io.flutter.plugins.camera.CameraProperties;
import io.flutter.plugins.camera.features.CameraFeature;

/** Controls the exposure offset making the resulting image brighter or darker. */
public class ExposureOffsetFeature extends CameraFeature<Double> {

private double currentSetting = 0;

/**
* Creates a new instance of the {@link ExposureOffsetFeature}.
*
* @param cameraProperties Collection of the characteristics for the current camera device.
*/
public ExposureOffsetFeature(CameraProperties cameraProperties) {
super(cameraProperties);
}

@Override
public String getDebugName() {
return "ExposureOffsetFeature";
}

@Override
public Double getValue() {
return currentSetting;
}

@Override
public void setValue(@NonNull Double value) {
double stepSize = getExposureOffsetStepSize();
this.currentSetting = value / stepSize;
}

// Available on all devices.
@Override
public boolean checkIsSupported() {
return true;
}

@Override
public void updateBuilder(CaptureRequest.Builder requestBuilder) {
if (!checkIsSupported()) {
return;
}

requestBuilder.set(CaptureRequest.CONTROL_AE_EXPOSURE_COMPENSATION, (int) currentSetting);
}

/**
* Returns the minimum exposure offset.
*
* @return double Minimum exposure offset.
*/
public double getMinExposureOffset() {
Range<Integer> range = cameraProperties.getControlAutoExposureCompensationRange();
double minStepped = range == null ? 0 : range.getLower();
double stepSize = getExposureOffsetStepSize();
return minStepped * stepSize;
}

/**
* Returns the maximum exposure offset.
*
* @return double Maximum exposure offset.
*/
public double getMaxExposureOffset() {
Range<Integer> range = cameraProperties.getControlAutoExposureCompensationRange();
double maxStepped = range == null ? 0 : range.getUpper();
double stepSize = getExposureOffsetStepSize();
return maxStepped * stepSize;
}

/**
* Returns the smallest step by which the exposure compensation can be changed.
*
* <p>Example: if this has a value of 0.5, then an aeExposureCompensation setting of -2 means that
* the actual AE offset is -1. More details can be found in the official Android documentation:
* https://developer.android.com/reference/android/hardware/camera2/CameraCharacteristics.html#CONTROL_AE_COMPENSATION_STEP
*
* @return double Smallest step by which the exposure compensation can be changed.
*/
public double getExposureOffsetStepSize() {
return cameraProperties.getControlAutoExposureCompensationStep();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package io.flutter.plugins.camera.features.exposurepoint;

import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.params.MeteringRectangle;
import android.util.Log;
import androidx.annotation.NonNull;
import io.flutter.plugins.camera.CameraProperties;
import io.flutter.plugins.camera.features.CameraFeature;
import io.flutter.plugins.camera.features.Point;
import io.flutter.plugins.camera.types.CameraRegions;

/** Exposure point controls where in the frame exposure metering will come from. */
public class ExposurePointFeature extends CameraFeature<Point> {

private final CameraRegions cameraRegions;
private Point currentSetting = new Point(0.0, 0.0);

/**
* Creates a new instance of the {@link ExposurePointFeature}.
*
* @param cameraProperties Collection of the characteristics for the current camera device.
* @param cameraRegions Utility class to assist in calculating exposure boundaries.
*/
public ExposurePointFeature(CameraProperties cameraProperties, CameraRegions cameraRegions) {
super(cameraProperties);
this.cameraRegions = cameraRegions;
}

@Override
public String getDebugName() {
return "ExposurePointFeature";
}

@Override
public Point getValue() {
return currentSetting;
}

@Override
public void setValue(@NonNull Point value) {
this.currentSetting = value;

if (value.x == null || value.y == null) {
cameraRegions.resetAutoExposureMeteringRectangle();
} else {
cameraRegions.setAutoExposureMeteringRectangleFromPoint(value.x, value.y);
}
}

// Whether or not this camera can set the exposure point.
@Override
public boolean checkIsSupported() {
Integer supportedRegions = cameraProperties.getControlMaxRegionsAutoExposure();
return supportedRegions != null && supportedRegions > 0;
}

@Override
public void updateBuilder(CaptureRequest.Builder requestBuilder) {
if (!checkIsSupported()) {
return;
}

MeteringRectangle aeRect = null;
try {
aeRect = cameraRegions.getAEMeteringRectangle();
} catch (Exception e) {
Log.w("Camera", "Unable to retrieve the Auto Exposure metering rectangle.", e);
stuartmorgan marked this conversation as resolved.
Show resolved Hide resolved
}

requestBuilder.set(
CaptureRequest.CONTROL_AE_REGIONS,
aeRect == null ? null : new MeteringRectangle[] {aeRect});
}
}
Loading