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