Skip to content
This repository was 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
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
}

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