Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 8 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
5 changes: 5 additions & 0 deletions packages/camera/camera_android/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 0.10.0+1

* Fixes zoom computation for virtual cameras hiding physical cameras in Android 11+.
* Removes the unused CameraZoom class from the codebase.

## 0.10.0

* **Breaking Change** Updates Android camera access permission error codes to be consistent with other platforms. If your app still handles the legacy `cameraPermission` exception, please update it to handle the new permission exception codes that are noted in the README.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,10 +150,32 @@ public interface CameraProperties {
* android.hardware.camera2.CameraCharacteristics#SCALER_AVAILABLE_MAX_DIGITAL_ZOOM key.
*
* @return Float Maximum ratio between both active area width and crop region width, and active
* area height and crop region height
* area height and crop region height.
*/
Float getScalerAvailableMaxDigitalZoom();

/**
* Returns the minimum ratio between the default camera zoom setting and all of the available zoom
*
* <p>By default maps to the @see
* android.hardware.camera2.CameraCharacteristics#CONTROL_ZOOM_RATIO_RANGE key's lower value.
*
* @return Float Minimum ratio between the default zoom ratio and the maximum possible zoom
*/
@RequiresApi(api = VERSION_CODES.R)
Float getScalerMinZoomRatio();

/**
* Returns the maximum ratio between the default camera zoom setting and all of the available zoom
*
* <p>By default maps to the @see
* android.hardware.camera2.CameraCharacteristics#CONTROL_ZOOM_RATIO_RANGE key's upper value.
*
* @return Float Maximum ratio between the default zoom ratio and the maximum possible zoom
*/
@RequiresApi(api = VERSION_CODES.R)
Float getScalerMaxZoomRatio();

/**
* Returns the area of the image sensor which corresponds to active pixels after any geometric
* distortion correction has been applied.
Expand Down Expand Up @@ -315,6 +337,18 @@ public Float getScalerAvailableMaxDigitalZoom() {
return cameraCharacteristics.get(CameraCharacteristics.SCALER_AVAILABLE_MAX_DIGITAL_ZOOM);
}

@RequiresApi(api = VERSION_CODES.R)
@Override
public Float getScalerMaxZoomRatio() {
return cameraCharacteristics.get(CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE).getUpper();
}

@RequiresApi(api = VERSION_CODES.R)
@Override
public Float getScalerMinZoomRatio() {
return cameraCharacteristics.get(CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE).getLower();
}

@Override
public Rect getSensorInfoActiveArraySize() {
return cameraCharacteristics.get(CameraCharacteristics.SENSOR_INFO_ACTIVE_ARRAY_SIZE);
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,18 @@

import android.graphics.Rect;
import android.hardware.camera2.CaptureRequest;
import android.os.Build;
import io.flutter.plugins.camera.CameraProperties;
import io.flutter.plugins.camera.features.CameraFeature;

/** Controls the zoom configuration on the {@link android.hardware.camera2} API. */
public class ZoomLevelFeature extends CameraFeature<Float> {
private static final float MINIMUM_ZOOM_LEVEL = 1.0f;
private static final Float defaultZoomLevel = 1.0f;
private final boolean hasSupport;
private final Rect sensorArraySize;
private Float currentSetting = MINIMUM_ZOOM_LEVEL;
private Float maximumZoomLevel = MINIMUM_ZOOM_LEVEL;
private Float currentSetting = defaultZoomLevel;
private Float minimumZoomLevel = currentSetting;
private Float maximumZoomLevel;

/**
* Creates a new instance of the {@link ZoomLevelFeature}.
Expand All @@ -28,18 +30,24 @@ public ZoomLevelFeature(CameraProperties cameraProperties) {
sensorArraySize = cameraProperties.getSensorInfoActiveArraySize();

if (sensorArraySize == null) {
maximumZoomLevel = MINIMUM_ZOOM_LEVEL;
maximumZoomLevel = minimumZoomLevel;
hasSupport = false;
return;
}
// On Android 11+ CONTROL_ZOOM_RATIO_RANGE should be use to get the zoom ratio directly as minimum zoom does not have to be 1.0f
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
minimumZoomLevel = cameraProperties.getScalerMinZoomRatio();
maximumZoomLevel = cameraProperties.getScalerMaxZoomRatio();
} else {
minimumZoomLevel = defaultZoomLevel;
Float maxDigitalZoom = cameraProperties.getScalerAvailableMaxDigitalZoom();
maximumZoomLevel =
((maxDigitalZoom == null) || (maxDigitalZoom < minimumZoomLevel))
? minimumZoomLevel
: maxDigitalZoom;
}

Float maxDigitalZoom = cameraProperties.getScalerAvailableMaxDigitalZoom();
maximumZoomLevel =
((maxDigitalZoom == null) || (maxDigitalZoom < MINIMUM_ZOOM_LEVEL))
? MINIMUM_ZOOM_LEVEL
: maxDigitalZoom;

hasSupport = (Float.compare(maximumZoomLevel, MINIMUM_ZOOM_LEVEL) > 0);
hasSupport = (Float.compare(maximumZoomLevel, minimumZoomLevel) > 0);
}

@Override
Expand Down Expand Up @@ -67,11 +75,19 @@ public void updateBuilder(CaptureRequest.Builder requestBuilder) {
if (!checkIsSupported()) {
return;
}

final Rect computedZoom =
ZoomUtils.computeZoom(
currentSetting, sensorArraySize, MINIMUM_ZOOM_LEVEL, maximumZoomLevel);
requestBuilder.set(CaptureRequest.SCALER_CROP_REGION, computedZoom);
// On Android 11+ CONTROL_ZOOM_RATIO can be set to a zoom ratio and the camera feed will compute
// how to zoom on its own accounting for multiple logical cameras.
// Prior the image cropping window must be calculated and set manually.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
requestBuilder.set(
CaptureRequest.CONTROL_ZOOM_RATIO,
ZoomUtils.computeZoomRatio(currentSetting, minimumZoomLevel, maximumZoomLevel));
} else {
final Rect computedZoom =
ZoomUtils.computeZoomRect(
currentSetting, sensorArraySize, minimumZoomLevel, maximumZoomLevel);
requestBuilder.set(CaptureRequest.SCALER_CROP_REGION, computedZoom);
}
}

/**
Expand All @@ -80,7 +96,7 @@ public void updateBuilder(CaptureRequest.Builder requestBuilder) {
* @return The minimum zoom level.
*/
public float getMinimumZoomLevel() {
return MINIMUM_ZOOM_LEVEL;
return minimumZoomLevel;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,18 @@ final class ZoomUtils {
* Computes an image sensor area based on the supplied zoom settings.
*
* <p>The returned image sensor area can be applied to the {@link android.hardware.camera2} API in
* order to control zoom levels.
* order to control zoom levels. This method of zoom should only be used for Android versions
* <= 11 as past that, the newer {@link #computeZoomRatio()} functional can be used.
*
* @param zoom The desired zoom level.
* @param sensorArraySize The current area of the image sensor.
* @param minimumZoomLevel The minimum supported zoom level.
* @param maximumZoomLevel The maximim supported zoom level.
* @return An image sensor area based on the supplied zoom settings
*/
static Rect computeZoom(
static Rect computeZoomRect(
float zoom, @NonNull Rect sensorArraySize, float minimumZoomLevel, float maximumZoomLevel) {
final float newZoom = MathUtils.clamp(zoom, minimumZoomLevel, maximumZoomLevel);
final float newZoom = computeZoomRatio(zoom, minimumZoomLevel, maximumZoomLevel);

final int centerX = sensorArraySize.width() / 2;
final int centerY = sensorArraySize.height() / 2;
Expand All @@ -37,4 +38,8 @@ static Rect computeZoom(

return new Rect(centerX - deltaX, centerY - deltaY, centerX + deltaX, centerY + deltaY);
}

static Float computeZoomRatio(float zoom, float minimumZoomLevel, float maximumZoomLevel) {
return MathUtils.clamp(zoom, minimumZoomLevel, maximumZoomLevel);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,30 @@ public void getScalerAvailableMaxDigitalZoomTest() {
assertEquals(actualDigitalZoom, expectedDigitalZoom);
}

@Test
public void getScalerGetScalerMinZoomRatioTest() {
Range zoomRange = mock(Range.class);
when(mockCharacteristics.get(CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE))
.thenReturn(zoomRange);

Float minZoom = cameraProperties.getScalerMinZoomRatio();

verify(mockCharacteristics, times(1)).get(CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE);
assertEquals(zoomRange.getLower(), minZoom);
}

@Test
public void getScalerGetScalerMaxZoomRatioTest() {
Range zoomRange = mock(Range.class);
when(mockCharacteristics.get(CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE))
.thenReturn(zoomRange);

Float maxZoom = cameraProperties.getScalerMaxZoomRatio();

verify(mockCharacteristics, times(1)).get(CameraCharacteristics.CONTROL_ZOOM_RATIO_RANGE);
assertEquals(zoomRange.getUpper(), maxZoom);
}

@Test
public void getSensorInfoActiveArraySizeTest() {
Rect expectedArraySize = mock(Rect.class);
Expand Down

This file was deleted.

Loading