blob: 7718819f4cbc337897bc2bda57c11fc760029581 [file] [log] [blame]
Shuzhen Wangdbdf72b2019-11-13 11:22:12 -08001/*
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 "device3/ZoomRatioMapper.h"
23
24namespace android {
25
26namespace camera3 {
27
28ZoomRatioMapper::ZoomRatioMapper() : mHalSupportsZoomRatio(false) {
29}
30
31status_t ZoomRatioMapper::initZoomRatioInTemplate(CameraMetadata *request) {
32 camera_metadata_entry_t entry;
33 entry = request->find(ANDROID_CONTROL_ZOOM_RATIO);
34 float defaultZoomRatio = 1.0f;
35 if (entry.count == 0) {
36 return request->update(ANDROID_CONTROL_ZOOM_RATIO, &defaultZoomRatio, 1);
37 }
38 return OK;
39}
40
41status_t ZoomRatioMapper::overrideZoomRatioTags(
42 CameraMetadata* deviceInfo, bool* supportNativeZoomRatio) {
43 if (deviceInfo == nullptr || supportNativeZoomRatio == nullptr) {
44 return BAD_VALUE;
45 }
46
47 camera_metadata_entry_t entry;
48 entry = deviceInfo->find(ANDROID_CONTROL_ZOOM_RATIO_RANGE);
49 if (entry.count != 2 && entry.count != 0) return BAD_VALUE;
50
51 // Hal has zoom ratio support
52 if (entry.count == 2) {
53 *supportNativeZoomRatio = true;
54 return OK;
55 }
56
57 // Hal has no zoom ratio support
58 *supportNativeZoomRatio = false;
59
60 entry = deviceInfo->find(ANDROID_SCALER_AVAILABLE_MAX_DIGITAL_ZOOM);
61 if (entry.count != 1) {
62 ALOGI("%s: Camera device doesn't support SCALER_AVAILABLE_MAX_DIGITAL_ZOOM key!",
63 __FUNCTION__);
64 return OK;
65 }
66
67 float zoomRange[] = {1.0f, entry.data.f[0]};
68 status_t res = deviceInfo->update(ANDROID_CONTROL_ZOOM_RATIO_RANGE, zoomRange, 2);
69 if (res != OK) {
70 ALOGE("%s: Failed to update CONTROL_ZOOM_RATIO_RANGE key: %s (%d)",
71 __FUNCTION__, strerror(-res), res);
72 return res;
73 }
74
75 std::vector<int32_t> requestKeys;
76 entry = deviceInfo->find(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS);
77 if (entry.count > 0) {
78 requestKeys.insert(requestKeys.end(), entry.data.i32, entry.data.i32 + entry.count);
79 }
80 requestKeys.push_back(ANDROID_CONTROL_ZOOM_RATIO);
81 res = deviceInfo->update(ANDROID_REQUEST_AVAILABLE_REQUEST_KEYS,
82 requestKeys.data(), requestKeys.size());
83 if (res != OK) {
84 ALOGE("%s: Failed to update REQUEST_AVAILABLE_REQUEST_KEYS: %s (%d)",
85 __FUNCTION__, strerror(-res), res);
86 return res;
87 }
88
89 std::vector<int32_t> resultKeys;
90 entry = deviceInfo->find(ANDROID_REQUEST_AVAILABLE_RESULT_KEYS);
91 if (entry.count > 0) {
92 resultKeys.insert(resultKeys.end(), entry.data.i32, entry.data.i32 + entry.count);
93 }
94 resultKeys.push_back(ANDROID_CONTROL_ZOOM_RATIO);
95 res = deviceInfo->update(ANDROID_REQUEST_AVAILABLE_RESULT_KEYS,
96 resultKeys.data(), resultKeys.size());
97 if (res != OK) {
98 ALOGE("%s: Failed to update REQUEST_AVAILABLE_RESULT_KEYS: %s (%d)",
99 __FUNCTION__, strerror(-res), res);
100 return res;
101 }
102
103 std::vector<int32_t> charKeys;
104 entry = deviceInfo->find(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS);
105 if (entry.count > 0) {
106 charKeys.insert(charKeys.end(), entry.data.i32, entry.data.i32 + entry.count);
107 }
108 charKeys.push_back(ANDROID_CONTROL_ZOOM_RATIO_RANGE);
109 res = deviceInfo->update(ANDROID_REQUEST_AVAILABLE_CHARACTERISTICS_KEYS,
110 charKeys.data(), charKeys.size());
111 if (res != OK) {
112 ALOGE("%s: Failed to update REQUEST_AVAILABLE_CHARACTERISTICS_KEYS: %s (%d)",
113 __FUNCTION__, strerror(-res), res);
114 return res;
115 }
116
117 return OK;
118}
119
120status_t ZoomRatioMapper::initZoomRatioTags(const CameraMetadata* deviceInfo,
121 bool supportNativeZoomRatio, bool usePrecorrectArray) {
122 std::lock_guard<std::mutex> lock(mMutex);
123
124 camera_metadata_ro_entry_t entry;
125
126 entry = deviceInfo->find(ANDROID_SENSOR_INFO_PRE_CORRECTION_ACTIVE_ARRAY_SIZE);
127 if (entry.count != 4) return BAD_VALUE;
128 int32_t arrayW = entry.data.i32[2];
129 int32_t arrayH = entry.data.i32[3];
130
131 entry = deviceInfo->find(ANDROID_SENSOR_INFO_ACTIVE_ARRAY_SIZE);
132 if (entry.count != 4) return BAD_VALUE;
133 int32_t activeW = entry.data.i32[2];
134 int32_t activeH = entry.data.i32[3];
135
136 if (usePrecorrectArray) {
137 mArrayWidth = arrayW;
138 mArrayHeight = arrayH;
139 } else {
140 mArrayWidth = activeW;
141 mArrayHeight = activeH;
142 }
143 mHalSupportsZoomRatio = supportNativeZoomRatio;
144
145 ALOGV("%s: array size: %d x %d, mHalSupportsZoomRatio %d",
146 __FUNCTION__, mArrayWidth, mArrayHeight, mHalSupportsZoomRatio);
147 return OK;
148}
149
150status_t ZoomRatioMapper::updateCaptureRequest(CameraMetadata* request) {
151 std::lock_guard<std::mutex> lock(mMutex);
152 status_t res = OK;
153 bool zoomRatioIs1 = true;
154 camera_metadata_entry_t entry;
155
156 entry = request->find(ANDROID_CONTROL_ZOOM_RATIO);
157 if (entry.count == 1 && entry.data.f[0] != 1.0f) {
158 zoomRatioIs1 = false;
159 }
160
161 if (mHalSupportsZoomRatio && zoomRatioIs1) {
162 res = separateZoomFromCropLocked(request, false/*isResult*/);
163 } else if (!mHalSupportsZoomRatio && !zoomRatioIs1) {
164 res = combineZoomAndCropLocked(request, false/*isResult*/);
165 }
166
167 // If CONTROL_ZOOM_RATIO is in request, but HAL doesn't support
168 // CONTROL_ZOOM_RATIO, remove it from the request.
169 if (!mHalSupportsZoomRatio && entry.count == 1) {
170 request->erase(ANDROID_CONTROL_ZOOM_RATIO);
171 }
172
173 return res;
174}
175
176status_t ZoomRatioMapper::updateCaptureResult(CameraMetadata* result, bool requestedZoomRatioIs1) {
177 std::lock_guard<std::mutex> lock(mMutex);
178 status_t res = OK;
179
180 if (mHalSupportsZoomRatio && requestedZoomRatioIs1) {
181 res = combineZoomAndCropLocked(result, true/*isResult*/);
182 } else if (!mHalSupportsZoomRatio && !requestedZoomRatioIs1) {
183 res = separateZoomFromCropLocked(result, true/*isResult*/);
184 } else {
185 camera_metadata_entry_t entry = result->find(ANDROID_CONTROL_ZOOM_RATIO);
186 if (entry.count == 0) {
187 float zoomRatio1x = 1.0f;
188 result->update(ANDROID_CONTROL_ZOOM_RATIO, &zoomRatio1x, 1);
189 }
190 }
191
192 return res;
193}
194
195float ZoomRatioMapper::deriveZoomRatio(const CameraMetadata* metadata) {
196 float zoomRatio = 1.0;
197
198 camera_metadata_ro_entry_t entry;
199 entry = metadata->find(ANDROID_SCALER_CROP_REGION);
200 if (entry.count != 4) return zoomRatio;
201
202 // Center of the preCorrection/active size
203 float arrayCenterX = mArrayWidth / 2.0;
204 float arrayCenterY = mArrayHeight / 2.0;
205
206 // Re-map crop region to coordinate system centered to (arrayCenterX,
207 // arrayCenterY).
208 float cropRegionLeft = arrayCenterX - entry.data.i32[0] ;
209 float cropRegionTop = arrayCenterY - entry.data.i32[1];
210 float cropRegionRight = entry.data.i32[0] + entry.data.i32[2] - arrayCenterX;
211 float cropRegionBottom = entry.data.i32[1] + entry.data.i32[3] - arrayCenterY;
212
213 // Calculate the scaling factor for left, top, bottom, right
214 float zoomRatioLeft = std::max(mArrayWidth / (2 * cropRegionLeft), 1.0f);
215 float zoomRatioTop = std::max(mArrayHeight / (2 * cropRegionTop), 1.0f);
216 float zoomRatioRight = std::max(mArrayWidth / (2 * cropRegionRight), 1.0f);
217 float zoomRatioBottom = std::max(mArrayHeight / (2 * cropRegionBottom), 1.0f);
218
219 // Use minimum scaling factor to handle letterboxing or pillarboxing
220 zoomRatio = std::min(std::min(zoomRatioLeft, zoomRatioRight),
221 std::min(zoomRatioTop, zoomRatioBottom));
222
223 ALOGV("%s: derived zoomRatio is %f", __FUNCTION__, zoomRatio);
224 return zoomRatio;
225}
226
227status_t ZoomRatioMapper::separateZoomFromCropLocked(CameraMetadata* metadata, bool isResult) {
228 status_t res;
229 float zoomRatio = deriveZoomRatio(metadata);
230
231 // Update zoomRatio metadata tag
232 res = metadata->update(ANDROID_CONTROL_ZOOM_RATIO, &zoomRatio, 1);
233 if (res != OK) {
234 ALOGE("%s: Failed to update ANDROID_CONTROL_ZOOM_RATIO: %s(%d)",
235 __FUNCTION__, strerror(-res), res);
236 return res;
237 }
238
239 // Scale regions using zoomRatio
240 camera_metadata_entry_t entry;
241 for (auto region : kMeteringRegionsToCorrect) {
242 entry = metadata->find(region);
243 for (size_t j = 0; j < entry.count; j += 5) {
244 int32_t weight = entry.data.i32[j + 4];
245 if (weight == 0) {
246 continue;
247 }
248 // Top-left is inclusively clamped
249 scaleCoordinates(entry.data.i32 + j, 1, zoomRatio, ClampInclusive);
250 // Bottom-right is exclusively clamped
251 scaleCoordinates(entry.data.i32 + j + 2, 1, zoomRatio, ClampExclusive);
252 }
253 }
254
255 for (auto rect : kRectsToCorrect) {
256 entry = metadata->find(rect);
257 scaleRects(entry.data.i32, entry.count / 4, zoomRatio);
258 }
259
260 if (isResult) {
261 for (auto pts : kResultPointsToCorrectNoClamp) {
262 entry = metadata->find(pts);
263 scaleCoordinates(entry.data.i32, entry.count / 2, zoomRatio, ClampOff);
264 }
265 }
266
267 return OK;
268}
269
270status_t ZoomRatioMapper::combineZoomAndCropLocked(CameraMetadata* metadata, bool isResult) {
271 float zoomRatio = 1.0f;
272 camera_metadata_entry_t entry;
273 entry = metadata->find(ANDROID_CONTROL_ZOOM_RATIO);
274 if (entry.count == 1) {
275 zoomRatio = entry.data.f[0];
276 }
277
278 // Unscale regions with zoomRatio
279 status_t res;
280 for (auto region : kMeteringRegionsToCorrect) {
281 entry = metadata->find(region);
282 for (size_t j = 0; j < entry.count; j += 5) {
283 int32_t weight = entry.data.i32[j + 4];
284 if (weight == 0) {
285 continue;
286 }
287 // Top-left is inclusively clamped
288 scaleCoordinates(entry.data.i32 + j, 1, 1.0 / zoomRatio, ClampInclusive);
289 // Bottom-right is exclusively clamped
290 scaleCoordinates(entry.data.i32 + j + 2, 1, 1.0 / zoomRatio, ClampExclusive);
291 }
292 }
293 for (auto rect : kRectsToCorrect) {
294 entry = metadata->find(rect);
295 scaleRects(entry.data.i32, entry.count / 4, 1.0 / zoomRatio);
296 }
297 if (isResult) {
298 for (auto pts : kResultPointsToCorrectNoClamp) {
299 entry = metadata->find(pts);
300 scaleCoordinates(entry.data.i32, entry.count / 2, 1.0 / zoomRatio, ClampOff);
301 }
302 }
303
304 zoomRatio = 1.0;
305 res = metadata->update(ANDROID_CONTROL_ZOOM_RATIO, &zoomRatio, 1);
306 if (res != OK) {
307 return res;
308 }
309
310 return OK;
311}
312
313void ZoomRatioMapper::scaleCoordinates(int32_t* coordPairs, int coordCount,
314 float scaleRatio, ClampMode clamp) {
315 for (int i = 0; i < coordCount * 2; i += 2) {
316 float x = coordPairs[i];
317 float y = coordPairs[i + 1];
318 float xCentered = x - mArrayWidth / 2;
319 float yCentered = y - mArrayHeight / 2;
320 float scaledX = xCentered * scaleRatio;
321 float scaledY = yCentered * scaleRatio;
322 scaledX += mArrayWidth / 2;
323 scaledY += mArrayHeight / 2;
324 // Clamp to within activeArray/preCorrectionActiveArray
325 coordPairs[i] = static_cast<int32_t>(scaledX);
326 coordPairs[i+1] = static_cast<int32_t>(scaledY);
327 if (clamp != ClampOff) {
328 int32_t right, bottom;
329 if (clamp == ClampInclusive) {
330 right = mArrayWidth - 1;
331 bottom = mArrayHeight - 1;
332 } else {
333 right = mArrayWidth;
334 bottom = mArrayHeight;
335 }
336 coordPairs[i] =
337 std::min(right, std::max(0, coordPairs[i]));
338 coordPairs[i+1] =
339 std::min(bottom, std::max(0, coordPairs[i+1]));
340 }
341 ALOGV("%s: coordinates: %d, %d", __FUNCTION__, coordPairs[i], coordPairs[i+1]);
342 }
343}
344
345void ZoomRatioMapper::scaleRects(int32_t* rects, int rectCount,
346 float scaleRatio) {
347 for (int i = 0; i < rectCount * 4; i += 4) {
348 // Map from (l, t, width, height) to (l, t, r, b).
349 // [l, t] is inclusive, and [r, b] is exclusive.
350 int32_t coords[4] = {
351 rects[i],
352 rects[i + 1],
353 rects[i] + rects[i + 2],
354 rects[i + 1] + rects[i + 3]
355 };
356
357 // top-left
358 scaleCoordinates(coords, 1, scaleRatio, ClampInclusive);
359 // bottom-right
360 scaleCoordinates(coords+2, 1, scaleRatio, ClampExclusive);
361
362 // Map back to (l, t, width, height)
363 rects[i] = coords[0];
364 rects[i + 1] = coords[1];
365 rects[i + 2] = coords[2] - coords[0];
366 rects[i + 3] = coords[3] - coords[1];
367 }
368}
369
370} // namespace camera3
371
372} // namespace android