xref: /aosp_15_r20/frameworks/av/services/camera/libcameraservice/device3/ZoomRatioMapper.cpp (revision ec779b8e0859a360c3d303172224686826e6e0e1)
1 /*
2  * Copyright (C) 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "Camera3-ZoomRatioMapper"
18 //#define LOG_NDEBUG 0
19 
20 #include <algorithm>
21 
22 #include <com_android_internal_camera_flags.h>
23 
24 #include "device3/ZoomRatioMapper.h"
25 #include "utils/SessionConfigurationUtilsHost.h"
26 
27 namespace android {
28 
29 namespace camera3 {
30 
initRemappedKeys()31 void ZoomRatioMapper::initRemappedKeys() {
32     mRemappedKeys.insert(
33             kMeteringRegionsToCorrect.begin(),
34             kMeteringRegionsToCorrect.end());
35     mRemappedKeys.insert(
36             kRectsToCorrect.begin(),
37             kRectsToCorrect.end());
38     mRemappedKeys.insert(
39             kResultPointsToCorrectNoClamp.begin(),
40             kResultPointsToCorrectNoClamp.end());
41 
42     mRemappedKeys.insert(ANDROID_CONTROL_ZOOM_RATIO);
43     mRemappedKeys.insert(ANDROID_LOGICAL_MULTI_CAMERA_ACTIVE_PHYSICAL_SENSOR_CROP_REGION);
44 }
45 
initZoomRatioInTemplate(CameraMetadata * request)46 status_t ZoomRatioMapper::initZoomRatioInTemplate(CameraMetadata *request) {
47     status_t res = OK;
48 
49     if (flags::zoom_method()) {
50         uint8_t zoomMethod = ANDROID_CONTROL_ZOOM_METHOD_AUTO;
51         res = request->update(ANDROID_CONTROL_ZOOM_METHOD, &zoomMethod, 1);
52         if (res != OK) {
53             ALOGE("%s: Failed to update CONTROL_ZOOM_METHOD key: %s (%d)",
54                     __FUNCTION__, strerror(-res), res);
55             return res;
56         }
57     }
58 
59     camera_metadata_entry_t entry;
60     entry = request->find(ANDROID_CONTROL_ZOOM_RATIO);
61     float defaultZoomRatio = 1.0f;
62     if (entry.count == 0) {
63         res = request->update(ANDROID_CONTROL_ZOOM_RATIO, &defaultZoomRatio, 1);
64     }
65     return res;
66 }
67 
overrideZoomRatioTags(CameraMetadata * deviceInfo,bool * supportNativeZoomRatio)68 status_t ZoomRatioMapper::overrideZoomRatioTags(
69         CameraMetadata* deviceInfo, bool* supportNativeZoomRatio) {
70     if (deviceInfo == nullptr || supportNativeZoomRatio == nullptr) {
71         return BAD_VALUE;
72     }
73 
74     bool halSupportZoomRatio = false;
75     camera_metadata_entry_t entry;
76     entry = deviceInfo->find(ANDROID_CONTROL_ZOOM_RATIO_RANGE);
77     if (entry.count != 2 && entry.count != 0) return BAD_VALUE;
78     // Hal has zoom ratio support
79     if (entry.count == 2) {
80         halSupportZoomRatio = true;
81     }
82 
83     // Add ZOOM_METHOD request and result keys
84     std::vector<int32_t> requestKeys;
85     entry = deviceInfo->find(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS);
86     if (entry.count > 0) {
87         requestKeys.insert(requestKeys.end(), entry.data.i32, entry.data.i32 + entry.count);
88     }
89     if (flags::zoom_method()) {
90         requestKeys.push_back(ANDROID_CONTROL_ZOOM_METHOD);
91     }
92     std::vector<int32_t> resultKeys;
93     entry = deviceInfo->find(ANDROID_REQUEST_AVAILABLE_RESULT_KEYS);
94     if (entry.count > 0) {
95         resultKeys.insert(resultKeys.end(), entry.data.i32, entry.data.i32 + entry.count);
96     }
97     if (flags::zoom_method()) {
98         resultKeys.push_back(ANDROID_CONTROL_ZOOM_METHOD);
99     }
100 
101     // Add additional keys if the HAL doesn't support ZOOM_RATIO
102     status_t res = OK;
103     if (!halSupportZoomRatio) {
104         entry = deviceInfo->find(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM);
105         if (entry.count != 1) {
106             ALOGI("%s: Camera device doesn't support SCALER_AVAILABLE_MAX_DIGITAL_ZOOM key!",
107                     __FUNCTION__);
108             return OK;
109         }
110         float zoomRange[] = {1.0f, entry.data.f[0]};
111         res = deviceInfo->update(ANDROID_CONTROL_ZOOM_RATIO_RANGE, zoomRange, 2);
112         if (res != OK) {
113             ALOGE("%s: Failed to update CONTROL_ZOOM_RATIO_RANGE key: %s (%d)",
114                     __FUNCTION__, strerror(-res), res);
115             return res;
116         }
117 
118         requestKeys.push_back(ANDROID_CONTROL_ZOOM_RATIO);
119         resultKeys.push_back(ANDROID_CONTROL_ZOOM_RATIO);
120 
121         std::vector<int32_t> charKeys;
122         entry = deviceInfo->find(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS);
123         if (entry.count > 0) {
124             charKeys.insert(charKeys.end(), entry.data.i32, entry.data.i32 + entry.count);
125         }
126         charKeys.push_back(ANDROID_CONTROL_ZOOM_RATIO_RANGE);
127         res = deviceInfo->update(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS,
128                 charKeys.data(), charKeys.size());
129         if (res != OK) {
130             ALOGE("%s: Failed to update REQUEST_AVAILABLE_CHARACTERISTICS_KEYS: %s (%d)",
131                     __FUNCTION__, strerror(-res), res);
132             return res;
133         }
134     }
135 
136     // Update available request and result keys
137     res = deviceInfo->update(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS,
138             requestKeys.data(), requestKeys.size());
139     if (res != OK) {
140         ALOGE("%s: Failed to update REQUEST_AVAILABLE_REQUEST_KEYS: %s (%d)",
141                 __FUNCTION__, strerror(-res), res);
142         return res;
143     }
144     res = deviceInfo->update(ANDROID_REQUEST_AVAILABLE_RESULT_KEYS,
145             resultKeys.data(), resultKeys.size());
146     if (res != OK) {
147         ALOGE("%s: Failed to update REQUEST_AVAILABLE_RESULT_KEYS: %s (%d)",
148                 __FUNCTION__, strerror(-res), res);
149         return res;
150     }
151 
152     *supportNativeZoomRatio = halSupportZoomRatio;
153     return OK;
154 }
155 
ZoomRatioMapper(const CameraMetadata * deviceInfo,bool supportNativeZoomRatio,bool usePrecorrectArray)156 ZoomRatioMapper::ZoomRatioMapper(const CameraMetadata* deviceInfo,
157         bool supportNativeZoomRatio, bool usePrecorrectArray) {
158     initRemappedKeys();
159 
160     int32_t arrayW = 0;
161     int32_t arrayH = 0;
162     int32_t arrayMaximumResolutionW = 0;
163     int32_t arrayMaximumResolutionH = 0;
164     int32_t activeW = 0;
165     int32_t activeH = 0;
166     int32_t activeMaximumResolutionW = 0;
167     int32_t activeMaximumResolutionH = 0;
168 
169     if (!SessionConfigurationUtils::getArrayWidthAndHeight(deviceInfo,
170             ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE, &arrayW, &arrayH)) {
171         ALOGE("%s: Couldn't get pre correction active array size", __FUNCTION__);
172         return;
173     }
174      if (!SessionConfigurationUtils::getArrayWidthAndHeight(deviceInfo,
175             ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE, &activeW, &activeH)) {
176         ALOGE("%s: Couldn't get active array size", __FUNCTION__);
177         return;
178     }
179 
180     bool supportsUltraHighResolutionCapture =
181             camera3::SessionConfigurationUtils::supportsUltraHighResolutionCapture(*deviceInfo);
182     if (supportsUltraHighResolutionCapture) {
183         if (!SessionConfigurationUtils::getArrayWidthAndHeight(deviceInfo,
184                 ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION,
185                 &arrayMaximumResolutionW, &arrayMaximumResolutionH)) {
186             ALOGE("%s: Couldn't get maximum resolution pre correction active array size",
187                     __FUNCTION__);
188             return;
189         }
190          if (!SessionConfigurationUtils::getArrayWidthAndHeight(deviceInfo,
191                 ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE_MAXIMUM_RESOLUTION,
192                 &activeMaximumResolutionW, &activeMaximumResolutionH)) {
193             ALOGE("%s: Couldn't get maximum resolution pre correction active array size",
194                     __FUNCTION__);
195             return;
196         }
197     }
198 
199     if (usePrecorrectArray) {
200         mArrayWidth = arrayW;
201         mArrayHeight = arrayH;
202         mArrayWidthMaximumResolution = arrayMaximumResolutionW;
203         mArrayHeightMaximumResolution = arrayMaximumResolutionH;
204     } else {
205         mArrayWidth = activeW;
206         mArrayHeight = activeH;
207         mArrayWidthMaximumResolution = activeMaximumResolutionW;
208         mArrayHeightMaximumResolution = activeMaximumResolutionH;
209     }
210     mHalSupportsZoomRatio = supportNativeZoomRatio;
211 
212     ALOGV("%s: array size: %d x %d, full res array size: %d x %d,  mHalSupportsZoomRatio %d",
213             __FUNCTION__, mArrayWidth, mArrayHeight, mArrayWidthMaximumResolution,
214             mArrayHeightMaximumResolution, mHalSupportsZoomRatio);
215     mIsValid = true;
216 }
217 
getArrayDimensionsToBeUsed(const CameraMetadata * settings,int32_t * arrayWidth,int32_t * arrayHeight)218 status_t ZoomRatioMapper::getArrayDimensionsToBeUsed(const CameraMetadata *settings,
219         int32_t *arrayWidth, int32_t *arrayHeight) {
220     if (settings == nullptr || arrayWidth == nullptr || arrayHeight == nullptr) {
221         return BAD_VALUE;
222     }
223     // First we get the sensorPixelMode from the settings metadata.
224     int32_t sensorPixelMode = ANDROID_SENSOR_PIXEL_MODE_DEFAULT;
225     camera_metadata_ro_entry sensorPixelModeEntry = settings->find(ANDROID_SENSOR_PIXEL_MODE);
226     if (sensorPixelModeEntry.count != 0) {
227         sensorPixelMode = sensorPixelModeEntry.data.u8[0];
228         if (sensorPixelMode != ANDROID_SENSOR_PIXEL_MODE_DEFAULT &&
229             sensorPixelMode != ANDROID_SENSOR_PIXEL_MODE_MAXIMUM_RESOLUTION) {
230             ALOGE("%s: Request sensor pixel mode is not one of the valid values %d",
231                       __FUNCTION__, sensorPixelMode);
232             return BAD_VALUE;
233         }
234     }
235     if (sensorPixelMode == ANDROID_SENSOR_PIXEL_MODE_DEFAULT) {
236         *arrayWidth = mArrayWidth;
237         *arrayHeight = mArrayHeight;
238     } else {
239         *arrayWidth = mArrayWidthMaximumResolution;
240         *arrayHeight = mArrayHeightMaximumResolution;
241     }
242     return OK;
243 }
244 
updateCaptureRequest(CameraMetadata * request)245 status_t ZoomRatioMapper::updateCaptureRequest(CameraMetadata* request) {
246     if (!mIsValid) return INVALID_OPERATION;
247 
248     status_t res = OK;
249     camera_metadata_entry_t entry;
250     int arrayHeight, arrayWidth = 0;
251     res = getArrayDimensionsToBeUsed(request, &arrayWidth, &arrayHeight);
252     if (res != OK) {
253         return res;
254     }
255     entry = request->find(ANDROID_CONTROL_ZOOM_RATIO);
256     bool zoomRatioIs1 = (entry.count == 0 || entry.data.f[0] == 1.0f);
257     bool useZoomRatio = !zoomRatioIs1;
258     if (flags::zoom_method()) {
259         entry = request->find(ANDROID_CONTROL_ZOOM_METHOD);
260         useZoomRatio |= (entry.count == 1
261                         && entry.data.u8[0] == ANDROID_CONTROL_ZOOM_METHOD_ZOOM_RATIO);
262     }
263     if (useZoomRatio) {
264         // If cropRegion is windowboxing, override it with activeArray
265         camera_metadata_entry_t cropRegionEntry = request->find(ANDROID_SCALER_CROP_REGION);
266         if (cropRegionEntry.count == 4) {
267             int cropWidth = cropRegionEntry.data.i32[2];
268             int cropHeight = cropRegionEntry.data.i32[3];
269             if (cropWidth < arrayWidth && cropHeight < arrayHeight) {
270                 cropRegionEntry.data.i32[0] = 0;
271                 cropRegionEntry.data.i32[1] = 0;
272                 cropRegionEntry.data.i32[2] = arrayWidth;
273                 cropRegionEntry.data.i32[3] = arrayHeight;
274             }
275         }
276     }
277 
278     if (mHalSupportsZoomRatio && !useZoomRatio) {
279         res = separateZoomFromCropLocked(request, false/*isResult*/, arrayWidth, arrayHeight);
280     } else if (!mHalSupportsZoomRatio && useZoomRatio) {
281         res = combineZoomAndCropLocked(request, false/*isResult*/, arrayWidth, arrayHeight);
282     }
283 
284     // If CONTROL_ZOOM_RATIO is in request, but HAL doesn't support
285     // CONTROL_ZOOM_RATIO, remove it from the request.
286     if (!mHalSupportsZoomRatio && entry.count == 1) {
287         request->erase(ANDROID_CONTROL_ZOOM_RATIO);
288     }
289 
290     return res;
291 }
292 
updateCaptureResult(CameraMetadata * result,bool zoomMethodIsRatio,bool zoomRatioIs1)293 status_t ZoomRatioMapper::updateCaptureResult(
294         CameraMetadata* result, bool zoomMethodIsRatio, bool zoomRatioIs1) {
295     if (!mIsValid) return INVALID_OPERATION;
296 
297     status_t res = OK;
298 
299     int arrayHeight, arrayWidth = 0;
300     res = getArrayDimensionsToBeUsed(result, &arrayWidth, &arrayHeight);
301     if (res != OK) {
302         return res;
303     }
304 
305     bool useZoomRatio = !zoomRatioIs1 || zoomMethodIsRatio;
306     if (mHalSupportsZoomRatio && !useZoomRatio) {
307         res = combineZoomAndCropLocked(result, true/*isResult*/, arrayWidth, arrayHeight);
308     } else if (!mHalSupportsZoomRatio && useZoomRatio) {
309         res = separateZoomFromCropLocked(result, true/*isResult*/, arrayWidth, arrayHeight);
310     } else {
311         camera_metadata_entry_t entry = result->find(ANDROID_CONTROL_ZOOM_RATIO);
312         if (entry.count == 0) {
313             float zoomRatio1x = 1.0f;
314             result->update(ANDROID_CONTROL_ZOOM_RATIO, &zoomRatio1x, 1);
315         }
316     }
317 
318     if (flags::zoom_method()) {
319         uint8_t zoomMethod = zoomMethodIsRatio ?  ANDROID_CONTROL_ZOOM_METHOD_ZOOM_RATIO :
320                 ANDROID_CONTROL_ZOOM_METHOD_AUTO;
321         result->update(ANDROID_CONTROL_ZOOM_METHOD, &zoomMethod, 1);
322     }
323 
324     return res;
325 }
326 
deriveZoomRatio(const CameraMetadata * metadata,float * zoomRatioRet,int arrayWidth,int arrayHeight)327 status_t ZoomRatioMapper::deriveZoomRatio(const CameraMetadata* metadata, float *zoomRatioRet,
328         int arrayWidth, int arrayHeight) {
329     if (metadata == nullptr || zoomRatioRet == nullptr) {
330         return BAD_VALUE;
331     }
332     float zoomRatio = 1.0;
333 
334     camera_metadata_ro_entry_t entry;
335     entry = metadata->find(ANDROID_SCALER_CROP_REGION);
336     if (entry.count != 4) {
337         *zoomRatioRet = 1;
338         return OK;
339     }
340     // Center of the preCorrection/active size
341     float arrayCenterX = arrayWidth / 2.0;
342     float arrayCenterY = arrayHeight / 2.0;
343 
344     // Re-map crop region to coordinate system centered to (arrayCenterX,
345     // arrayCenterY).
346     float cropRegionLeft = arrayCenterX - entry.data.i32[0] ;
347     float cropRegionTop = arrayCenterY - entry.data.i32[1];
348     float cropRegionRight = entry.data.i32[0] + entry.data.i32[2] - arrayCenterX;
349     float cropRegionBottom = entry.data.i32[1] + entry.data.i32[3] - arrayCenterY;
350 
351     // Calculate the scaling factor for left, top, bottom, right
352     float zoomRatioLeft = std::max(arrayWidth / (2 * cropRegionLeft), 1.0f);
353     float zoomRatioTop = std::max(arrayHeight / (2 * cropRegionTop), 1.0f);
354     float zoomRatioRight = std::max(arrayWidth / (2 * cropRegionRight), 1.0f);
355     float zoomRatioBottom = std::max(arrayHeight / (2 * cropRegionBottom), 1.0f);
356 
357     // Use minimum scaling factor to handle letterboxing or pillarboxing
358     zoomRatio = std::min(std::min(zoomRatioLeft, zoomRatioRight),
359             std::min(zoomRatioTop, zoomRatioBottom));
360 
361     ALOGV("%s: derived zoomRatio is %f", __FUNCTION__, zoomRatio);
362     *zoomRatioRet = zoomRatio;
363     return OK;
364 }
365 
separateZoomFromCropLocked(CameraMetadata * metadata,bool isResult,int arrayWidth,int arrayHeight)366 status_t ZoomRatioMapper::separateZoomFromCropLocked(CameraMetadata* metadata, bool isResult,
367         int arrayWidth, int arrayHeight) {
368     float zoomRatio = 1.0;
369     status_t res = deriveZoomRatio(metadata, &zoomRatio, arrayWidth, arrayHeight);
370 
371     if (res != OK) {
372         ALOGE("%s: Failed to derive zoom ratio: %s(%d)",
373                 __FUNCTION__, strerror(-res), res);
374         return res;
375     }
376 
377     // Update zoomRatio metadata tag
378     res = metadata->update(ANDROID_CONTROL_ZOOM_RATIO, &zoomRatio, 1);
379     if (res != OK) {
380         ALOGE("%s: Failed to update ANDROID_CONTROL_ZOOM_RATIO: %s(%d)",
381                 __FUNCTION__, strerror(-res), res);
382         return res;
383     }
384 
385     // Scale regions using zoomRatio
386     camera_metadata_entry_t entry;
387     for (auto region : kMeteringRegionsToCorrect) {
388         entry = metadata->find(region);
389         for (size_t j = 0; j < entry.count; j += 5) {
390             int32_t weight = entry.data.i32[j + 4];
391             if (weight == 0) {
392                 continue;
393             }
394             scaleRegion(entry.data.i32 + j, zoomRatio, arrayWidth,
395                     arrayHeight);
396         }
397     }
398 
399     for (auto rect : kRectsToCorrect) {
400         entry = metadata->find(rect);
401         scaleRects(entry.data.i32, entry.count / 4, zoomRatio, arrayWidth, arrayHeight);
402     }
403 
404     if (isResult) {
405         for (auto pts : kResultPointsToCorrectNoClamp) {
406             entry = metadata->find(pts);
407             scaleCoordinates(entry.data.i32, entry.count / 2, zoomRatio, false /*clamp*/,
408                     arrayWidth, arrayHeight);
409         }
410     }
411 
412     return OK;
413 }
414 
combineZoomAndCropLocked(CameraMetadata * metadata,bool isResult,int arrayWidth,int arrayHeight)415 status_t ZoomRatioMapper::combineZoomAndCropLocked(CameraMetadata* metadata, bool isResult,
416         int arrayWidth, int arrayHeight) {
417     float zoomRatio = 1.0f;
418     camera_metadata_entry_t entry;
419     entry = metadata->find(ANDROID_CONTROL_ZOOM_RATIO);
420     if (entry.count == 1) {
421         zoomRatio = entry.data.f[0];
422     }
423 
424     // Unscale regions with zoomRatio
425     for (auto region : kMeteringRegionsToCorrect) {
426         entry = metadata->find(region);
427         for (size_t j = 0; j < entry.count; j += 5) {
428             int32_t weight = entry.data.i32[j + 4];
429             if (weight == 0) {
430                 continue;
431             }
432             scaleRegion(entry.data.i32 + j, 1.0 / zoomRatio, arrayWidth,
433                     arrayHeight);
434         }
435     }
436     for (auto rect : kRectsToCorrect) {
437         entry = metadata->find(rect);
438         scaleRects(entry.data.i32, entry.count / 4, 1.0 / zoomRatio, arrayWidth, arrayHeight);
439     }
440     if (isResult) {
441         for (auto pts : kResultPointsToCorrectNoClamp) {
442             entry = metadata->find(pts);
443             scaleCoordinates(entry.data.i32, entry.count / 2, 1.0 / zoomRatio, false /*clamp*/,
444                     arrayWidth, arrayHeight);
445         }
446     }
447 
448     zoomRatio = 1.0;
449     status_t res = metadata->update(ANDROID_CONTROL_ZOOM_RATIO, &zoomRatio, 1);
450     if (res != OK) {
451         return res;
452     }
453 
454     return OK;
455 }
456 
scaleCoordinates(int32_t * coordPairs,int coordCount,float scaleRatio,bool clamp,int32_t arrayWidth,int32_t arrayHeight)457 void ZoomRatioMapper::scaleCoordinates(int32_t* coordPairs, int coordCount,
458         float scaleRatio, bool clamp, int32_t arrayWidth, int32_t arrayHeight) {
459     // A pixel's coordinate is represented by the position of its top-left corner.
460     // To avoid the rounding error, we use the coordinate for the center of the
461     // pixel instead:
462     // 1. First shift the coordinate system half pixel both horizontally and
463     // vertically, so that [x, y] is the center of the pixel, not the top-left corner.
464     // 2. Do zoom operation to scale the coordinate relative to the center of
465     // the active array (shifted by 0.5 pixel as well).
466     // 3. Shift the coordinate system back by directly using the pixel center
467     // coordinate.
468     for (int i = 0; i < coordCount * 2; i += 2) {
469         float x = coordPairs[i];
470         float y = coordPairs[i + 1];
471         float xCentered = x - (arrayWidth - 2) / 2;
472         float yCentered = y - (arrayHeight - 2) / 2;
473         float scaledX = xCentered * scaleRatio;
474         float scaledY = yCentered * scaleRatio;
475         scaledX += (arrayWidth - 2) / 2;
476         scaledY += (arrayHeight - 2) / 2;
477         coordPairs[i] = static_cast<int32_t>(std::round(scaledX));
478         coordPairs[i+1] = static_cast<int32_t>(std::round(scaledY));
479         // Clamp to within activeArray/preCorrectionActiveArray
480         if (clamp) {
481             int32_t right = arrayWidth - 1;
482             int32_t bottom = arrayHeight - 1;
483             coordPairs[i] =
484                     std::min(right, std::max(0, coordPairs[i]));
485             coordPairs[i+1] =
486                     std::min(bottom, std::max(0, coordPairs[i+1]));
487         }
488         ALOGV("%s: coordinates: %d, %d", __FUNCTION__, coordPairs[i], coordPairs[i+1]);
489     }
490 }
491 
scaleRegion(int32_t * region,float scaleRatio,int32_t arrayWidth,int32_t arrayHeight)492 void ZoomRatioMapper::scaleRegion(int32_t* region, float scaleRatio,
493         int32_t arrayWidth, int32_t arrayHeight) {
494     // Top-left (inclusive)
495     scaleCoordinates(region, 1, scaleRatio, true /*clamp*/, arrayWidth,
496             arrayHeight);
497     // Bottom-right (exclusive): Use adjacent inclusive pixel to
498     // calculate.
499     region[2] -= 1;
500     region[3] -= 1;
501     scaleCoordinates(region + 2, 1, scaleRatio, true /*clamp*/, arrayWidth,
502             arrayHeight);
503     region[2] += 1;
504     region[3] += 1;
505     // Make sure bottom-right >= top-left
506     region[2] = std::max(region[0], region[2]);
507     region[3] = std::max(region[1], region[3]);
508 }
509 
scaleRects(int32_t * rects,int rectCount,float scaleRatio,int32_t arrayWidth,int32_t arrayHeight)510 void ZoomRatioMapper::scaleRects(int32_t* rects, int rectCount,
511         float scaleRatio, int32_t arrayWidth, int32_t arrayHeight) {
512     for (int i = 0; i < rectCount * 4; i += 4) {
513         // Map from (l, t, width, height) to (l, t, l+width-1, t+height-1),
514         // where both top-left and bottom-right are inclusive.
515         int32_t coords[4] = {
516             rects[i],
517             rects[i + 1],
518             rects[i] + rects[i + 2] - 1,
519             rects[i + 1] + rects[i + 3] - 1
520         };
521 
522         // top-left
523         scaleCoordinates(coords, 1, scaleRatio, true /*clamp*/, arrayWidth, arrayHeight);
524         // bottom-right
525         scaleCoordinates(coords+2, 1, scaleRatio, true /*clamp*/, arrayWidth, arrayHeight);
526 
527         // Map back to (l, t, width, height)
528         rects[i] = coords[0];
529         rects[i + 1] = coords[1];
530         rects[i + 2] = coords[2] - coords[0] + 1;
531         rects[i + 3] = coords[3] - coords[1] + 1;
532     }
533 }
534 
535 } // namespace camera3
536 
537 } // namespace android
538