1 /*
2 * Copyright (C) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "core/common/ai/image_analyzer_manager.h"
17
18 #include "interfaces/inner_api/ace/ai/image_analyzer.h"
19 #include "js_native_api_types.h"
20
21 #include "base/geometry/offset.h"
22 #include "base/image/pixel_map.h"
23 #include "base/utils/utils.h"
24 #include "core/common/ai/image_analyzer_adapter.h"
25 #include "core/common/ai/image_analyzer_mgr.h"
26 #include "core/components_ng/base/view_stack_processor.h"
27 #include "core/components_ng/pattern/image/image_pattern.h"
28 #include "core/components_ng/pattern/image/image_render_property.h"
29 #include "core/components_ng/pattern/video/video_layout_property.h"
30 #include "core/components_ng/property/measure_property.h"
31
32 namespace OHOS::Ace {
33
ImageAnalyzerManager(const RefPtr<NG::FrameNode> & frameNode,ImageAnalyzerHolder holder)34 ImageAnalyzerManager::ImageAnalyzerManager(const RefPtr<NG::FrameNode>& frameNode, ImageAnalyzerHolder holder)
35 : frameNode_(frameNode), holder_(holder)
36 {
37 imageAnalyzerAdapter_ = std::shared_ptr<ImageAnalyzerAdapter>(CreateImageAnalyzerAdapter());
38 }
39
CreateAnalyzerOverlay(const RefPtr<OHOS::Ace::PixelMap> & pixelMap,const NG::OffsetF & offset)40 void ImageAnalyzerManager::CreateAnalyzerOverlay(const RefPtr<OHOS::Ace::PixelMap>& pixelMap,
41 const NG::OffsetF& offset)
42 {
43 CHECK_NULL_VOID(imageAnalyzerAdapter_);
44 void* pixelmapNapiVal = nullptr;
45 if (pixelMap) {
46 pixelmapNapiVal = imageAnalyzerAdapter_->ConvertPixmapNapi(pixelMap);
47 }
48
49 analyzerUIConfig_.holder = holder_;
50 if (holder_ != ImageAnalyzerHolder::IMAGE && holder_ != ImageAnalyzerHolder::WEB) {
51 analyzerUIConfig_.contentWidth = pixelMap->GetWidth();
52 analyzerUIConfig_.contentHeight = pixelMap->GetHeight();
53 }
54
55 if (holder_ == ImageAnalyzerHolder::VIDEO_CUSTOM) {
56 analyzerUIConfig_.pixelMapWidth = pixelMap->GetWidth();
57 analyzerUIConfig_.pixelMapHeight = pixelMap->GetHeight();
58 analyzerUIConfig_.overlayOffset = offset;
59 }
60
61 RefPtr<NG::UINode> customNode;
62 {
63 NG::ScopedViewStackProcessor builderViewStackProcessor;
64 auto analyzerConfig = imageAnalyzerAdapter_->GetImageAnalyzerConfig();
65 ImageAnalyzerMgr::GetInstance().BuildNodeFunc(
66 pixelmapNapiVal, analyzerConfig, &analyzerUIConfig_, &overlayData_);
67 customNode = NG::ViewStackProcessor::GetInstance()->Finish();
68 }
69 auto overlayNode = AceType::DynamicCast<NG::FrameNode>(customNode);
70 CHECK_NULL_VOID(overlayNode);
71 auto node = frameNode_.Upgrade();
72 CHECK_NULL_VOID(node);
73 node->SetOverlayNode(overlayNode);
74 overlayNode->SetParent(AceType::WeakClaim(AceType::RawPtr(node)));
75 overlayNode->SetActive(true);
76 UpdateAnalyzerOverlayLayout();
77
78 auto renderContext = overlayNode->GetRenderContext();
79 CHECK_NULL_VOID(renderContext);
80 renderContext->UpdateZIndex(INT32_MAX);
81 auto focusHub = overlayNode->GetOrCreateFocusHub();
82 CHECK_NULL_VOID(focusHub);
83 focusHub->SetFocusable(false);
84 overlayNode->MarkDirtyNode(NG::PROPERTY_UPDATE_MEASURE_SELF);
85
86 isAnalyzerOverlayBuild_ = true;
87 CHECK_NULL_VOID(analyzerUIConfig_.onAnalyzed);
88 (analyzerUIConfig_.onAnalyzed.value())(ImageAnalyzerState::FINISHED);
89 analyzerUIConfig_.onAnalyzed = std::nullopt;
90 }
91
UpdateAnalyzerOverlay(const RefPtr<OHOS::Ace::PixelMap> & pixelMap,const NG::OffsetF & offset)92 void ImageAnalyzerManager::UpdateAnalyzerOverlay(const RefPtr<OHOS::Ace::PixelMap>& pixelMap,
93 const NG::OffsetF& offset)
94 {
95 if (!isAnalyzerOverlayBuild_) {
96 return;
97 }
98
99 auto node = frameNode_.Upgrade();
100 CHECK_NULL_VOID(node);
101 if (holder_ == ImageAnalyzerHolder::IMAGE) {
102 auto imagePattern = AceType::DynamicCast<NG::ImagePattern>(node->GetPattern());
103 CHECK_NULL_VOID(imagePattern);
104 if (!imagePattern->hasSceneChanged()) {
105 return;
106 }
107 }
108
109 CHECK_NULL_VOID(pixelMap);
110 if (holder_ == ImageAnalyzerHolder::VIDEO_CUSTOM) {
111 analyzerUIConfig_.pixelMapWidth = pixelMap->GetWidth();
112 analyzerUIConfig_.pixelMapHeight = pixelMap->GetHeight();
113 analyzerUIConfig_.overlayOffset = offset;
114 }
115
116 if (holder_ != ImageAnalyzerHolder::IMAGE) {
117 analyzerUIConfig_.contentWidth = pixelMap->GetWidth();
118 analyzerUIConfig_.contentHeight = pixelMap->GetHeight();
119 }
120
121 CHECK_NULL_VOID(imageAnalyzerAdapter_);
122 auto pixelmapNapiVal = imageAnalyzerAdapter_->ConvertPixmapNapi(pixelMap);
123 auto overlayNode = node->GetOverlayNode();
124 CHECK_NULL_VOID(overlayNode);
125 auto analyzerConfig = imageAnalyzerAdapter_->GetImageAnalyzerConfig();
126 ImageAnalyzerMgr::GetInstance().UpdateImage(&overlayData_, pixelmapNapiVal, analyzerConfig, &analyzerUIConfig_);
127 overlayNode->MarkDirtyNode(NG::PROPERTY_UPDATE_MEASURE_SELF);
128 }
129
DestroyAnalyzerOverlay()130 void ImageAnalyzerManager::DestroyAnalyzerOverlay()
131 {
132 ReleaseImageAnalyzer();
133
134 if (!isAnalyzerOverlayBuild_) {
135 return;
136 }
137 auto node = frameNode_.Upgrade();
138 CHECK_NULL_VOID(node);
139 auto overlayNode = node->GetOverlayNode();
140 CHECK_NULL_VOID(overlayNode);
141 node->SetOverlayNode(RefPtr<NG::FrameNode>());
142
143 isAnalyzerOverlayBuild_ = false;
144 CHECK_NULL_VOID(analyzerUIConfig_.onAnalyzed);
145 (analyzerUIConfig_.onAnalyzed.value())(ImageAnalyzerState::STOPPED);
146 analyzerUIConfig_.onAnalyzed = std::nullopt;
147
148 napi_value nullValue = nullptr;
149 CHECK_NULL_VOID(imageAnalyzerAdapter_);
150 imageAnalyzerAdapter_->SetImageAnalyzerConfig(nullValue);
151 }
152
IsSupportImageAnalyzerFeature()153 bool ImageAnalyzerManager::IsSupportImageAnalyzerFeature()
154 {
155 auto node = frameNode_.Upgrade();
156 CHECK_NULL_RETURN(node, false);
157 auto eventHub = node->GetEventHub<NG::EventHub>();
158 CHECK_NULL_RETURN(eventHub, false);
159 if (!eventHub->IsEnabled()) {
160 return false;
161 }
162
163 bool hasObscured = false;
164 if (node->GetRenderContext()->GetObscured().has_value()) {
165 auto obscuredReasons = node->GetRenderContext()->GetObscured().value();
166 hasObscured = std::any_of(obscuredReasons.begin(), obscuredReasons.end(),
167 [](const auto& reason) { return reason == ObscuredReasons::PLACEHOLDER; });
168 if (hasObscured) {
169 return false;
170 }
171 }
172
173 if (holder_ == ImageAnalyzerHolder::IMAGE) {
174 auto imageRenderProperty = node->GetPaintProperty<NG::ImageRenderProperty>();
175 CHECK_NULL_RETURN(imageRenderProperty, false);
176 ImageRepeat repeat = imageRenderProperty->GetImageRepeat().value_or(ImageRepeat::NO_REPEAT);
177 if (repeat != ImageRepeat::NO_REPEAT) {
178 return false;
179 }
180 }
181
182 return ImageAnalyzerMgr::GetInstance().IsImageAnalyzerSupported();
183 }
184
IsOverlayCreated()185 bool ImageAnalyzerManager::IsOverlayCreated()
186 {
187 return isAnalyzerOverlayBuild_;
188 }
189
UpdateAnalyzerOverlayLayout()190 void ImageAnalyzerManager::UpdateAnalyzerOverlayLayout()
191 {
192 auto node = frameNode_.Upgrade();
193 CHECK_NULL_VOID(node);
194 auto layoutProperty = node->GetLayoutProperty();
195 CHECK_NULL_VOID(layoutProperty);
196 auto padding = layoutProperty->CreatePaddingAndBorder();
197 auto overlayNode = node->GetOverlayNode();
198 CHECK_NULL_VOID(overlayNode);
199 auto overlayLayoutProperty = overlayNode->GetLayoutProperty();
200 CHECK_NULL_VOID(overlayLayoutProperty);
201 overlayLayoutProperty->UpdateMeasureType(NG::MeasureType::MATCH_PARENT);
202 overlayLayoutProperty->UpdateAlignment(Alignment::TOP_LEFT);
203 if (holder_ == ImageAnalyzerHolder::IMAGE || holder_ == ImageAnalyzerHolder::VIDEO_CUSTOM) {
204 overlayLayoutProperty->SetOverlayOffset(Dimension(padding.Offset().GetX()),
205 Dimension(padding.Offset().GetY()));
206 if (holder_ == ImageAnalyzerHolder::IMAGE) {
207 auto renderContext = overlayNode->GetRenderContext();
208 CHECK_NULL_VOID(renderContext);
209 renderContext->SetRenderFrameOffset({ -padding.Offset().GetX(), -padding.Offset().GetY() });
210 }
211 }
212 }
213
UpdateAnalyzerUIConfig(const RefPtr<NG::GeometryNode> & geometryNode,const PixelMapInfo & info)214 void ImageAnalyzerManager::UpdateAnalyzerUIConfig(const RefPtr<NG::GeometryNode>& geometryNode,
215 const PixelMapInfo& info)
216 {
217 CHECK_NULL_VOID(geometryNode);
218 auto node = frameNode_.Upgrade();
219 CHECK_NULL_VOID(node);
220 bool isUIConfigUpdate = false;
221
222 auto layoutProps = node->GetLayoutProperty();
223 CHECK_NULL_VOID(layoutProps);
224 if (holder_ == ImageAnalyzerHolder::IMAGE) {
225 auto props = DynamicCast<NG::ImageLayoutProperty>(layoutProps);
226 CHECK_NULL_VOID(props);
227 if (analyzerUIConfig_.imageFit != props->GetImageFit().value_or(ImageFit::COVER)) {
228 analyzerUIConfig_.imageFit = props->GetImageFit().value_or(ImageFit::COVER);
229 isUIConfigUpdate = true;
230 }
231 }
232
233 if (holder_ == ImageAnalyzerHolder::VIDEO_CUSTOM) {
234 isUIConfigUpdate = UpdateVideoConfig(info);
235 } else {
236 auto padding = layoutProps->CreatePaddingAndBorder();
237 float paddingWidth = holder_ == ImageAnalyzerHolder::IMAGE ? padding.left.value_or(0) +
238 padding.right.value_or(0) : 0.0f;
239 float paddingHeight = holder_ == ImageAnalyzerHolder::IMAGE ? padding.top.value_or(0) +
240 padding.bottom.value_or(0) : 0.0f;
241 NG::SizeF frameSize = geometryNode->GetFrameSize();
242 bool shouldUpdateSize = analyzerUIConfig_.contentWidth != frameSize.Width() - paddingWidth ||
243 analyzerUIConfig_.contentHeight != frameSize.Height() - paddingHeight;
244 if (shouldUpdateSize) {
245 analyzerUIConfig_.contentWidth = frameSize.Width() - paddingWidth;
246 analyzerUIConfig_.contentHeight = frameSize.Height()- paddingHeight;
247 isUIConfigUpdate = true;
248 }
249 }
250
251 auto renderContext = node->GetRenderContext();
252 CHECK_NULL_VOID(renderContext);
253 auto transformMat = renderContext->GetTransformMatrixValue(Matrix4::CreateIdentity());
254 if (!(analyzerUIConfig_.transformMat == transformMat)) {
255 analyzerUIConfig_.transformMat = transformMat;
256 isUIConfigUpdate = true;
257 }
258
259 if (isUIConfigUpdate) {
260 ImageAnalyzerMgr::GetInstance().UpdateInnerConfig(&overlayData_, &analyzerUIConfig_);
261 }
262 }
263
UpdateVideoConfig(const PixelMapInfo & info)264 bool ImageAnalyzerManager::UpdateVideoConfig(const PixelMapInfo& info)
265 {
266 bool shouldUpdateFit = false;
267 auto node = frameNode_.Upgrade();
268 CHECK_NULL_RETURN(node, false);
269 auto layoutProps = node->GetLayoutProperty();
270 CHECK_NULL_RETURN(layoutProps, false);
271 auto videoProps = DynamicCast<NG::VideoLayoutProperty>(layoutProps);
272 if (analyzerUIConfig_.imageFit != videoProps->GetObjectFitValue(ImageFit::COVER)) {
273 analyzerUIConfig_.imageFit = videoProps->GetObjectFitValue(ImageFit::COVER);
274 shouldUpdateFit = true;
275 }
276
277 bool shouldUpdateSize = analyzerUIConfig_.contentWidth != info.width ||
278 analyzerUIConfig_.contentHeight != info.height ||
279 analyzerUIConfig_.overlayOffset != info.overlayOffset;
280 if (shouldUpdateSize) {
281 analyzerUIConfig_.UpdateFromInfo(info);
282 }
283 return shouldUpdateFit || shouldUpdateSize;
284 }
285
SetImageAnalyzerConfig(void * config)286 void ImageAnalyzerManager::SetImageAnalyzerConfig(void* config)
287 {
288 CHECK_NULL_VOID(imageAnalyzerAdapter_);
289 bool hasConfig = imageAnalyzerAdapter_->HasImageAnalyzerConfig();
290 if (hasConfig) {
291 return;
292 }
293 imageAnalyzerAdapter_->SetImageAnalyzerConfig(config);
294 auto analyzerConfig = imageAnalyzerAdapter_->GetImageAnalyzerConfig();
295 if (isAnalyzerOverlayBuild_) {
296 ImageAnalyzerMgr::GetInstance().UpdateConfig(&overlayData_, analyzerConfig);
297 }
298 }
299
SetImageAIOptions(void * options)300 void ImageAnalyzerManager::SetImageAIOptions(void* options)
301 {
302 CHECK_NULL_VOID(imageAnalyzerAdapter_);
303 imageAnalyzerAdapter_->SetImageAnalyzerConfig(options, true);
304 auto analyzerConfig = imageAnalyzerAdapter_->GetImageAnalyzerConfig();
305 if (isAnalyzerOverlayBuild_) {
306 ImageAnalyzerMgr::GetInstance().UpdateConfig(&overlayData_, analyzerConfig);
307 }
308 }
309
SetImageAnalyzerCallback(OnAnalyzedCallback & callback)310 void ImageAnalyzerManager::SetImageAnalyzerCallback(OnAnalyzedCallback& callback)
311 {
312 analyzerUIConfig_.onAnalyzed = callback;
313 }
314
ReleaseImageAnalyzer()315 void ImageAnalyzerManager::ReleaseImageAnalyzer()
316 {
317 if (isAnalyzerOverlayBuild_) {
318 ImageAnalyzerMgr::GetInstance().Release(&overlayData_);
319 }
320 }
321
UpdatePressOverlay(const RefPtr<OHOS::Ace::PixelMap> & pixelMap,int offsetX,int offsetY,int rectWidth,int rectHeight,int pointX,int pointY,OnTextSelectedCallback callback)322 void ImageAnalyzerManager::UpdatePressOverlay(const RefPtr<OHOS::Ace::PixelMap>& pixelMap, int offsetX, int offsetY,
323 int rectWidth, int rectHeight, int pointX, int pointY, OnTextSelectedCallback callback)
324 {
325 analyzerUIConfig_.overlayOffset.SetX(offsetX);
326 analyzerUIConfig_.overlayOffset.SetY(offsetY);
327 if (rectWidth > 0 && rectHeight > 0) {
328 analyzerUIConfig_.touchInfo.touchPoint.x = 1.0 * pointX / rectWidth * pixelMap->GetWidth();
329 analyzerUIConfig_.touchInfo.touchPoint.y = 1.0 * pointY / rectHeight * pixelMap->GetHeight();
330 }
331 analyzerUIConfig_.touchInfo.touchType = TouchType::DOWN;
332 analyzerUIConfig_.selectedStatus = Status::SELECTED;
333 analyzerUIConfig_.menuStatus = Status::MENU_SHOW;
334 if (!analyzerUIConfig_.onTextSelected) {
335 analyzerUIConfig_.onTextSelected = std::move(callback);
336 }
337 if (pixelMap && imageAnalyzerAdapter_) {
338 analyzerUIConfig_.contentWidth = rectWidth;
339 analyzerUIConfig_.contentHeight = rectHeight;
340 analyzerUIConfig_.pixelMapWidth = pixelMap->GetWidth();
341 analyzerUIConfig_.pixelMapHeight = pixelMap->GetHeight();
342 analyzerUIConfig_.pixelmapNapiVal = imageAnalyzerAdapter_->ConvertPixmapNapi(pixelMap);
343 }
344 ImageAnalyzerMgr::GetInstance().UpdatePressOverlay(&overlayData_, &analyzerUIConfig_);
345 analyzerUIConfig_.pixelmapNapiVal = nullptr;
346 }
347
UpdateOverlayTouchInfo(int touchPointX,int touchPointY,TouchType touchType)348 void ImageAnalyzerManager::UpdateOverlayTouchInfo(int touchPointX, int touchPointY, TouchType touchType)
349 {
350 analyzerUIConfig_.touchInfo.touchPoint.x = touchPointX - analyzerUIConfig_.overlayOffset.GetX();
351 analyzerUIConfig_.touchInfo.touchPoint.y = touchPointY - analyzerUIConfig_.overlayOffset.GetY();
352 analyzerUIConfig_.touchInfo.touchType = touchType;
353 ImageAnalyzerMgr::GetInstance().UpdatePressOverlay(&overlayData_, &analyzerUIConfig_);
354 }
355
UpdateOverlayStatus(bool status,int offsetX,int offsetY,int rectWidth,int rectHeight)356 void ImageAnalyzerManager::UpdateOverlayStatus(bool status, int offsetX, int offsetY, int rectWidth, int rectHeight)
357 {
358 if (status) {
359 analyzerUIConfig_.overlayOffset.SetX(offsetX);
360 analyzerUIConfig_.overlayOffset.SetY(offsetY);
361 analyzerUIConfig_.contentWidth = rectWidth;
362 analyzerUIConfig_.contentHeight = rectHeight;
363 analyzerUIConfig_.selectedStatus = Status::SELECTED;
364 analyzerUIConfig_.menuStatus = Status::MENU_SHOW;
365 } else {
366 analyzerUIConfig_.selectedStatus = Status::UNSELECTED;
367 analyzerUIConfig_.menuStatus = Status::MENU_HIDE;
368 }
369 ImageAnalyzerMgr::GetInstance().UpdateOverlayStatus(&overlayData_, &analyzerUIConfig_);
370 }
371
UpdateAIButtonConfig(AIButtonConfig config)372 void ImageAnalyzerManager::UpdateAIButtonConfig(AIButtonConfig config)
373 {
374 CHECK_NULL_VOID(isAnalyzerOverlayBuild_);
375 ImageAnalyzerMgr::GetInstance().UpdateAIButtonConfig(&overlayData_, &config);
376 }
377
UpdateOverlayActiveStatus(bool status)378 void ImageAnalyzerManager::UpdateOverlayActiveStatus(bool status)
379 {
380 CHECK_NULL_VOID(isAnalyzerOverlayBuild_);
381 ImageAnalyzerMgr::GetInstance().UpdateOverlayActiveStatus(&overlayData_, status);
382 }
383
SetNotifySelectedCallback(OnNotifySelectedStatusCallback && callback)384 void ImageAnalyzerManager::SetNotifySelectedCallback(
385 OnNotifySelectedStatusCallback&& callback)
386 {
387 analyzerUIConfig_.onNotifySelectedStatus = std::move(callback);
388 }
389 } // namespace OHOS::Ace