1 /*
2 * Copyright (c) 2021-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 "js_rendering_context.h"
17
18 #include <cstdint>
19
20 #include "interfaces/inner_api/ace/ai/image_analyzer.h"
21 #include "js_native_api.h"
22 #include "js_native_api_types.h"
23
24 #include "base/error/error_code.h"
25 #include "base/memory/referenced.h"
26 #include "base/log/ace_scoring_log.h"
27 #include "base/utils/utils.h"
28 #include "bridge/common/utils/engine_helper.h"
29 #include "bridge/declarative_frontend/engine/bindings.h"
30 #include "bridge/declarative_frontend/engine/js_converter.h"
31 #include "bridge/declarative_frontend/engine/js_types.h"
32 #include "bridge/declarative_frontend/jsview/canvas/js_offscreen_rendering_context.h"
33 #include "bridge/declarative_frontend/jsview/js_utils.h"
34 #include "bridge/declarative_frontend/jsview/models/canvas/canvas_rendering_context_2d_model_impl.h"
35 #include "core/common/container_scope.h"
36 #include "core/components_ng/pattern/canvas/canvas_rendering_context_2d_model_ng.h"
37
38 namespace OHOS::Ace {
39 struct CanvasAsyncCxt {
40 napi_env env = nullptr;
41 napi_deferred deferred = nullptr;
42 };
43 } // namespace OHOS::Ace
44
45 namespace OHOS::Ace::Framework {
46
JSRenderingContext()47 JSRenderingContext::JSRenderingContext()
48 {
49 #ifdef NG_BUILD
50 renderingContext2DModel_ = AceType::MakeRefPtr<NG::CanvasRenderingContext2DModelNG>();
51 #else
52 if (Container::IsCurrentUseNewPipeline()) {
53 renderingContext2DModel_ = AceType::MakeRefPtr<NG::CanvasRenderingContext2DModelNG>();
54 auto onAttach = [weakCtx = WeakClaim(this)]() {
55 auto ctx = weakCtx.Upgrade();
56 CHECK_NULL_VOID(ctx);
57 ctx->OnAttachToCanvas();
58 };
59 auto onDetach = [weakCtx = WeakClaim(this)]() {
60 auto ctx = weakCtx.Upgrade();
61 CHECK_NULL_VOID(ctx);
62 ctx->OnDetachFromCanvas();
63 };
64 auto canvasRenderingContext2DModel =
65 AceType::DynamicCast<CanvasRenderingContext2DModel>(renderingContext2DModel_);
66 CHECK_NULL_VOID(canvasRenderingContext2DModel);
67 canvasRenderingContext2DModel->SetOnAttach(onAttach);
68 canvasRenderingContext2DModel->SetOnDetach(onDetach);
69 } else {
70 renderingContext2DModel_ = AceType::MakeRefPtr<Framework::CanvasRenderingContext2DModelImpl>();
71 }
72 #endif
73 }
74
JSBind(BindingTarget globalObj)75 void JSRenderingContext::JSBind(BindingTarget globalObj)
76 {
77 // Define the class "CanvasRenderingContext2D"
78 JSClass<JSRenderingContext>::Declare("CanvasRenderingContext2D");
79
80 // Define all properties of the "CanvasRenderingContext2D"
81 JSClass<JSRenderingContext>::CustomProperty(
82 "canvas", &JSRenderingContext::JsGetCanvas, &JSRenderingContext::JsSetCanvas);
83 JSClass<JSRenderingContext>::CustomProperty(
84 "width", &JSRenderingContext::JsGetWidth, &JSRenderingContext::JsSetWidth);
85 JSClass<JSRenderingContext>::CustomProperty(
86 "height", &JSRenderingContext::JsGetHeight, &JSRenderingContext::JsSetHeight);
87 JSClass<JSRenderingContext>::CustomProperty(
88 "filter", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetFilter);
89 JSClass<JSRenderingContext>::CustomProperty(
90 "direction", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetDirection);
91 JSClass<JSRenderingContext>::CustomProperty(
92 "fillStyle", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetFillStyle);
93 JSClass<JSRenderingContext>::CustomProperty(
94 "strokeStyle", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetStrokeStyle);
95 JSClass<JSRenderingContext>::CustomProperty(
96 "lineCap", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetLineCap);
97 JSClass<JSRenderingContext>::CustomProperty(
98 "lineJoin", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetLineJoin);
99 JSClass<JSRenderingContext>::CustomProperty(
100 "miterLimit", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetMiterLimit);
101 JSClass<JSRenderingContext>::CustomProperty(
102 "lineWidth", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetLineWidth);
103 JSClass<JSRenderingContext>::CustomProperty("font", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetFont);
104 JSClass<JSRenderingContext>::CustomProperty(
105 "textAlign", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetTextAlign);
106 JSClass<JSRenderingContext>::CustomProperty(
107 "textBaseline", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetTextBaseline);
108 JSClass<JSRenderingContext>::CustomProperty(
109 "globalAlpha", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetGlobalAlpha);
110 JSClass<JSRenderingContext>::CustomProperty(
111 "globalCompositeOperation", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetGlobalCompositeOperation);
112 JSClass<JSRenderingContext>::CustomProperty(
113 "lineDashOffset", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetLineDashOffset);
114 JSClass<JSRenderingContext>::CustomProperty(
115 "shadowBlur", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetShadowBlur);
116 JSClass<JSRenderingContext>::CustomProperty(
117 "shadowColor", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetShadowColor);
118 JSClass<JSRenderingContext>::CustomProperty(
119 "shadowOffsetX", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetShadowOffsetX);
120 JSClass<JSRenderingContext>::CustomProperty(
121 "shadowOffsetY", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetShadowOffsetY);
122 JSClass<JSRenderingContext>::CustomProperty(
123 "imageSmoothingEnabled", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetImageSmoothingEnabled);
124 JSClass<JSRenderingContext>::CustomProperty(
125 "imageSmoothingQuality", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetImageSmoothingQuality);
126 JSClass<JSRenderingContext>::CustomProperty(
127 "letterSpacing", &JSCanvasRenderer::JSGetEmpty, &JSCanvasRenderer::JsSetLetterSpacing);
128
129 // Define all methods of the "CanvasRenderingContext2D"
130 JSClass<JSRenderingContext>::CustomMethod("toDataURL", &JSCanvasRenderer::JsToDataUrl);
131 JSClass<JSRenderingContext>::CustomMethod("createRadialGradient", &JSCanvasRenderer::JsCreateRadialGradient);
132 JSClass<JSRenderingContext>::CustomMethod("fillRect", &JSCanvasRenderer::JsFillRect);
133 JSClass<JSRenderingContext>::CustomMethod("strokeRect", &JSCanvasRenderer::JsStrokeRect);
134 JSClass<JSRenderingContext>::CustomMethod("clearRect", &JSCanvasRenderer::JsClearRect);
135 JSClass<JSRenderingContext>::CustomMethod("createLinearGradient", &JSCanvasRenderer::JsCreateLinearGradient);
136 JSClass<JSRenderingContext>::CustomMethod("fillText", &JSCanvasRenderer::JsFillText);
137 JSClass<JSRenderingContext>::CustomMethod("strokeText", &JSCanvasRenderer::JsStrokeText);
138 JSClass<JSRenderingContext>::CustomMethod("measureText", &JSCanvasRenderer::JsMeasureText);
139 JSClass<JSRenderingContext>::CustomMethod("moveTo", &JSCanvasRenderer::JsMoveTo);
140 JSClass<JSRenderingContext>::CustomMethod("lineTo", &JSCanvasRenderer::JsLineTo);
141 JSClass<JSRenderingContext>::CustomMethod("bezierCurveTo", &JSCanvasRenderer::JsBezierCurveTo);
142 JSClass<JSRenderingContext>::CustomMethod("quadraticCurveTo", &JSCanvasRenderer::JsQuadraticCurveTo);
143 JSClass<JSRenderingContext>::CustomMethod("arcTo", &JSCanvasRenderer::JsArcTo);
144 JSClass<JSRenderingContext>::CustomMethod("arc", &JSCanvasRenderer::JsArc);
145 JSClass<JSRenderingContext>::CustomMethod("ellipse", &JSCanvasRenderer::JsEllipse);
146 JSClass<JSRenderingContext>::CustomMethod("fill", &JSCanvasRenderer::JsFill);
147 JSClass<JSRenderingContext>::CustomMethod("stroke", &JSCanvasRenderer::JsStroke);
148 JSClass<JSRenderingContext>::CustomMethod("clip", &JSCanvasRenderer::JsClip);
149 JSClass<JSRenderingContext>::CustomMethod("rect", &JSCanvasRenderer::JsRect);
150 JSClass<JSRenderingContext>::CustomMethod("beginPath", &JSCanvasRenderer::JsBeginPath);
151 JSClass<JSRenderingContext>::CustomMethod("closePath", &JSCanvasRenderer::JsClosePath);
152 JSClass<JSRenderingContext>::CustomMethod("restore", &JSCanvasRenderer::JsRestore);
153 JSClass<JSRenderingContext>::CustomMethod("save", &JSCanvasRenderer::JsSave);
154 JSClass<JSRenderingContext>::CustomMethod("rotate", &JSCanvasRenderer::JsRotate);
155 JSClass<JSRenderingContext>::CustomMethod("scale", &JSCanvasRenderer::JsScale);
156 JSClass<JSRenderingContext>::CustomMethod("getTransform", &JSCanvasRenderer::JsGetTransform);
157 JSClass<JSRenderingContext>::CustomMethod("setTransform", &JSCanvasRenderer::JsSetTransform);
158 JSClass<JSRenderingContext>::CustomMethod("resetTransform", &JSCanvasRenderer::JsResetTransform);
159 JSClass<JSRenderingContext>::CustomMethod("transform", &JSCanvasRenderer::JsTransform);
160 JSClass<JSRenderingContext>::CustomMethod("translate", &JSCanvasRenderer::JsTranslate);
161 JSClass<JSRenderingContext>::CustomMethod("setLineDash", &JSCanvasRenderer::JsSetLineDash);
162 JSClass<JSRenderingContext>::CustomMethod("getLineDash", &JSCanvasRenderer::JsGetLineDash);
163 JSClass<JSRenderingContext>::CustomMethod("drawImage", &JSCanvasRenderer::JsDrawImage);
164 JSClass<JSRenderingContext>::CustomMethod("createPattern", &JSCanvasRenderer::JsCreatePattern);
165 JSClass<JSRenderingContext>::CustomMethod("createImageData", &JSCanvasRenderer::JsCreateImageData);
166 JSClass<JSRenderingContext>::CustomMethod("putImageData", &JSCanvasRenderer::JsPutImageData);
167 JSClass<JSRenderingContext>::CustomMethod("getImageData", &JSCanvasRenderer::JsGetImageData);
168 JSClass<JSRenderingContext>::CustomMethod("getJsonData", &JSCanvasRenderer::JsGetJsonData);
169 JSClass<JSRenderingContext>::CustomMethod("getPixelMap", &JSCanvasRenderer::JsGetPixelMap);
170 JSClass<JSRenderingContext>::CustomMethod("setPixelMap", &JSCanvasRenderer::JsSetPixelMap);
171 JSClass<JSRenderingContext>::CustomMethod("drawBitmapMesh", &JSCanvasRenderer::JsDrawBitmapMesh);
172 JSClass<JSRenderingContext>::CustomMethod(
173 "transferFromImageBitmap", &JSRenderingContext::JsTransferFromImageBitmap);
174 JSClass<JSRenderingContext>::CustomMethod("createConicGradient", &JSCanvasRenderer::JsCreateConicGradient);
175 JSClass<JSRenderingContext>::CustomMethod("saveLayer", &JSCanvasRenderer::JsSaveLayer);
176 JSClass<JSRenderingContext>::CustomMethod("restoreLayer", &JSCanvasRenderer::JsRestoreLayer);
177 JSClass<JSRenderingContext>::CustomMethod("reset", &JSCanvasRenderer::JsReset);
178 JSClass<JSRenderingContext>::CustomMethod("startImageAnalyzer", &JSRenderingContext::JsStartImageAnalyzer);
179 JSClass<JSRenderingContext>::CustomMethod("stopImageAnalyzer", &JSRenderingContext::JsStopImageAnalyzer);
180 JSClass<JSRenderingContext>::CustomMethod("on", &JSRenderingContext::JsOn);
181 JSClass<JSRenderingContext>::CustomMethod("off", &JSRenderingContext::JsOff);
182
183 // Register the "CanvasRenderingContext2D" to the global object of the vm
184 JSClass<JSRenderingContext>::Bind(globalObj, JSRenderingContext::Constructor, JSRenderingContext::Destructor);
185 }
186
Constructor(const JSCallbackInfo & args)187 void JSRenderingContext::Constructor(const JSCallbackInfo& args)
188 {
189 auto jsRenderContext = Referenced::MakeRefPtr<JSRenderingContext>();
190 jsRenderContext->IncRefCount();
191 args.SetReturnValue(Referenced::RawPtr(jsRenderContext));
192
193 auto* jsContextSetting = args.UnwrapArg<JSRenderingContextSettings>(0);
194 if (jsContextSetting) {
195 bool anti = jsContextSetting->GetAntialias();
196 jsRenderContext->SetAnti(anti);
197
198 int32_t unit = 0;
199 if (args.GetInt32Arg(1, unit) && (static_cast<CanvasUnit>(unit) == CanvasUnit::PX)) {
200 jsRenderContext->SetUnit(CanvasUnit::PX);
201 }
202 }
203 }
204
Destructor(JSRenderingContext * controller)205 void JSRenderingContext::Destructor(JSRenderingContext* controller)
206 {
207 if (controller != nullptr) {
208 controller->DecRefCount();
209 }
210 }
211
OnAttachToCanvas()212 void JSRenderingContext::OnAttachToCanvas()
213 {
214 ContainerScope scope(instanceId_);
215 for (const auto& iter : attachCallback_) {
216 auto callback = iter.second;
217 if (callback) {
218 callback();
219 }
220 }
221 }
222
OnDetachFromCanvas()223 void JSRenderingContext::OnDetachFromCanvas()
224 {
225 ContainerScope scope(instanceId_);
226 for (const auto& iter : detachCallback_) {
227 auto callback = iter.second;
228 if (callback) {
229 callback();
230 }
231 }
232 }
233
JsGetCanvas(const JSCallbackInfo & info)234 void JSRenderingContext::JsGetCanvas(const JSCallbackInfo& info)
235 {
236 auto canvasRenderingContext2DModel = AceType::DynamicCast<CanvasRenderingContext2DModel>(renderingContext2DModel_);
237 CHECK_NULL_VOID(canvasRenderingContext2DModel);
238 auto nodeId = canvasRenderingContext2DModel->GetId();
239
240 auto vm = info.GetVm();
241 auto globalObj = JSNApi::GetGlobalObject(vm);
242 auto globalFunc = globalObj->Get(vm, panda::StringRef::NewFromUtf8(vm, "__getFrameNodeByNodeId__"));
243 JsiValue jsiValue(globalFunc);
244 JsiRef<JsiValue> globalFuncRef = JsiRef<JsiValue>::Make(jsiValue);
245 if (!globalFuncRef->IsFunction()) {
246 return;
247 }
248 RefPtr<JsFunction> jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(globalFuncRef));
249 JSRef<JSVal> params[2]; // The count of the function's params is 2.
250 params[0] = JSRef<JSVal>::Make(ToJSValue(instanceId_));
251 params[1] = JSRef<JSVal>::Make(ToJSValue(nodeId));
252 auto returnPtr = jsFunc->ExecuteJS(2, params);
253 info.SetReturnValue(returnPtr);
254 return;
255 }
256
JsSetCanvas(const JSCallbackInfo & info)257 void JSRenderingContext::JsSetCanvas(const JSCallbackInfo& info)
258 {
259 return;
260 }
261
JsGetWidth(const JSCallbackInfo & info)262 void JSRenderingContext::JsGetWidth(const JSCallbackInfo& info)
263 {
264 double width = 0.0;
265 auto canvasRenderingContext2DModel = AceType::DynamicCast<CanvasRenderingContext2DModel>(renderingContext2DModel_);
266 CHECK_NULL_VOID(canvasRenderingContext2DModel);
267 canvasRenderingContext2DModel->GetWidth(width);
268 double density = GetDensity();
269 width /= density;
270 auto returnValue = JSVal(ToJSValue(width));
271 auto returnPtr = JSRef<JSVal>::Make(returnValue);
272 info.SetReturnValue(returnPtr);
273 }
274
JsSetWidth(const JSCallbackInfo & info)275 void JSRenderingContext::JsSetWidth(const JSCallbackInfo& info)
276 {
277 return;
278 }
279
JsSetHeight(const JSCallbackInfo & info)280 void JSRenderingContext::JsSetHeight(const JSCallbackInfo& info)
281 {
282 return;
283 }
284
JsGetHeight(const JSCallbackInfo & info)285 void JSRenderingContext::JsGetHeight(const JSCallbackInfo& info)
286 {
287 double height = 0.0;
288 auto canvasRenderingContext2DModel = AceType::DynamicCast<CanvasRenderingContext2DModel>(renderingContext2DModel_);
289 CHECK_NULL_VOID(canvasRenderingContext2DModel);
290 canvasRenderingContext2DModel->GetHeight(height);
291 double density = GetDensity();
292 height /= density;
293 auto returnValue = JSVal(ToJSValue(height));
294 auto returnPtr = JSRef<JSVal>::Make(returnValue);
295 info.SetReturnValue(returnPtr);
296 }
297
JsTransferFromImageBitmap(const JSCallbackInfo & info)298 void JSRenderingContext::JsTransferFromImageBitmap(const JSCallbackInfo& info)
299 {
300 ContainerScope scope(Container::CurrentIdSafely());
301 if (info.Length() == 0) {
302 return;
303 }
304 if (!info[0]->IsObject()) {
305 return;
306 }
307 auto engine = EngineHelper::GetCurrentEngine();
308 CHECK_NULL_VOID(engine);
309 NativeEngine* nativeEngine = engine->GetNativeEngine();
310 napi_env env = reinterpret_cast<napi_env>(nativeEngine);
311 panda::Local<JsiValue> value = info[0].Get().GetLocalHandle();
312 JSValueWrapper valueWrapper = value;
313 napi_value napiValue = nativeEngine->ValueToNapiValue(valueWrapper);
314 void* nativeObj = nullptr;
315 NAPI_CALL_RETURN_VOID(env, napi_unwrap(env, napiValue, &nativeObj));
316 auto jsImage = (JSRenderImage*)nativeObj;
317 CHECK_NULL_VOID(jsImage);
318 auto canvasRenderingContext2DModel = AceType::DynamicCast<CanvasRenderingContext2DModel>(renderingContext2DModel_);
319 CHECK_NULL_VOID(canvasRenderingContext2DModel);
320 #ifdef PIXEL_MAP_SUPPORTED
321 auto pixelMap = jsImage->GetPixelMap();
322 CHECK_NULL_VOID(pixelMap);
323 canvasRenderingContext2DModel->TransferFromImageBitmap(pixelMap);
324 #else
325 auto imageData = jsImage->GetImageData();
326 CHECK_NULL_VOID(imageData);
327 canvasRenderingContext2DModel->TransferFromImageBitmap(imageData);
328 #endif
329 }
330
CreateErrorValue(napi_env env,int32_t errCode,const std::string & errMsg="")331 napi_value CreateErrorValue(napi_env env, int32_t errCode, const std::string& errMsg = "")
332 {
333 napi_value code = nullptr;
334 std::string codeStr = std::to_string(errCode);
335 napi_create_string_utf8(env, codeStr.c_str(), codeStr.length(), &code);
336 napi_value msg = nullptr;
337 napi_create_string_utf8(env, errMsg.c_str(), errMsg.length(), &msg);
338 napi_value error = nullptr;
339 napi_create_error(env, code, msg, &error);
340 return error;
341 }
342
HandleDeferred(const shared_ptr<CanvasAsyncCxt> & asyncCtx,ImageAnalyzerState state)343 void HandleDeferred(const shared_ptr<CanvasAsyncCxt>& asyncCtx, ImageAnalyzerState state)
344 {
345 auto env = asyncCtx->env;
346 CHECK_NULL_VOID(env);
347 auto deferred = asyncCtx->deferred;
348 CHECK_NULL_VOID(deferred);
349
350 napi_handle_scope scope = nullptr;
351 NAPI_CALL_RETURN_VOID(env, napi_open_handle_scope(env, &scope));
352
353 napi_value result = nullptr;
354 switch (state) {
355 case ImageAnalyzerState::UNSUPPORTED:
356 result = CreateErrorValue(env, ERROR_CODE_AI_ANALYSIS_UNSUPPORTED);
357 napi_reject_deferred(env, deferred, result);
358 break;
359 case ImageAnalyzerState::ONGOING:
360 result = CreateErrorValue(env, ERROR_CODE_AI_ANALYSIS_IS_ONGOING);
361 napi_reject_deferred(env, deferred, result);
362 break;
363 case ImageAnalyzerState::STOPPED:
364 result = CreateErrorValue(env, ERROR_CODE_AI_ANALYSIS_IS_STOPPED);
365 napi_reject_deferred(env, deferred, result);
366 break;
367 case ImageAnalyzerState::FINISHED:
368 napi_get_null(env, &result);
369 napi_resolve_deferred(env, deferred, result);
370 break;
371 default:
372 break;
373 }
374 napi_close_handle_scope(env, scope);
375 }
376
ReturnPromise(const JSCallbackInfo & info,napi_value result)377 void ReturnPromise(const JSCallbackInfo& info, napi_value result)
378 {
379 CHECK_NULL_VOID(result);
380 auto jsPromise = JsConverter::ConvertNapiValueToJsVal(result);
381 if (!jsPromise->IsObject()) {
382 return;
383 }
384 info.SetReturnValue(JSRef<JSObject>::Cast(jsPromise));
385 }
386
JsStartImageAnalyzer(const JSCallbackInfo & info)387 void JSRenderingContext::JsStartImageAnalyzer(const JSCallbackInfo& info)
388 {
389 ContainerScope scope(instanceId_);
390 auto engine = EngineHelper::GetCurrentEngine();
391 CHECK_NULL_VOID(engine);
392 NativeEngine* nativeEngine = engine->GetNativeEngine();
393 auto env = reinterpret_cast<napi_env>(nativeEngine);
394 auto asyncCtx = std::make_shared<CanvasAsyncCxt>();
395 asyncCtx->env = env;
396 napi_value promise = nullptr;
397 napi_create_promise(env, &asyncCtx->deferred, &promise);
398 if (info.Length() < 1 || !info[0]->IsObject()) {
399 ReturnPromise(info, promise);
400 return;
401 }
402
403 ScopeRAII scopeRaii(env);
404 panda::Local<JsiValue> value = info[0].Get().GetLocalHandle();
405 JSValueWrapper valueWrapper = value;
406 napi_value configNativeValue = nativeEngine->ValueToNapiValue(valueWrapper);
407
408 if (isImageAnalyzing_) {
409 napi_value result = CreateErrorValue(env, ERROR_CODE_AI_ANALYSIS_IS_ONGOING);
410 napi_reject_deferred(env, asyncCtx->deferred, result);
411 ReturnPromise(info, promise);
412 return;
413 }
414
415 OnAnalyzedCallback onAnalyzed_ = [asyncCtx, weakCtx = WeakClaim(this)](ImageAnalyzerState state) {
416 CHECK_NULL_VOID(asyncCtx);
417 HandleDeferred(asyncCtx, state);
418 auto ctx = weakCtx.Upgrade();
419 CHECK_NULL_VOID(ctx);
420 ctx->isImageAnalyzing_ = false;
421 };
422 isImageAnalyzing_ = true;
423 auto canvasRenderingContext2DModel = AceType::DynamicCast<CanvasRenderingContext2DModel>(renderingContext2DModel_);
424 CHECK_NULL_VOID(canvasRenderingContext2DModel);
425 canvasRenderingContext2DModel->StartImageAnalyzer(configNativeValue, onAnalyzed_);
426 ReturnPromise(info, promise);
427 }
428
JsStopImageAnalyzer(const JSCallbackInfo & info)429 void JSRenderingContext::JsStopImageAnalyzer(const JSCallbackInfo& info)
430 {
431 ContainerScope scope(instanceId_);
432 auto canvasRenderingContext2DModel = AceType::DynamicCast<CanvasRenderingContext2DModel>(renderingContext2DModel_);
433 CHECK_NULL_VOID(canvasRenderingContext2DModel);
434 canvasRenderingContext2DModel->StopImageAnalyzer();
435 }
436
FindCbList(napi_env env,napi_value cb,CanvasCallbackFuncPairList & callbackFuncPairList)437 CanvasCallbackFuncPairList::const_iterator JSRenderingContext::FindCbList(
438 napi_env env, napi_value cb, CanvasCallbackFuncPairList& callbackFuncPairList)
439 {
440 return std::find_if(callbackFuncPairList.begin(), callbackFuncPairList.end(), [env, cb](const auto& item) -> bool {
441 bool result = false;
442 napi_value refItem;
443 napi_get_reference_value(env, item.first, &refItem);
444 napi_strict_equals(env, refItem, cb, &result);
445 return result;
446 });
447 }
448
AddCallbackToList(napi_env env,napi_value cb,CanvasCallbackType type,const std::function<void ()> && onFunc)449 void JSRenderingContext::AddCallbackToList(
450 napi_env env, napi_value cb, CanvasCallbackType type, const std::function<void()>&& onFunc)
451 {
452 if (type == CanvasCallbackType::ON_ATTACH) {
453 auto iter = FindCbList(env, cb, attachCallback_);
454 if (iter == attachCallback_.end()) {
455 napi_ref ref = nullptr;
456 napi_create_reference(env, cb, 1, &ref);
457 attachCallback_.emplace_back(ref, onFunc);
458 }
459 } else if (type == CanvasCallbackType::ON_DETACH) {
460 auto iter = FindCbList(env, cb, detachCallback_);
461 if (iter == detachCallback_.end()) {
462 napi_ref ref = nullptr;
463 napi_create_reference(env, cb, 1, &ref);
464 detachCallback_.emplace_back(ref, onFunc);
465 }
466 }
467 }
468
DeleteCallbackFromList(uint32_t argc,napi_env env,napi_value cb,CanvasCallbackType type)469 void JSRenderingContext::DeleteCallbackFromList(uint32_t argc, napi_env env, napi_value cb, CanvasCallbackType type)
470 {
471 if (argc == 1) {
472 if (type == CanvasCallbackType::ON_ATTACH) {
473 for (const auto& item : attachCallback_) {
474 napi_delete_reference(env, item.first);
475 }
476 attachCallback_.clear();
477 } else if (type == CanvasCallbackType::ON_DETACH) {
478 for (const auto& item : detachCallback_) {
479 napi_delete_reference(env, item.first);
480 }
481 detachCallback_.clear();
482 }
483 } else if (argc == 2) { // The count of params is 2.
484 if (type == CanvasCallbackType::ON_ATTACH) {
485 auto iter = FindCbList(env, cb, attachCallback_);
486 if (iter != attachCallback_.end()) {
487 napi_delete_reference(env, iter->first);
488 attachCallback_.erase(iter);
489 }
490 } else if (type == CanvasCallbackType::ON_DETACH) {
491 auto iter = FindCbList(env, cb, detachCallback_);
492 if (iter != detachCallback_.end()) {
493 napi_delete_reference(env, iter->first);
494 detachCallback_.erase(iter);
495 }
496 }
497 }
498 }
499
GetCanvasCallbackType(const std::string & strType)500 CanvasCallbackType JSRenderingContext::GetCanvasCallbackType(const std::string& strType)
501 {
502 CanvasCallbackType type = CanvasCallbackType::UNKNOWN;
503 static constexpr char attachType[] = "onAttach";
504 static constexpr char detachType[] = "onDetach";
505 if (strType.compare(attachType) == 0) {
506 type = CanvasCallbackType::ON_ATTACH;
507 } else if (strType.compare(detachType) == 0) {
508 type = CanvasCallbackType::ON_DETACH;
509 }
510 return type;
511 }
512
JsOn(const JSCallbackInfo & info)513 void JSRenderingContext::JsOn(const JSCallbackInfo& info)
514 {
515 if (!info[0]->IsString() || !info[1]->IsFunction()) {
516 return;
517 }
518 const CanvasCallbackType type = GetCanvasCallbackType(info[0]->ToString());
519 if (type == CanvasCallbackType::UNKNOWN) {
520 JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s", "Input parameter error.");
521 return;
522 }
523 auto jsFunc = AceType::MakeRefPtr<JsFunction>(JSRef<JSObject>(), JSRef<JSFunc>::Cast(info[1]));
524 std::function<void()> onFunc = [execCtx = info.GetExecutionContext(), func = std::move(jsFunc),
525 id = instanceId_]() {
526 ContainerScope scope(id);
527 JAVASCRIPT_EXECUTION_SCOPE_WITH_CHECK(execCtx);
528 func->Execute();
529 };
530 ContainerScope scope(instanceId_);
531 #if !defined(PREVIEW)
532 auto engine = std::static_pointer_cast<ArkJSRuntime>(JsiDeclarativeEngineInstance::GetCurrentRuntime());
533 #else
534 auto engine = EngineHelper::GetCurrentEngineSafely();
535 #endif
536 CHECK_NULL_VOID(engine);
537 NativeEngine* nativeEngine = engine->GetNativeEngine();
538 CHECK_NULL_VOID(nativeEngine);
539 auto env = reinterpret_cast<napi_env>(nativeEngine);
540 ScopeRAII scopeNapi(env);
541 panda::Local<JsiValue> value = info[1].Get().GetLocalHandle();
542 JSValueWrapper valueWrapper = value;
543 napi_value cb = nativeEngine->ValueToNapiValue(valueWrapper);
544 napi_handle_scope napiScope = nullptr;
545 napi_open_handle_scope(env, &napiScope);
546 CHECK_NULL_VOID(napiScope);
547
548 AddCallbackToList(env, cb, type, std::move(onFunc));
549 napi_close_handle_scope(env, napiScope);
550 }
551
JsOff(const JSCallbackInfo & info)552 void JSRenderingContext::JsOff(const JSCallbackInfo& info)
553 {
554 if (!info[0]->IsString()) {
555 return;
556 }
557 const CanvasCallbackType type = GetCanvasCallbackType(info[0]->ToString());
558 if (type == CanvasCallbackType::UNKNOWN) {
559 JSException::Throw(ERROR_CODE_PARAM_INVALID, "%s", "Input parameter error.");
560 return;
561 }
562 ContainerScope scope(instanceId_);
563 #if !defined(PREVIEW)
564 auto engine = std::static_pointer_cast<ArkJSRuntime>(JsiDeclarativeEngineInstance::GetCurrentRuntime());
565 #else
566 auto engine = EngineHelper::GetCurrentEngineSafely();
567 #endif
568 CHECK_NULL_VOID(engine);
569 NativeEngine* nativeEngine = engine->GetNativeEngine();
570 CHECK_NULL_VOID(nativeEngine);
571 auto env = reinterpret_cast<napi_env>(nativeEngine);
572 ScopeRAII scopeNapi(env);
573 napi_value cb = nullptr;
574 if (info[1]->IsFunction()) {
575 panda::Local<JsiValue> value = info[1].Get().GetLocalHandle();
576 JSValueWrapper valueWrapper = value;
577 cb = nativeEngine->ValueToNapiValue(valueWrapper);
578 }
579
580 DeleteCallbackFromList(info.Length(), env, cb, type);
581 }
582 } // namespace OHOS::Ace::Framework
583