Camera: Make sure metering regions are valid in capture result
- Make sure ZoomRatioMapper doesn't produce metering rectangles
with negative width/height
- If HAL or any framework component produces metering rectangles
with negative width/height, overwrite it to have 0 width/height.
Test: manual testing, cameraservice_test
Bug: 243985907
Change-Id: I04ca487c64f7db3e7ece209def2d1d03bc4a4238
diff --git a/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp b/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp
index f4e3fad..5d3da45 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputUtils.cpp
@@ -128,12 +128,41 @@
return res;
}
+void correctMeteringRegions(camera_metadata_t *meta) {
+ if (meta == nullptr) return;
+
+ uint32_t meteringRegionKeys[] = {
+ ANDROID_CONTROL_AE_REGIONS,
+ ANDROID_CONTROL_AWB_REGIONS,
+ ANDROID_CONTROL_AF_REGIONS };
+
+ for (uint32_t key : meteringRegionKeys) {
+ camera_metadata_entry_t entry;
+ int res = find_camera_metadata_entry(meta, key, &entry);
+ if (res != OK) continue;
+
+ for (size_t i = 0; i < entry.count; i += 5) {
+ if (entry.data.i32[0] > entry.data.i32[2]) {
+ ALOGW("%s: Invalid metering region (%d): left: %d, right: %d",
+ __FUNCTION__, key, entry.data.i32[0], entry.data.i32[2]);
+ entry.data.i32[2] = entry.data.i32[0];
+ }
+ if (entry.data.i32[1] > entry.data.i32[3]) {
+ ALOGW("%s: Invalid metering region (%d): top: %d, bottom: %d",
+ __FUNCTION__, key, entry.data.i32[1], entry.data.i32[3]);
+ entry.data.i32[3] = entry.data.i32[1];
+ }
+ }
+ }
+}
+
void insertResultLocked(CaptureOutputStates& states, CaptureResult *result, uint32_t frameNumber) {
if (result == nullptr) return;
camera_metadata_t *meta = const_cast<camera_metadata_t *>(
result->mMetadata.getAndLock());
set_camera_metadata_vendor_id(meta, states.vendorTagId);
+ correctMeteringRegions(meta);
result->mMetadata.unlock(meta);
if (result->mMetadata.update(ANDROID_REQUEST_FRAME_COUNT,
@@ -152,6 +181,7 @@
camera_metadata_t *pmeta = const_cast<camera_metadata_t *>(
physicalMetadata.mPhysicalCameraMetadata.getAndLock());
set_camera_metadata_vendor_id(pmeta, states.vendorTagId);
+ correctMeteringRegions(pmeta);
physicalMetadata.mPhysicalCameraMetadata.unlock(pmeta);
}
diff --git a/services/camera/libcameraservice/device3/ZoomRatioMapper.cpp b/services/camera/libcameraservice/device3/ZoomRatioMapper.cpp
index 27b00c9..515259e 100644
--- a/services/camera/libcameraservice/device3/ZoomRatioMapper.cpp
+++ b/services/camera/libcameraservice/device3/ZoomRatioMapper.cpp
@@ -354,17 +354,8 @@
if (weight == 0) {
continue;
}
- // Top left (inclusive)
- scaleCoordinates(entry.data.i32 + j, 1, zoomRatio, true /*clamp*/, arrayWidth,
+ scaleRegion(entry.data.i32 + j, zoomRatio, arrayWidth,
arrayHeight);
- // Bottom right (exclusive): Use adjacent inclusive pixel to
- // calculate.
- entry.data.i32[j+2] -= 1;
- entry.data.i32[j+3] -= 1;
- scaleCoordinates(entry.data.i32 + j + 2, 1, zoomRatio, true /*clamp*/, arrayWidth,
- arrayHeight);
- entry.data.i32[j+2] += 1;
- entry.data.i32[j+3] += 1;
}
}
@@ -401,17 +392,8 @@
if (weight == 0) {
continue;
}
- // Top-left (inclusive)
- scaleCoordinates(entry.data.i32 + j, 1, 1.0 / zoomRatio, true /*clamp*/, arrayWidth,
+ scaleRegion(entry.data.i32 + j, 1.0 / zoomRatio, arrayWidth,
arrayHeight);
- // Bottom-right (exclusive): Use adjacent inclusive pixel to
- // calculate.
- entry.data.i32[j+2] -= 1;
- entry.data.i32[j+3] -= 1;
- scaleCoordinates(entry.data.i32 + j + 2, 1, 1.0 / zoomRatio, true /*clamp*/, arrayWidth,
- arrayHeight);
- entry.data.i32[j+2] += 1;
- entry.data.i32[j+3] += 1;
}
}
for (auto rect : kRectsToCorrect) {
@@ -470,6 +452,24 @@
}
}
+void ZoomRatioMapper::scaleRegion(int32_t* region, float scaleRatio,
+ int32_t arrayWidth, int32_t arrayHeight) {
+ // Top-left (inclusive)
+ scaleCoordinates(region, 1, scaleRatio, true /*clamp*/, arrayWidth,
+ arrayHeight);
+ // Bottom-right (exclusive): Use adjacent inclusive pixel to
+ // calculate.
+ region[2] -= 1;
+ region[3] -= 1;
+ scaleCoordinates(region + 2, 1, scaleRatio, true /*clamp*/, arrayWidth,
+ arrayHeight);
+ region[2] += 1;
+ region[3] += 1;
+ // Make sure bottom-right >= top-left
+ region[2] = std::max(region[0], region[2]);
+ region[3] = std::max(region[1], region[3]);
+}
+
void ZoomRatioMapper::scaleRects(int32_t* rects, int rectCount,
float scaleRatio, int32_t arrayWidth, int32_t arrayHeight) {
for (int i = 0; i < rectCount * 4; i += 4) {
diff --git a/services/camera/libcameraservice/device3/ZoomRatioMapper.h b/services/camera/libcameraservice/device3/ZoomRatioMapper.h
index b7a9e41..1aa8e78 100644
--- a/services/camera/libcameraservice/device3/ZoomRatioMapper.h
+++ b/services/camera/libcameraservice/device3/ZoomRatioMapper.h
@@ -69,6 +69,8 @@
public: // Visible for testing. Do not use concurently.
void scaleCoordinates(int32_t* coordPairs, int coordCount,
float scaleRatio, bool clamp, int32_t arrayWidth, int32_t arrayHeight);
+ void scaleRegion(int32_t* region, float scaleRatio,
+ int32_t arrayWidth, int32_t arrayHeight);
bool isValid() { return mIsValid; }
private:
diff --git a/services/camera/libcameraservice/tests/ZoomRatioTest.cpp b/services/camera/libcameraservice/tests/ZoomRatioTest.cpp
index ff7aafd..badd47a 100644
--- a/services/camera/libcameraservice/tests/ZoomRatioTest.cpp
+++ b/services/camera/libcameraservice/tests/ZoomRatioTest.cpp
@@ -160,11 +160,9 @@
false/*hasZoomRatioRange*/, zoomRatioRange,
usePreCorrectArray));
- size_t index = 0;
int32_t width = testActiveArraySize[2];
int32_t height = testActiveArraySize[3];
if (usePreCorrectArray) {
- index = 1;
width = testPreCorrActiveArraySize[2];
height = testPreCorrActiveArraySize[3];
}
@@ -254,6 +252,19 @@
for (size_t i = 0; i < coords.size(); i++) {
EXPECT_LE(std::abs(coords[i] - expectedZoomOutCoords[i]), kMaxAllowedPixelError);
}
+
+ // Verify region zoom scaling doesn't generate invalid metering region
+ // (width < 0, or height < 0)
+ std::array<float, 3> scaleRatios = {10.0f, 1.0f, 0.1f};
+ for (float scaleRatio : scaleRatios) {
+ for (size_t i = 0; i < originalCoords.size(); i+= 2) {
+ int32_t coordinates[] = {originalCoords[i], originalCoords[i+1],
+ originalCoords[i], originalCoords[i+1]};
+ mapper.scaleRegion(coordinates, scaleRatio, width, height);
+ EXPECT_LE(coordinates[0], coordinates[2]);
+ EXPECT_LE(coordinates[1], coordinates[3]);
+ }
+ }
}
TEST(ZoomRatioTest, scaleCoordinatesTest) {