1 /*
2  * Copyright (C) 2020 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 "GCH_ZoomRatioMapper"
18 
19 #include <log/log.h>
20 #include <cmath>
21 
22 #include "utils.h"
23 #include "zoom_ratio_mapper.h"
24 
25 namespace android {
26 namespace google_camera_hal {
27 
28 int32_t kWeightedRectToConvert[] = {ANDROID_CONTROL_AE_REGIONS,
29                                     ANDROID_CONTROL_AF_REGIONS,
30                                     ANDROID_CONTROL_AWB_REGIONS};
31 
32 int32_t kRectToConvert[] = {ANDROID_SCALER_CROP_REGION};
33 
34 int32_t kResultPointsToConvert[] = {ANDROID_STATISTICS_FACE_LANDMARKS,
35                                     ANDROID_STATISTICS_FACE_RECTANGLES};
36 
Initialize(InitParams * params)37 void ZoomRatioMapper::Initialize(InitParams* params) {
38   if (params == nullptr) {
39     ALOGE("%s: invalid param", __FUNCTION__);
40     return;
41   }
42   memcpy(&active_array_dimension_, &params->active_array_dimension,
43          sizeof(active_array_dimension_));
44   physical_cam_active_array_dimension_ =
45       params->physical_cam_active_array_dimension;
46   memcpy(&zoom_ratio_range_, &params->zoom_ratio_range,
47          sizeof(zoom_ratio_range_));
48   zoom_ratio_mapper_hwl_ = std::move(params->zoom_ratio_mapper_hwl);
49   is_zoom_ratio_supported_ = true;
50   camera_id_ = params->camera_id;
51 }
52 
UpdateCaptureRequest(CaptureRequest * request)53 void ZoomRatioMapper::UpdateCaptureRequest(CaptureRequest* request) {
54   if (request == nullptr) {
55     ALOGE("%s: request is nullptr", __FUNCTION__);
56     return;
57   }
58 
59   if (!is_zoom_ratio_supported_) {
60     ALOGV("%s: zoom ratio is not supported", __FUNCTION__);
61     return;
62   }
63 
64   if (request->settings != nullptr) {
65     Dimension override_dimension;
66     Dimension active_array_dimension_chosen = active_array_dimension_;
67     if (zoom_ratio_mapper_hwl_ &&
68         zoom_ratio_mapper_hwl_->GetActiveArrayDimensionToBeUsed(
69             camera_id_, request->settings.get(), &override_dimension)) {
70       active_array_dimension_chosen = override_dimension;
71     }
72     ApplyZoomRatio(active_array_dimension_chosen, true, request->settings.get());
73   }
74 
75   for (auto& [camera_id, metadata] : request->physical_camera_settings) {
76     if (metadata != nullptr) {
77       auto physical_cam_iter =
78           physical_cam_active_array_dimension_.find(camera_id);
79       if (physical_cam_iter == physical_cam_active_array_dimension_.end()) {
80         ALOGE("%s: Physical camera id %d is not found!", __FUNCTION__,
81               camera_id);
82         continue;
83       }
84       Dimension override_dimension;
85       Dimension physical_active_array_dimension = physical_cam_iter->second;
86       if (zoom_ratio_mapper_hwl_ &&
87           zoom_ratio_mapper_hwl_->GetActiveArrayDimensionToBeUsed(
88               camera_id, metadata.get(), &override_dimension)) {
89         physical_active_array_dimension = override_dimension;
90       }
91       ApplyZoomRatio(physical_active_array_dimension, true, metadata.get());
92     }
93   }
94   if (zoom_ratio_mapper_hwl_) {
95     zoom_ratio_mapper_hwl_->UpdateCaptureRequest(request);
96   }
97 }
98 
UpdateCaptureResult(CaptureResult * result)99 void ZoomRatioMapper::UpdateCaptureResult(CaptureResult* result) {
100   if (result == nullptr) {
101     ALOGE("%s: result is nullptr", __FUNCTION__);
102     return;
103   }
104 
105   if (!is_zoom_ratio_supported_) {
106     ALOGV("%s: zoom ratio is not supported", __FUNCTION__);
107     return;
108   }
109 
110   if (result->result_metadata != nullptr) {
111     Dimension override_dimension;
112     Dimension active_array_dimension_chosen = active_array_dimension_;
113     if (zoom_ratio_mapper_hwl_ &&
114         zoom_ratio_mapper_hwl_->GetActiveArrayDimensionToBeUsed(
115             camera_id_, result->result_metadata.get(), &override_dimension)) {
116       active_array_dimension_chosen = override_dimension;
117     }
118     ApplyZoomRatio(active_array_dimension_chosen, false,
119                    result->result_metadata.get());
120   }
121 
122   for (auto& [camera_id, metadata] : result->physical_metadata) {
123     if (metadata != nullptr) {
124       auto physical_cam_iter =
125           physical_cam_active_array_dimension_.find(camera_id);
126       if (physical_cam_iter == physical_cam_active_array_dimension_.end()) {
127         ALOGE("%s: Physical camera id %d is not found!", __FUNCTION__,
128               camera_id);
129         continue;
130       }
131       Dimension override_dimension;
132       Dimension physical_active_array_dimension = physical_cam_iter->second;
133       if (zoom_ratio_mapper_hwl_ &&
134           zoom_ratio_mapper_hwl_->GetActiveArrayDimensionToBeUsed(
135               camera_id, metadata.get(), &override_dimension)) {
136         physical_active_array_dimension = override_dimension;
137       }
138       ApplyZoomRatio(physical_active_array_dimension, false, metadata.get());
139     }
140   }
141   if (zoom_ratio_mapper_hwl_) {
142     zoom_ratio_mapper_hwl_->UpdateCaptureResult(result);
143   }
144 }
145 
ApplyZoomRatio(const Dimension & active_array_dimension,const bool is_request,HalCameraMetadata * metadata)146 void ZoomRatioMapper::ApplyZoomRatio(const Dimension& active_array_dimension,
147                                      const bool is_request,
148                                      HalCameraMetadata* metadata) {
149   if (metadata == nullptr) {
150     ALOGE("%s: metadata is nullptr", __FUNCTION__);
151     return;
152   }
153 
154   camera_metadata_ro_entry entry = {};
155   status_t res = metadata->Get(ANDROID_CONTROL_ZOOM_RATIO, &entry);
156   if (res != OK) {
157     ALOGV("%s: zoom ratio doesn't exist, cancel the conversion", __FUNCTION__);
158     return;
159   }
160   float zoom_ratio = entry.data.f[0];
161 
162   if (zoom_ratio < zoom_ratio_range_.min) {
163     ALOGE("%s, zoom_ratio(%f) is smaller than lower bound(%f)", __FUNCTION__,
164           zoom_ratio, zoom_ratio_range_.min);
165     zoom_ratio = zoom_ratio_range_.min;
166   } else if (zoom_ratio > zoom_ratio_range_.max) {
167     ALOGE("%s, zoom_ratio(%f) is larger than upper bound(%f)", __FUNCTION__,
168           zoom_ratio, zoom_ratio_range_.max);
169     zoom_ratio = zoom_ratio_range_.max;
170   }
171 
172   if (zoom_ratio_mapper_hwl_ != nullptr && is_request) {
173     zoom_ratio_mapper_hwl_->LimitZoomRatioIfConcurrent(&zoom_ratio);
174   }
175 
176   if (fabs(zoom_ratio - entry.data.f[0]) > 1e-9) {
177     metadata->Set(ANDROID_CONTROL_ZOOM_RATIO, &zoom_ratio, entry.count);
178   }
179 
180   for (auto tag_id : kRectToConvert) {
181     UpdateRects(zoom_ratio, tag_id, active_array_dimension, is_request,
182                 metadata);
183   }
184 
185   for (auto tag_id : kWeightedRectToConvert) {
186     UpdateWeightedRects(zoom_ratio, tag_id, active_array_dimension, is_request,
187                         metadata);
188   }
189 
190   if (!is_request) {
191     for (auto tag_id : kResultPointsToConvert) {
192       UpdatePoints(zoom_ratio, tag_id, active_array_dimension, metadata);
193     }
194   }
195 }
196 
UpdateRects(const float zoom_ratio,const uint32_t tag_id,const Dimension & active_array_dimension,const bool is_request,HalCameraMetadata * metadata)197 void ZoomRatioMapper::UpdateRects(const float zoom_ratio, const uint32_t tag_id,
198                                   const Dimension& active_array_dimension,
199                                   const bool is_request,
200                                   HalCameraMetadata* metadata) {
201   if (metadata == nullptr) {
202     ALOGE("%s: metadata is nullptr", __FUNCTION__);
203     return;
204   }
205   camera_metadata_ro_entry entry = {};
206   status_t res = metadata->Get(tag_id, &entry);
207   if (res != OK || entry.count == 0) {
208     ALOGE("%s: Failed to get the region: %d, res: %d", __FUNCTION__, tag_id,
209           res);
210     return;
211   }
212   int32_t left = entry.data.i32[0];
213   int32_t top = entry.data.i32[1];
214   int32_t width = entry.data.i32[2];
215   int32_t height = entry.data.i32[3];
216 
217   if (is_request) {
218     utils::ConvertZoomRatio(zoom_ratio, active_array_dimension, &left, &top,
219                             &width, &height);
220   } else {
221     utils::RevertZoomRatio(zoom_ratio, active_array_dimension, true, &left,
222                            &top, &width, &height);
223   }
224   int32_t rect[4] = {left, top, width, height};
225 
226   ALOGV(
227       "%s: is request: %d, zoom ratio: %f, set rect: [%d, %d, %d, %d] -> [%d, "
228       "%d, %d, %d]",
229       __FUNCTION__, is_request, zoom_ratio, entry.data.i32[0], entry.data.i32[1],
230       entry.data.i32[2], entry.data.i32[3], rect[0], rect[1], rect[2], rect[3]);
231 
232   res = metadata->Set(tag_id, rect, sizeof(rect) / sizeof(int32_t));
233   if (res != OK) {
234     ALOGE("%s: Updating region: %u failed: %s (%d)", __FUNCTION__, tag_id,
235           strerror(-res), res);
236   }
237 }
238 
UpdateWeightedRects(const float zoom_ratio,const uint32_t tag_id,const Dimension & active_array_dimension,const bool is_request,HalCameraMetadata * metadata)239 void ZoomRatioMapper::UpdateWeightedRects(
240     const float zoom_ratio, const uint32_t tag_id,
241     const Dimension& active_array_dimension, const bool is_request,
242     HalCameraMetadata* metadata) {
243   if (metadata == nullptr) {
244     ALOGE("%s: metadata is nullptr", __FUNCTION__);
245     return;
246   }
247   camera_metadata_ro_entry entry = {};
248   status_t res = metadata->Get(tag_id, &entry);
249   if (res != OK || entry.count == 0) {
250     ALOGV("%s: Failed to get the region: %d, res: %d", __FUNCTION__, tag_id,
251           res);
252     return;
253   }
254   const WeightedRect* regions =
255       reinterpret_cast<const WeightedRect*>(entry.data.i32);
256   const size_t kNumElementsInTuple = sizeof(WeightedRect) / sizeof(int32_t);
257   std::vector<WeightedRect> updated_regions(entry.count / kNumElementsInTuple);
258 
259   for (size_t i = 0; i < updated_regions.size(); i++) {
260     int32_t left = regions[i].left;
261     int32_t top = regions[i].top;
262     int32_t width = regions[i].right - regions[i].left + 1;
263     int32_t height = regions[i].bottom - regions[i].top + 1;
264 
265     if (is_request) {
266       utils::ConvertZoomRatio(zoom_ratio, active_array_dimension, &left, &top,
267                               &width, &height);
268     } else {
269       utils::RevertZoomRatio(zoom_ratio, active_array_dimension, true, &left,
270                              &top, &width, &height);
271     }
272 
273     updated_regions[i].left = left;
274     updated_regions[i].top = top;
275     updated_regions[i].right = left + width - 1;
276     updated_regions[i].bottom = top + height - 1;
277     updated_regions[i].weight = regions[i].weight;
278 
279     ALOGV("%s: set region(%d): [%d, %d, %d, %d, %d]", __FUNCTION__, tag_id,
280           updated_regions[i].left, updated_regions[i].top,
281           updated_regions[i].right, updated_regions[i].bottom,
282           updated_regions[i].weight);
283   }
284   res = metadata->Set(tag_id, reinterpret_cast<int32_t*>(updated_regions.data()),
285                       updated_regions.size() * kNumElementsInTuple);
286   if (res != OK) {
287     ALOGE("%s: Updating region(%d) failed: %s (%d)", __FUNCTION__, tag_id,
288           strerror(-res), res);
289   }
290 }
291 
UpdatePoints(const float zoom_ratio,const uint32_t tag_id,const Dimension & active_array_dimension,HalCameraMetadata * metadata)292 void ZoomRatioMapper::UpdatePoints(const float zoom_ratio, const uint32_t tag_id,
293                                    const Dimension& active_array_dimension,
294                                    HalCameraMetadata* metadata) {
295   if (metadata == nullptr) {
296     ALOGE("%s: metadata is nullptr", __FUNCTION__);
297     return;
298   }
299   camera_metadata_ro_entry entry = {};
300   if (metadata->Get(tag_id, &entry) != OK) {
301     ALOGV("%s: tag: %u not published.", __FUNCTION__, tag_id);
302     return;
303   }
304 
305   if (entry.count <= 0) {
306     ALOGV("%s: No data found, tag: %u", __FUNCTION__, tag_id);
307     return;
308   }
309   // x, y
310   const uint32_t kDataSizePerPoint = 2;
311   const uint32_t point_num = entry.count / kDataSizePerPoint;
312   std::vector<PointI> points(point_num);
313   uint32_t data_index = 0;
314 
315   for (uint32_t i = 0; i < point_num; i++) {
316     data_index = i * kDataSizePerPoint;
317     PointI* transformed = &points.at(i);
318     transformed->x = entry.data.i32[data_index];
319     transformed->y = entry.data.i32[data_index + 1];
320     utils::RevertZoomRatio(zoom_ratio, active_array_dimension, true,
321                            &transformed->x, &transformed->y);
322   }
323 
324   status_t res = metadata->Set(
325       tag_id, reinterpret_cast<int32_t*>(points.data()), entry.count);
326 
327   if (res != OK) {
328     ALOGE("%s: Updating tag: %u failed: %s (%d)", __FUNCTION__, tag_id,
329           strerror(-res), res);
330   }
331 }
332 
333 }  // namespace google_camera_hal
334 }  // namespace android
335